Part 2: Bug 1348733 - Update tests for site data manager now that we use quota storage manager, r?Gijs draft
authorFischer.json <fischer.json@gmail.com>
Wed, 10 May 2017 09:46:46 -0400
changeset 576352 4fd91c4f279316987fc0ad5bcd99999c23b3f775
parent 576351 2a8840671ffbdcef63a4174c4fafe4d30f99bcb4
child 628167 328a57adbf4eea9686f8707d4982bf29e49a4c4c
push id58329
push userbmo:fliu@mozilla.com
push dateThu, 11 May 2017 17:10:14 +0000
reviewersGijs
bugs1348733
milestone55.0a1
Part 2: Bug 1348733 - Update tests for site data manager now that we use quota storage manager, r?Gijs The patch does - update both the in-content and in-content-old tests - add the offline test page for appcache testing case - update tests for switching to Quota Manager from Permission Manager to get sites list - add one test case of grouping sites across scheme, port and origin attributes by host - add one test case of list site using quota usage or site using appcache MozReview-Commit-ID: 6brLVwzmSgf
browser/components/preferences/SiteDataManager.jsm
browser/components/preferences/in-content-old/tests/browser.ini
browser/components/preferences/in-content-old/tests/browser_advanced_siteData.js
browser/components/preferences/in-content-old/tests/offline/manifest.appcache
browser/components/preferences/in-content-old/tests/offline/offline.html
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_siteData.js
browser/components/preferences/in-content/tests/browser_siteData2.js
browser/components/preferences/in-content/tests/head.js
browser/components/preferences/in-content/tests/offline/manifest.appcache
browser/components/preferences/in-content/tests/offline/offline.html
browser/components/preferences/siteDataSettings.js
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -66,17 +66,17 @@ this.SiteDataManager = {
                 site.persisted = true;
               }
               site.principals.push(principal);
               site.quotaUsage += result.usage;
               this._sites.set(uri.host, site);
             }
           }
           this._updateAppCache();
