Part 2: Bug 1348733 - Update tests for site data manager now that we use quota storage manager, r=Gijs
authorFischer.json <fischer.json@gmail.com>
Wed, 10 May 2017 09:46:46 -0400
changeset 357835 1bd9e6b07fc06336d9b4e43586850475648b905d
parent 357834 c50e480ea03538a65a292b34ec89e63317689faa
child 357836 79f6ba23157ed61893b5ec55624824a07d11abd4
push id42506
push userryanvm@gmail.com
push dateThu, 11 May 2017 18:54:33 +0000
treeherderautoland@1bd9e6b07fc0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1348733
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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") {