-          Services.obs.notifyObservers(null, "sitedatamanager:sites-updated", null);
+          Services.obs.notifyObservers(null, "sitedatamanager:sites-updated");
         });
   },
 
   _getQuotaUsage() {
     this._getQuotaUsagePromise = new Promise(resolve => {
       let callback = {
         onUsageResult(request) {
           resolve(request.result);
--- a/browser/components/preferences/in-content-old/tests/browser.ini
+++ b/browser/components/preferences/in-content-old/tests/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
   privacypane_tests_perwindow.js
   site_data_test.html
+  offline/offline.html
+  offline/manifest.appcache
 
 [browser_applications_selection.js]
 [browser_advanced_siteData.js]
 [browser_advanced_update.js]
 skip-if = !updater
 [browser_basic_rebuild_fonts_test.js]
 [browser_bug410900.js]
 [browser_bug705422.js]
--- a/browser/components/preferences/in-content-old/tests/browser_advanced_siteData.js
+++ b/browser/components/preferences/in-content-old/tests/browser_advanced_siteData.js
@@ -4,19 +4,22 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 /* import-globals-from ../../../../../testing/modules/sinon-1.16.1.js */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
 
-const TEST_HOST = "example.com";
-const TEST_ORIGIN = "http://" + TEST_HOST;
-const TEST_BASE_URL = TEST_ORIGIN + "/browser/browser/components/preferences/in-content-old/tests/";
+const TEST_QUOTA_USAGE_HOST = "example.com";
+const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
+const TEST_QUOTA_USAGE_URL = TEST_QUOTA_USAGE_ORIGIN + "/browser/browser/components/preferences/in-content-old/tests/site_data_test.html";
+const TEST_OFFLINE_HOST = "example.org";
+const TEST_OFFLINE_ORIGIN = "https://" + TEST_OFFLINE_HOST;
+const TEST_OFFLINE_URL = TEST_OFFLINE_ORIGIN + "/browser/browser/components/preferences/in-content-old/tests/offline/offline.html";
 const REMOVE_DIALOG_URL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
 
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { DownloadUtils } = Cu.import("resource://gre/modules/DownloadUtils.jsm", {});
 const { SiteDataManager } = Cu.import("resource:///modules/SiteDataManager.jsm", {});
 const { OfflineAppCacheHelper } = Cu.import("resource:///modules/offlineAppCache.jsm", {});
 
 const mockOfflineAppCacheHelper = {
@@ -31,103 +34,60 @@ const mockOfflineAppCacheHelper = {
   },
 
   unregister() {
     OfflineAppCacheHelper.clear = this.originalClear;
   }
 };
 
 const mockSiteDataManager = {
-  sites: new Map([
-    [
-      "https://account.xyz.com/",
-      {
-        usage: 1024 * 200,
-        host: "account.xyz.com",
-        status: Ci.nsIPermissionManager.ALLOW_ACTION
-      }
-    ],
-    [
-      "https://shopping.xyz.com/",
-      {
-        usage: 1024 * 100,
-        host: "shopping.xyz.com",
-        status: Ci.nsIPermissionManager.DENY_ACTION
-      }
-    ],
-    [
-      "https://video.bar.com/",
-      {
-        usage: 1024 * 20,
-        host: "video.bar.com",
-        status: Ci.nsIPermissionManager.ALLOW_ACTION
-      }
-    ],
-    [
-      "https://music.bar.com/",
-      {
-        usage: 1024 * 10,
-        host: "music.bar.com",
-        status: Ci.nsIPermissionManager.DENY_ACTION
-      }
-    ],
-    [
-      "https://books.foo.com/",
-      {
-        usage: 1024 * 2,
-        host: "books.foo.com",
-        status: Ci.nsIPermissionManager.ALLOW_ACTION
-      }
-    ],
-    [
-      "https://news.foo.com/",
-      {
-        usage: 1024,
-        host: "news.foo.com",
-        status: Ci.nsIPermissionManager.DENY_ACTION
-      }
-    ]
-  ]),
+
+  _originalGetQuotaUsage: null,
+  _originalRemoveQuotaUsage: null,
 
-  _originalGetSites: null,
-
-  getSites() {
-    let list = [];
-    this.sites.forEach((data, origin) => {
-      list.push({
-        usage: data.usage,
-        status: data.status,
-        uri: NetUtil.newURI(origin)
+  _getQuotaUsage() {
+    let results = [];
+    this.fakeSites.forEach(site => {
+      results.push({
+        origin: site.principal.origin,
+        usage: site.usage,
+        persisted: site.persisted
       });
     });
-    return Promise.resolve(list);
+    SiteDataManager._getQuotaUsagePromise = Promise.resolve(results);
+    return SiteDataManager._getQuotaUsagePromise;
+  },
+
+  _removeQuotaUsage(site) {
+    var target = site.principals[0].URI.host;
+    this.fakeSites = this.fakeSites.filter(fakeSite => {
+      return fakeSite.principal.URI.host != target;
+    });
   },
 
   register() {
-    this._originalGetSites = SiteDataManager.getSites;
-    SiteDataManager.getSites = this.getSites.bind(this);
+    this._originalGetQuotaUsage = SiteDataManager._getQuotaUsage;
+    SiteDataManager._getQuotaUsage = this._getQuotaUsage.bind(this);
+    this._originalRemoveQuotaUsage = SiteDataManager._removeQuotaUsage;
+    SiteDataManager._removeQuotaUsage = this._removeQuotaUsage.bind(this);
+    this.fakeSites = null;
   },
 
   unregister() {
-    SiteDataManager.getSites = this._originalGetSites;
+    SiteDataManager._getQuotaUsage = this._originalGetQuotaUsage;
+    SiteDataManager._removeQuotaUsage = this._originalRemoveQuotaUsage;
   }
 };
 
 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 removePersistentStoragePerm(origin) {
-  let uri = NetUtil.newURI(origin);
-  let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
-  Services.perms.removeFromPrincipal(principal, "persistent-storage");
-}
-
 function getPersistentStoragePermStatus(origin) {
   let uri = NetUtil.newURI(origin);
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
   return Services.perms.testExactPermissionFromPrincipal(principal, "persistent-storage");
 }
 
 function getQuotaUsage(origin) {
   return new Promise(resolve => {
@@ -198,39 +158,139 @@ function promiseSitesUpdated() {
 }
 
 function promiseCookiesCleared() {
   return TestUtils.topicObserved("cookie-changed", (subj, data) => {
     return data === "cleared";
   });
 }
 
-function assertSitesListed(doc, origins) {
+function assertSitesListed(doc, hosts) {
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let removeBtn = frameDoc.getElementById("removeSelected");
   let removeAllBtn = frameDoc.getElementById("removeAll");
   let sitesList = frameDoc.getElementById("sitesList");
   let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
-  is(totalSitesNumber, origins.length, "Should list the right sites number");
-  origins.forEach(origin => {
-    let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
-    let host = site.getAttribute("host");
-    ok(origin.includes(host), `Should list the site of ${origin}`);
+  is(totalSitesNumber, hosts.length, "Should list the right sites number");
+  hosts.forEach(host => {
+    let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+    ok(site, `Should list the site of ${host}`);
   });
   is(removeBtn.disabled, false, "Should enable the removeSelected button");
   is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
 }
 
 registerCleanupFunction(function() {
   delete window.sinon;
   delete window.setImmediate;
   delete window.clearImmediate;
   mockOfflineAppCacheHelper.unregister();
 });
 
+// Test grouping and listing sites across scheme, port and origin attributes by host
+add_task(function *() {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  const quotaUsage = 1024;
+  mockSiteDataManager.register();
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com^userContextId=1"),
+      persisted: true
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com:123"),
+      persisted: false
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://account.xyz.com"),
+      persisted: false
+    },
+  ];
+
+  let updatedPromise = promiseSitesUpdated();
+  yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+  yield updatedPromise;
+  yield openSettingsDialog();
+  let dialogFrame = gBrowser.selectedBrowser.contentDocument.getElementById("dialogFrame");
+  let frameDoc = dialogFrame.contentDocument;
+
+  let siteItems = frameDoc.getElementsByTagName("richlistitem");
+  is(siteItems.length, 1, "Should group sites across scheme, port and origin attributes");
+
+  let expected = "account.xyz.com";
+  let hostCol = siteItems[0].getAttribute("host");
+  is(hostCol, expected, "Should group and list sites by host");
+
+  let prefStrBundle = frameDoc.getElementById("bundlePreferences");
+  expected = prefStrBundle.getFormattedString("siteUsage",
+    DownloadUtils.convertByteUnits(quotaUsage * mockSiteDataManager.fakeSites.length));
+  let usageCol = siteItems[0].getAttribute("usage");
+  is(usageCol, expected, "Should sum up usages across scheme, port and origin attributes");
+
+  expected = prefStrBundle.getString("persistent");
+  let statusCol = siteItems[0].getAttribute("status");
+  is(statusCol, expected, "Should mark persisted status across scheme, port and origin attributes");
+
+  mockSiteDataManager.unregister();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+// Test listing site using quota usage or site using appcache
+add_task(function *() {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+
+  // Open a test site which would save into appcache
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  // Open a test site which would save into quota manager
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
+  yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  let updatedPromise = promiseSitesUpdated();
+  yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+  yield updatedPromise;
+  yield openSettingsDialog();
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let dialogFrame = doc.getElementById("dialogFrame");
+  let frameDoc = dialogFrame.contentDocument;
+
+  let siteItems = frameDoc.getElementsByTagName("richlistitem");
+  is(siteItems.length, 2, "Should list sites using quota usage or appcache");
+
+  let appcacheSite = frameDoc.querySelector(`richlistitem[host="${TEST_OFFLINE_HOST}"]`);
+  ok(appcacheSite, "Should list site using appcache");
+
+  let qoutaUsageSite = frameDoc.querySelector(`richlistitem[host="${TEST_QUOTA_USAGE_HOST}"]`);
+  ok(qoutaUsageSite, "Should list site using quota usage");
+
+  // Always remember to clean up
+  OfflineAppCacheHelper.clear();
+  yield new Promise(resolve => {
+    let principal = Services.scriptSecurityManager
+                            .createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
+    let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
+    request.callback = resolve;
+  });
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
 // Test buttons are disabled and loading message shown while updating sites
 add_task(function *() {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
   let updatedPromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatedPromise;
 
   let actual = null;
@@ -266,48 +326,49 @@ add_task(function *() {
                           expected = prefStrBundle.getFormattedString(
                            "totalSiteDataSize", DownloadUtils.convertByteUnits(usage));
                           is(actual, expected, "Should show the right total site data size");
                        });
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
+// Test the function of the "Clear All Data" button
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  addPersistentStoragePerm(TEST_ORIGIN);
+  addPersistentStoragePerm(TEST_QUOTA_USAGE_ORIGIN);
 
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_BASE_URL + "site_data_test.html");
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
   yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
 
   // Test the initial states
   let cacheUsage = yield cacheUsageGetter.get();
-  let quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  let quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   let totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
 
   // Test cancelling "Clear All Data"
   // Click "Clear All Data" button and then cancel
   let doc = gBrowser.selectedBrowser.contentDocument;
   let cancelPromise = promiseAlertDialogOpen("cancel");
   let clearBtn = doc.getElementById("clearSiteDataButton");
   clearBtn.doCommand();
   yield cancelPromise;
 
   // Test the items are not removed
-  let status = getPersistentStoragePermStatus(TEST_ORIGIN);
+  let status = getPersistentStoragePermStatus(TEST_QUOTA_USAGE_ORIGIN);
   is(status, Ci.nsIPermissionManager.ALLOW_ACTION, "Should not remove permission");
 
   cacheUsage = yield cacheUsageGetter.get();
-  quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
   // Test cancelling "Clear All Data" ends
 
   // Test accepting "Clear All Data"
   // Click "Clear All Data" button and then accept
@@ -321,375 +382,473 @@ add_task(function* () {
   yield updatePromise;
   mockOfflineAppCacheHelper.unregister();
 
   // Test all the items are removed
   yield cookiesClearedPromise;
 
   ok(mockOfflineAppCacheHelper.clear.calledOnce, "Should clear app cache");
 
-  status = getPersistentStoragePermStatus(TEST_ORIGIN);
+  status = getPersistentStoragePermStatus(TEST_QUOTA_USAGE_ORIGIN);
   is(status, Ci.nsIPermissionManager.UNKNOWN_ACTION, "Should remove permission");
 
   cacheUsage = yield cacheUsageGetter.get();
-  quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
-  is(cacheUsage, 0, "The cahce usage should be removed");
+  is(cacheUsage, 0, "The cache 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
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
+// Test sorting
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  mockSiteDataManager.register();
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024 * 2,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://books.foo.com"),
+      persisted: false
+    },
+    {
+      usage: 1024 * 3,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+  ];
 
-  mockSiteDataManager.register();
   let updatePromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatePromise;
   yield openSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   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");
+  assertSortByUsage("descending");
+
+  // Test sorting on the usage column
+  usageCol.click();
+  assertSortByUsage("ascending");
+  usageCol.click();
+  assertSortByUsage("descending");
 
   // Test sorting on the host column
   hostCol.click();
-  assertSortByHost("descending");
+  assertSortByHost("ascending");
   hostCol.click();
-  assertSortByHost("ascending");
+  assertSortByHost("descending");
 
   // 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);
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let result = aHost.localeCompare(bHost);
       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;
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
+      let result = 0;
+      if (a.persisted && !b.persisted) {
+        result = 1;
+      } else if (!a.persisted && b.persisted) {
+        result = -1;
+      }
       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 aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
       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");
       }
     }
   }
+
+  function findSiteByHost(host) {
+    return mockSiteDataManager.fakeSites.find(site => site.principal.URI.host == host);
+  }
 });
 
+// Test search on the host column
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  mockSiteDataManager.register();
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
+  ];
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
-  mockSiteDataManager.register();
   let updatePromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatePromise;
   yield openSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let searchBox = frameDoc.getElementById("searchBox");
-  let mockOrigins = Array.from(mockSiteDataManager.sites.keys());
 
   searchBox.value = "xyz";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins.filter(o => o.includes("xyz")));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("xyz")));
 
   searchBox.value = "bar";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins.filter(o => o.includes("bar")));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("bar")));
 
   searchBox.value = "";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 // Test selecting and removing all sites one by one
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://mails.bar.com/",
-    "https://videos.xyz.com/",
-    "https://books.foo.com/",
-    "https://account.bar.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register();
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatePromise;
   yield openSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = null;
   let saveBtn = null;
   let cancelBtn = null;
   let settingsDialogClosePromise = null;
 
   // Test the initial state
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Cancel" button
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   cancelBtn = frameDoc.getElementById("cancel");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   cancelBtn.doCommand();
   yield settingsDialogClosePromise;
   yield openSettingsDialog();
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button but cancelling save
   let cancelPromise = promiseAlertDialogOpen("cancel");
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   saveBtn.doCommand();
   yield cancelPromise;
   yield settingsDialogClosePromise;
   yield openSettingsDialog();
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button and accepting save
   let acceptPromise = promiseAlertDialogOpen("accept");
   settingsDialogClosePromise = promiseSettingsDialogClose();
   updatePromise = promiseSitesUpdated();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   saveBtn.doCommand();
   yield acceptPromise;
   yield settingsDialogClosePromise;
   yield updatePromise;
   yield openSettingsDialog();
   assertAllSitesNotListed();
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   function removeAllSitesOneByOne() {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let sitesList = frameDoc.getElementById("sitesList");
     let sites = sitesList.getElementsByTagName("richlistitem");
     for (let i = sites.length - 1; i >= 0; --i) {
       sites[i].click();
       removeBtn.doCommand();
     }
   }
 
-  function assertAllSitesListed() {
-    frameDoc = doc.getElementById("dialogFrame").contentDocument;
-    let removeBtn = frameDoc.getElementById("removeSelected");
-    let removeAllBtn = frameDoc.getElementById("removeAll");
-    let sitesList = frameDoc.getElementById("sitesList");
-    let sites = sitesList.getElementsByTagName("richlistitem");
-    is(sites.length, fakeOrigins.length, "Should list all sites");
-    is(removeBtn.disabled, false, "Should enable the removeSelected button");
-    is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
-  }
-
   function assertAllSitesNotListed() {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let removeAllBtn = frameDoc.getElementById("removeAll");
     let sitesList = frameDoc.getElementById("sitesList");
     let sites = sitesList.getElementsByTagName("richlistitem");
     is(sites.length, 0, "Should not list all sites");
     is(removeBtn.disabled, true, "Should disable the removeSelected button");
     is(removeAllBtn.disabled, true, "Should disable the removeAllBtn button");
   }
 });
 
 // Test selecting and removing partial sites
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://mails.bar.com/",
-    "https://videos.xyz.com/",
-    "https://books.foo.com/",
-    "https://account.bar.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register();
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatePromise;
   yield openSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = null;
   let saveBtn = null;
   let cancelBtn = null;
   let removeDialogOpenPromise = null;
   let settingsDialogClosePromise = null;
 
   // Test the initial state
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Cancel" button
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   cancelBtn = frameDoc.getElementById("cancel");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   cancelBtn.doCommand();
   yield settingsDialogClosePromise;
   yield openSettingsDialog();
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button but canceling save
   removeDialogOpenPromise = promiseWindowDialogOpen("cancel", REMOVE_DIALOG_URL);
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
   yield removeDialogOpenPromise;
   yield settingsDialogClosePromise;
   yield openSettingsDialog();
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button and accepting save
   removeDialogOpenPromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
   yield removeDialogOpenPromise;
   yield settingsDialogClosePromise;
   yield openSettingsDialog();
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  assertSitesListed(doc, fakeHosts.slice(2));
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  function removeSelectedSite(origins) {
+  function removeSelectedSite(hosts) {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let sitesList = frameDoc.getElementById("sitesList");
-    origins.forEach(origin => {
-      let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
+    hosts.forEach(host => {
+      let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
       if (site) {
         site.click();
         removeBtn.doCommand();
       } else {
-        ok(false, `Should not select and remove inexisted site of ${origin}`);
+        ok(false, `Should not select and remove inexistent site of ${host}`);
       }
     });
   }
 });
 
+// Test searching and then removing only visible sites
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://books.foo.com/",
-    "https://mails.bar.com/",
-    "https://account.bar.com/",
-    "https://videos.xyz.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
   yield updatePromise;
   yield openSettingsDialog();
 
   // Search "foo" to only list foo.com sites
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let searchBox = frameDoc.getElementById("searchBox");
-  searchBox.value = "foo";
+  searchBox.value = "xyz";
   searchBox.doCommand();
-  assertSitesListed(doc, fakeOrigins.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("xyz")));
 
   // Test only removing all visible sites listed
   updatePromise = promiseSitesUpdated();
   let acceptRemovePromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
   let settingsDialogClosePromise = promiseSettingsDialogClose();
   let removeAllBtn = frameDoc.getElementById("removeAll");
   let saveBtn = frameDoc.getElementById("save");
   removeAllBtn.doCommand();
   saveBtn.doCommand();
   yield acceptRemovePromise;
   yield settingsDialogClosePromise;
   yield updatePromise;
   yield openSettingsDialog();
-  assertSitesListed(doc, fakeOrigins.slice(2));
+  assertSitesListed(doc, fakeHosts.filter(host => !host.includes("xyz")));
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content-old/tests/offline/manifest.appcache
@@ -0,0 +1,3 @@
+CACHE MANIFEST
+# V1
+offline.html
new file mode 100755
--- /dev/null
+++ b/browser/components/preferences/in-content-old/tests/offline/offline.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html manifest="manifest.appcache">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Cache-Control" content="public" />
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
+
+  </head>
+
+  <body>
+    <h1>Set up offline appcache Test</h1>
+  </body>
+</html>
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
   privacypane_tests_perwindow.js
   site_data_test.html
+  offline/offline.html
+  offline/manifest.appcache
 
 [browser_applications_selection.js]
 [browser_advanced_update.js]
 skip-if = !updater
 [browser_basic_rebuild_fonts_test.js]
 [browser_bug410900.js]
 [browser_bug705422.js]
 [browser_bug731866.js]
--- a/browser/components/preferences/in-content/tests/browser_siteData.js
+++ b/browser/components/preferences/in-content/tests/browser_siteData.js
@@ -4,19 +4,22 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 /* import-globals-from ../../../../../testing/modules/sinon-1.16.1.js */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
 
-const TEST_HOST = "example.com";
-const TEST_ORIGIN = "http://" + TEST_HOST;
-const TEST_BASE_URL = TEST_ORIGIN + "/browser/browser/components/preferences/in-content/tests/";
+const TEST_QUOTA_USAGE_HOST = "example.com";
+const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
+const TEST_QUOTA_USAGE_URL = TEST_QUOTA_USAGE_ORIGIN + "/browser/browser/components/preferences/in-content/tests/site_data_test.html";
+const TEST_OFFLINE_HOST = "example.org";
+const TEST_OFFLINE_ORIGIN = "https://" + TEST_OFFLINE_HOST;
+const TEST_OFFLINE_URL = TEST_OFFLINE_ORIGIN + "/browser/browser/components/preferences/in-content/tests/offline/offline.html";
 const REMOVE_DIALOG_URL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
 
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { DownloadUtils } = Cu.import("resource://gre/modules/DownloadUtils.jsm", {});
 const { SiteDataManager } = Cu.import("resource:///modules/SiteDataManager.jsm", {});
 const { OfflineAppCacheHelper } = Cu.import("resource:///modules/offlineAppCache.jsm", {});
 
 const mockOfflineAppCacheHelper = {
@@ -30,92 +33,16 @@ const mockOfflineAppCacheHelper = {
     OfflineAppCacheHelper.clear = this.clear;
   },
 
   unregister() {
     OfflineAppCacheHelper.clear = this.originalClear;
   }
 };
 
-const mockSiteDataManager = {
-  sites: new Map([
-    [
-      "https://account.xyz.com/",
-      {
-        usage: 1024 * 200,
-        host: "account.xyz.com",
-        status: Ci.nsIPermissionManager.ALLOW_ACTION
-      }
-    ],
-    [
-      "https://shopping.xyz.com/",
-      {
-        usage: 1024 * 100,
-        host: "shopping.xyz.com",
-        status: Ci.nsIPermissionManager.DENY_ACTION
-      }
-    ],
-    [
-      "https://video.bar.com/",
-      {
-        usage: 1024 * 20,
-        host: "video.bar.com",
-        status: Ci.nsIPermissionManager.ALLOW_ACTION
-      }
-    ],
-    [
-      "https://music.bar.com/",
-      {
-        usage: 1024 * 10,
-        host: "music.bar.com",
-        status: Ci.nsIPermissionManager.DENY_ACTION
-      }
-    ],
-    [
-      "https://books.foo.com/",
-      {
-        usage: 1024 * 2,
-        host: "books.foo.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 getPersistentStoragePermStatus(origin) {
   let uri = NetUtil.newURI(origin);
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
   return Services.perms.testExactPermissionFromPrincipal(principal, "persistent-storage");
 }
 
 function getQuotaUsage(origin) {
   return new Promise(resolve => {
@@ -162,16 +89,57 @@ function promiseCookiesCleared() {
 
 registerCleanupFunction(function() {
   delete window.sinon;
   delete window.setImmediate;
   delete window.clearImmediate;
   mockOfflineAppCacheHelper.unregister();
 });
 
+// Test listing site using quota usage or site using appcache
+add_task(function *() {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+
+  // Open a test site which would save into appcache
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  // Open a test site which would save into quota manager
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
+  yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  let updatedPromise = promiseSiteDataManagerSitesUpdated();
+  yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+  yield updatedPromise;
+  yield openSiteDataSettingsDialog();
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let dialogFrame = doc.getElementById("dialogFrame");
+  let frameDoc = dialogFrame.contentDocument;
+
+  let siteItems = frameDoc.getElementsByTagName("richlistitem");
+  is(siteItems.length, 2, "Should list sites using quota usage or appcache");
+
+  let appcacheSite = frameDoc.querySelector(`richlistitem[host="${TEST_OFFLINE_HOST}"]`);
+  ok(appcacheSite, "Should list site using appcache");
+
+  let qoutaUsageSite = frameDoc.querySelector(`richlistitem[host="${TEST_QUOTA_USAGE_HOST}"]`);
+  ok(qoutaUsageSite, "Should list site using quota usage");
+
+  // Always remember to clean up
+  OfflineAppCacheHelper.clear();
+  yield new Promise(resolve => {
+    let principal = Services.scriptSecurityManager
+                            .createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
+    let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
+    request.callback = resolve;
+  });
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
 // Test buttons are disabled and loading message shown while updating sites
 add_task(function *() {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
   let updatedPromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatedPromise;
 
   let actual = null;
@@ -207,48 +175,49 @@ add_task(function *() {
                          expected = prefStrBundle.getFormattedString(
                            "totalSiteDataSize", DownloadUtils.convertByteUnits(usage));
                           is(actual, expected, "Should show the right total site data size");
                        });
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
+// Test the function of the "Clear All Data" button
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  addPersistentStoragePerm(TEST_ORIGIN);
+  addPersistentStoragePerm(TEST_QUOTA_USAGE_ORIGIN);
 
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_BASE_URL + "site_data_test.html");
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
   yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
 
   // Test the initial states
   let cacheUsage = yield cacheUsageGetter.get();
-  let quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  let quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   let totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
 
   // Test cancelling "Clear All Data"
   // Click "Clear All Data" button and then cancel
   let doc = gBrowser.selectedBrowser.contentDocument;
   let cancelPromise = promiseAlertDialogOpen("cancel");
   let clearBtn = doc.getElementById("clearSiteDataButton");
   clearBtn.doCommand();
   yield cancelPromise;
 
   // Test the items are not removed
-  let status = getPersistentStoragePermStatus(TEST_ORIGIN);
+  let status = getPersistentStoragePermStatus(TEST_QUOTA_USAGE_ORIGIN);
   is(status, Ci.nsIPermissionManager.ALLOW_ACTION, "Should not remove permission");
 
   cacheUsage = yield cacheUsageGetter.get();
-  quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
   Assert.greater(quotaUsage, 0, "The quota usage should not be 0");
   Assert.greater(totalUsage, 0, "The total usage should not be 0");
   // Test cancelling "Clear All Data" ends
 
   // Test accepting "Clear All Data"
   // Click "Clear All Data" button and then accept
@@ -262,142 +231,196 @@ add_task(function* () {
   yield updatePromise;
   mockOfflineAppCacheHelper.unregister();
 
   // Test all the items are removed
   yield cookiesClearedPromise;
 
   ok(mockOfflineAppCacheHelper.clear.calledOnce, "Should clear app cache");
 
-  status = getPersistentStoragePermStatus(TEST_ORIGIN);
+  status = getPersistentStoragePermStatus(TEST_QUOTA_USAGE_ORIGIN);
   is(status, Ci.nsIPermissionManager.UNKNOWN_ACTION, "Should remove permission");
 
   cacheUsage = yield cacheUsageGetter.get();
-  quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
+  quotaUsage = yield getQuotaUsage(TEST_QUOTA_USAGE_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
-  is(cacheUsage, 0, "The cahce usage should be removed");
+  is(cacheUsage, 0, "The cache 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
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
+// Test sorting
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024 * 2,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://books.foo.com"),
+      persisted: false
+    },
+    {
+      usage: 1024 * 3,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+  ];
 
-  mockSiteDataManager.register();
   let updatePromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatePromise;
   yield openSiteDataSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   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");
+  assertSortByUsage("descending");
+
+  // Test sorting on the usage column
+  usageCol.click();
+  assertSortByUsage("ascending");
+  usageCol.click();
+  assertSortByUsage("descending");
 
   // Test sorting on the host column
   hostCol.click();
-  assertSortByHost("descending");
+  assertSortByHost("ascending");
   hostCol.click();
-  assertSortByHost("ascending");
+  assertSortByHost("descending");
 
   // 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);
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let result = aHost.localeCompare(bHost);
       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;
+      let aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
+      let result = 0;
+      if (a.persisted && !b.persisted) {
+        result = 1;
+      } else if (!a.persisted && b.persisted) {
+        result = -1;
+      }
       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 aHost = siteItems[i].getAttribute("host");
+      let bHost = siteItems[i + 1].getAttribute("host");
+      let a = findSiteByHost(aHost);
+      let b = findSiteByHost(bHost);
       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");
       }
     }
   }
+
+  function findSiteByHost(host) {
+    return mockSiteDataManager.fakeSites.find(site => site.principal.URI.host == host);
+  }
 });
 
+// Test search on the host column
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
+  ];
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
-  mockSiteDataManager.register();
   let updatePromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatePromise;
   yield openSiteDataSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let searchBox = frameDoc.getElementById("searchBox");
-  let mockOrigins = Array.from(mockSiteDataManager.sites.keys());
 
   searchBox.value = "xyz";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins.filter(o => o.includes("xyz")));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("xyz")));
 
   searchBox.value = "bar";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins.filter(o => o.includes("bar")));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("bar")));
 
   searchBox.value = "";
   searchBox.doCommand();
-  assertSitesListed(doc, mockOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/components/preferences/in-content/tests/browser_siteData2.js
+++ b/browser/components/preferences/in-content/tests/browser_siteData2.js
@@ -1,256 +1,356 @@
 "use strict";
+const { SiteDataManager } = Cu.import("resource:///modules/SiteDataManager.jsm", {});
 
 const REMOVE_DIALOG_URL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
+const { DownloadUtils } = Cu.import("resource://gre/modules/DownloadUtils.jsm", {});
+
+function promiseSettingsDialogClose() {
+  return new Promise(resolve => {
+    let doc = gBrowser.selectedBrowser.contentDocument;
+    let dialogOverlay = doc.getElementById("dialogOverlay");
+    let win = content.gSubDialog._frame.contentWindow;
+    win.addEventListener("unload", function unload() {
+      if (win.document.documentURI === "chrome://browser/content/preferences/siteDataSettings.xul") {
+        isnot(dialogOverlay.style.visibility, "visible", "The Settings dialog should be hidden");
+        resolve();
+      }
+    }, { once: true });
+  });
+}
 
 // Test selecting and removing all sites one by one
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://mails.bar.com/",
-    "https://videos.xyz.com/",
-    "https://books.foo.com/",
-    "https://account.bar.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatePromise;
   yield openSiteDataSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = null;
   let saveBtn = null;
   let cancelBtn = null;
   let settingsDialogClosePromise = null;
 
   // Test the initial state
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Cancel" button
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   cancelBtn = frameDoc.getElementById("cancel");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   cancelBtn.doCommand();
   yield settingsDialogClosePromise;
   yield openSiteDataSettingsDialog();
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button but cancelling save
   let cancelPromise = promiseAlertDialogOpen("cancel");
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   saveBtn.doCommand();
   yield cancelPromise;
   yield settingsDialogClosePromise;
   yield openSiteDataSettingsDialog();
-  assertAllSitesListed();
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button and accepting save
   let acceptPromise = promiseAlertDialogOpen("accept");
   settingsDialogClosePromise = promiseSettingsDialogClose();
   updatePromise = promiseSiteDataManagerSitesUpdated();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
   removeAllSitesOneByOne();
   assertAllSitesNotListed();
   saveBtn.doCommand();
   yield acceptPromise;
   yield settingsDialogClosePromise;
   yield updatePromise;
   yield openSiteDataSettingsDialog();
   assertAllSitesNotListed();
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   function removeAllSitesOneByOne() {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let sitesList = frameDoc.getElementById("sitesList");
     let sites = sitesList.getElementsByTagName("richlistitem");
     for (let i = sites.length - 1; i >= 0; --i) {
       sites[i].click();
       removeBtn.doCommand();
     }
   }
 
-  function assertAllSitesListed() {
-    frameDoc = doc.getElementById("dialogFrame").contentDocument;
-    let removeBtn = frameDoc.getElementById("removeSelected");
-    let removeAllBtn = frameDoc.getElementById("removeAll");
-    let sitesList = frameDoc.getElementById("sitesList");
-    let sites = sitesList.getElementsByTagName("richlistitem");
-    is(sites.length, fakeOrigins.length, "Should list all sites");
-    is(removeBtn.disabled, false, "Should enable the removeSelected button");
-    is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
-  }
-
   function assertAllSitesNotListed() {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let removeAllBtn = frameDoc.getElementById("removeAll");
     let sitesList = frameDoc.getElementById("sitesList");
     let sites = sitesList.getElementsByTagName("richlistitem");
     is(sites.length, 0, "Should not list all sites");
     is(removeBtn.disabled, true, "Should disable the removeSelected button");
     is(removeAllBtn.disabled, true, "Should disable the removeAllBtn button");
   }
 });
 
 // Test selecting and removing partial sites
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://mails.bar.com/",
-    "https://videos.xyz.com/",
-    "https://books.foo.com/",
-    "https://account.bar.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatePromise;
   yield openSiteDataSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = null;
   let saveBtn = null;
   let cancelBtn = null;
   let removeDialogOpenPromise = null;
   let settingsDialogClosePromise = null;
 
   // Test the initial state
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Cancel" button
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   cancelBtn = frameDoc.getElementById("cancel");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   cancelBtn.doCommand();
   yield settingsDialogClosePromise;
   yield openSiteDataSettingsDialog();
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button but canceling save
   removeDialogOpenPromise = promiseWindowDialogOpen("cancel", REMOVE_DIALOG_URL);
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
   yield removeDialogOpenPromise;
   yield settingsDialogClosePromise;
   yield openSiteDataSettingsDialog();
-  assertSitesListed(doc, fakeOrigins);
+  assertSitesListed(doc, fakeHosts);
 
   // Test the "Save Changes" button and accepting save
   removeDialogOpenPromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
   settingsDialogClosePromise = promiseSettingsDialogClose();
   frameDoc = doc.getElementById("dialogFrame").contentDocument;
   saveBtn = frameDoc.getElementById("save");
-  removeSelectedSite(fakeOrigins.slice(0, 4));
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  removeSelectedSite(fakeHosts.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
   yield removeDialogOpenPromise;
   yield settingsDialogClosePromise;
   yield openSiteDataSettingsDialog();
-  assertSitesListed(doc, fakeOrigins.slice(4));
+  assertSitesListed(doc, fakeHosts.slice(2));
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  function removeSelectedSite(origins) {
+  function removeSelectedSite(hosts) {
     frameDoc = doc.getElementById("dialogFrame").contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
     let sitesList = frameDoc.getElementById("sitesList");
-    origins.forEach(origin => {
-      let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
+    hosts.forEach(host => {
+      let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
       if (site) {
         site.click();
         removeBtn.doCommand();
       } else {
-        ok(false, `Should not select and remove inexisted site of ${origin}`);
+        ok(false, `Should not select and remove inexistent site of ${host}`);
       }
     });
   }
 });
 
+// Test searching and then removing only visible sites
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
-  let fakeOrigins = [
-    "https://news.foo.com/",
-    "https://books.foo.com/",
-    "https://mails.bar.com/",
-    "https://account.bar.com/",
-    "https://videos.xyz.com/",
-    "https://shopping.xyz.com/"
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://shopping.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://cinema.bar.com"),
+      persisted: true
+    },
+    {
+      usage: 1024,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://email.bar.com"),
+      persisted: false
+    },
   ];
-  fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
+  let fakeHosts = mockSiteDataManager.fakeSites.map(site => site.principal.URI.host);
 
   let updatePromise = promiseSiteDataManagerSitesUpdated();
   yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   yield updatePromise;
   yield openSiteDataSettingsDialog();
 
   // Search "foo" to only list foo.com sites
   let doc = gBrowser.selectedBrowser.contentDocument;
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let searchBox = frameDoc.getElementById("searchBox");
-  searchBox.value = "foo";
+  searchBox.value = "xyz";
   searchBox.doCommand();
-  assertSitesListed(doc, fakeOrigins.slice(0, 2));
+  assertSitesListed(doc, fakeHosts.filter(host => host.includes("xyz")));
 
   // Test only removing all visible sites listed
   updatePromise = promiseSiteDataManagerSitesUpdated();
   let acceptRemovePromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
   let settingsDialogClosePromise = promiseSettingsDialogClose();
   let removeAllBtn = frameDoc.getElementById("removeAll");
   let saveBtn = frameDoc.getElementById("save");
   removeAllBtn.doCommand();
   saveBtn.doCommand();
   yield acceptRemovePromise;
   yield settingsDialogClosePromise;
   yield updatePromise;
   yield openSiteDataSettingsDialog();
-  assertSitesListed(doc, fakeOrigins.slice(2));
+  assertSitesListed(doc, fakeHosts.filter(host => !host.includes("xyz")));
 
-  // Always clean up the fake origins
-  fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
+  mockSiteDataManager.unregister();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
-function promiseSettingsDialogClose() {
-  return new Promise(resolve => {
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let dialogOverlay = doc.getElementById("dialogOverlay");
-    let win = content.gSubDialog._frame.contentWindow;
-    win.addEventListener("unload", function unload() {
-      if (win.document.documentURI === "chrome://browser/content/preferences/siteDataSettings.xul") {
-        isnot(dialogOverlay.style.visibility, "visible", "The Settings dialog should be hidden");
-        resolve();
-      }
-    }, { once: true });
-  });
-}
+// Test grouping and listing sites across scheme, port and origin attributes by host
+add_task(function *() {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  const quotaUsage = 1024;
+  mockSiteDataManager.register(SiteDataManager);
+  mockSiteDataManager.fakeSites = [
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com^userContextId=1"),
+      persisted: true
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com"),
+      persisted: false
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("https://account.xyz.com:123"),
+      persisted: false
+    },
+    {
+      usage: quotaUsage,
+      principal: Services.scriptSecurityManager
+                         .createCodebasePrincipalFromOrigin("http://account.xyz.com"),
+      persisted: false
+    },
+  ];
 
-function removePersistentStoragePerm(origin) {
-  let uri = NetUtil.newURI(origin);
-  let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
-  Services.perms.removeFromPrincipal(principal, "persistent-storage");
-}
+  let updatedPromise = promiseSiteDataManagerSitesUpdated();
+  yield openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+  yield updatedPromise;
+  yield openSiteDataSettingsDialog();
+  let dialogFrame = gBrowser.selectedBrowser.contentDocument.getElementById("dialogFrame");
+  let frameDoc = dialogFrame.contentDocument;
+
+  let siteItems = frameDoc.getElementsByTagName("richlistitem");
+  is(siteItems.length, 1, "Should group sites across scheme, port and origin attributes");
+
+  let expected = "account.xyz.com";
+  let host = siteItems[0].getAttribute("host");
+  is(host, expected, "Should group and list sites by host");
+
+  let prefStrBundle = frameDoc.getElementById("bundlePreferences");
+  expected = prefStrBundle.getFormattedString("siteUsage",
+    DownloadUtils.convertByteUnits(quotaUsage * mockSiteDataManager.fakeSites.length));
+  let usage = siteItems[0].getAttribute("usage");
+  is(usage, expected, "Should sum up usages across scheme, port and origin attributes");
+
+  expected = prefStrBundle.getString("persistent");
+  let status = siteItems[0].getAttribute("status");
+  is(status, expected, "Should mark persisted status across scheme, port and origin attributes");
+
+  mockSiteDataManager.unregister();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/components/preferences/in-content/tests/head.js
+++ b/browser/components/preferences/in-content/tests/head.js
@@ -198,23 +198,63 @@ function openSiteDataSettingsDialog() {
   let dialogInitPromise = TestUtils.topicObserved("sitedata-settings-init", () => true);
   let fullyLoadPromise = Promise.all([ dialogLoadPromise, dialogInitPromise ]).then(() => {
     is(dialogOverlay.style.visibility, "visible", "The Settings dialog should be visible");
   });
   settingsBtn.doCommand();
   return fullyLoadPromise;
 }
 
-function assertSitesListed(doc, origins) {
+function assertSitesListed(doc, hosts) {
   let frameDoc = doc.getElementById("dialogFrame").contentDocument;
   let removeBtn = frameDoc.getElementById("removeSelected");
   let removeAllBtn = frameDoc.getElementById("removeAll");
   let sitesList = frameDoc.getElementById("sitesList");
   let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
-  is(totalSitesNumber, origins.length, "Should list the right sites number");
-  origins.forEach(origin => {
-    let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
-    let host = site.getAttribute("host");
-    ok(origin.includes(host), `Should list the site of ${origin}`);
+  is(totalSitesNumber, hosts.length, "Should list the right sites number");
+  hosts.forEach(host => {
+    let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+    ok(site, `Should list the site of ${host}`);
   });
   is(removeBtn.disabled, false, "Should enable the removeSelected button");
   is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
 }
+
+const mockSiteDataManager = {
+
+  _SiteDataManager: null,
+  _originalGetQuotaUsage: null,
+  _originalRemoveQuotaUsage: null,
+
+  _getQuotaUsage() {
+    let results = [];
+    this.fakeSites.forEach(site => {
+      results.push({
+        origin: site.principal.origin,
+        usage: site.usage,
+        persisted: site.persisted
+      });
+    });
+    this._SiteDataManager._getQuotaUsagePromise = Promise.resolve(results);
+    return this._SiteDataManager._getQuotaUsagePromise;
+  },
+
+  _removeQuotaUsage(site) {
+    var target = site.principals[0].URI.host;
+    this.fakeSites = this.fakeSites.filter(fakeSite => {
+      return fakeSite.principal.URI.host != target;
+    });
+  },
+
+  register(SiteDataManager) {
+    this._SiteDataManager = SiteDataManager;
+    this._originalGetQuotaUsage = this._SiteDataManager._getQuotaUsage;
+    this._SiteDataManager._getQuotaUsage = this._getQuotaUsage.bind(this);
+    this._originalRemoveQuotaUsage = this._SiteDataManager._removeQuotaUsage;
+    this._SiteDataManager._removeQuotaUsage = this._removeQuotaUsage.bind(this);
+    this.fakeSites = null;
+  },
+
+  unregister() {
+    this._SiteDataManager._getQuotaUsage = this._originalGetQuotaUsage;
+    this._SiteDataManager._removeQuotaUsage = this._originalRemoveQuotaUsage;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/offline/manifest.appcache
@@ -0,0 +1,3 @@
+CACHE MANIFEST
+# V1
+offline.html
new file mode 100755
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/offline/offline.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html manifest="manifest.appcache">>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Cache-Control" content="public" />
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
+
+  </head>
+
+  <body>
+    <h1>Set up offline appcache Test</h1>
+  </body>
+</html>
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -95,18 +95,22 @@ let gSiteDataSettings = {
           let aHost = a.host.toLowerCase();
           let bHost = b.host.toLowerCase();
           return aHost.localeCompare(bHost);
         }
         break;
 
       case "statusCol":
         sortFunc = (a, b) => {
-          return a.persisted && !b.persisted ? 1 :
-                 !a.persisted && b.persisted ? -1 : 0;
+          if (a.persisted && !b.persisted) {
+            return 1;
+          } else if (!a.persisted && b.persisted) {
+            return -1;
+          }
+          return 0;
         };
         break;
 
       case "usageCol":
         sortFunc = (a, b) => a.usage - b.usage;
         break;
     }
     if (sortDirection === "descending") {