Bug 1203253 - Add back/port the tests that got removed from bug 1140495. r=gijs, a=test-only a=lizzard
authorJared Wein <jwein@mozilla.com>
Thu, 24 Sep 2015 11:03:54 -0400
changeset 296397 b60e63d9a14b21dcba21f0b2a93015ad991e7196
parent 296396 970363447576e9a9ebe811a7aeecc37a0566065c
child 296398 f25b717ac20ab4635dbfbbebf7cb24501ccd10fd
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, test-only, lizzard
bugs1203253, 1140495
milestone43.0a2
Bug 1203253 - Add back/port the tests that got removed from bug 1140495. r=gijs, a=test-only a=lizzard
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_bug705422.js
browser/components/preferences/in-content/tests/browser_chunk_permissions.js
browser/components/preferences/in-content/tests/browser_cookies_exceptions.js
browser/components/preferences/in-content/tests/browser_permissions.js
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -2,26 +2,31 @@
 skip-if = buildapp == "mulet"
 support-files =
   head.js
   privacypane_tests_perwindow.js
 
 [browser_advanced_update.js]
 [browser_basic_rebuild_fonts_test.js]
 [browser_bug410900.js]
+[browser_bug705422.js]
 [browser_bug731866.js]
 [browser_bug795764_cachedisabled.js]
 [browser_bug1018066_resetScrollPosition.js]
 [browser_bug1020245_openPreferences_to_paneContent.js]
 [browser_change_app_handler.js]
 skip-if = os != "win" # This test tests the windows-specific app selection dialog, so can't run on non-Windows
+[browser_chunk_permissions.js]
 [browser_connection.js]
 [browser_connection_bug388287.js]
+[browser_cookies_exceptions.js]
+skip-if = os == "linux" # See bug 1209521 for re-enabling on Linux
 [browser_healthreport.js]
 skip-if = !healthreport || (os == 'linux' && debug)
+[browser_permissions.js]
 [browser_proxy_backup.js]
 [browser_privacypane_1.js]
 [browser_privacypane_3.js]
 [browser_privacypane_4.js]
 [browser_privacypane_5.js]
 [browser_privacypane_8.js]
 skip-if = e10s # Bug ?????? -  "leaked until shutdown [nsGlobalWindow #99 about:preferences]"
 [browser_sanitizeOnShutdown_prefLocked.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_bug705422.js
@@ -0,0 +1,144 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+  // Allow all cookies, then actually set up the test
+  SpecialPowers.pushPrefEnv({"set": [["network.cookie.cookieBehavior", 0]]}, initTest);
+}
+
+function initTest() {
+    const searchTerm = "example";
+    const dummyTerm = "elpmaxe";
+
+    var cm =  Components.classes["@mozilla.org/cookiemanager;1"]
+                        .getService(Components.interfaces.nsICookieManager);
+
+    // delete all cookies (might be left over from other tests)
+    cm.removeAll();
+
+    // data for cookies
+    var vals = [[searchTerm+".com", dummyTerm, dummyTerm],          // match
+                [searchTerm+".org", dummyTerm, dummyTerm],          // match
+                [dummyTerm+".com", searchTerm, dummyTerm],          // match
+                [dummyTerm+".edu", searchTerm+dummyTerm, dummyTerm],// match
+                [dummyTerm+".net", dummyTerm, searchTerm],          // match
+                [dummyTerm+".org", dummyTerm, searchTerm+dummyTerm],// match
+                [dummyTerm+".int", dummyTerm, dummyTerm]];          // no match
+
+    // matches must correspond to above data
+    const matches = 6;
+
+    var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                        .getService(Components.interfaces.nsIIOService);
+    var cookieSvc = Components.classes["@mozilla.org/cookieService;1"]
+                              .getService(Components.interfaces.nsICookieService);
+    var v;
+    // inject cookies
+    for (v in vals) {
+        let [host, name, value] = vals[v];
+        var cookieUri = ios.newURI("http://"+host, null, null);
+        cookieSvc.setCookieString(cookieUri, null, name+"="+value+";", null);
+    }
+
+    // open cookie manager
+    var cmd = window.openDialog("chrome://browser/content/preferences/cookies.xul",
+                                "Browser:Cookies", "", {});
+
+    // when it has loaded, run actual tests
+    cmd.addEventListener("load", function() {executeSoon(function() {runTest(cmd, searchTerm, vals.length, matches);});}, false);
+}
+
+function isDisabled(win, expectation) {
+    var disabled = win.document.getElementById("removeAllCookies").disabled;
+    is(disabled, expectation, "Remove all cookies button has correct state: "+(expectation?"disabled":"enabled"));
+}
+
+function runTest(win, searchTerm, cookies, matches) {
+    var cm =  Components.classes["@mozilla.org/cookiemanager;1"]
+                        .getService(Components.interfaces.nsICookieManager);
+
+
+    // number of cookies should match injected cookies
+    var cnt = 0,
+        enumerator = cm.enumerator;
+    while (enumerator.hasMoreElements()) {
+        cnt++;
+        enumerator.getNext();
+    }
+    is(cnt, cookies, "Number of cookies match injected cookies");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // filter cookies and count matches
+    win.gCookiesWindow.setFilter(searchTerm);
+    is(win.gCookiesWindow._view.rowCount, matches, "Correct number of cookies shown after filter is applied");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+
+    // select first cookie and delete
+    var tree = win.document.getElementById("cookiesList");
+    var deleteButton = win.document.getElementById("removeSelectedCookies");
+    var rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "cell");
+    EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
+    EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
+
+    // count cookies should be matches-1
+    is(win.gCookiesWindow._view.rowCount, matches-1, "Deleted selected cookie");
+
+    // select two adjacent cells and delete
+    EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
+    var eventObj = {};
+    if (navigator.platform.indexOf("Mac") >= 0)
+        eventObj.metaKey = true;
+    else
+        eventObj.ctrlKey = true;
+    rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
+    EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, eventObj, win);
+    EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
+
+    // count cookies should be matches-3
+    is(win.gCookiesWindow._view.rowCount, matches-3, "Deleted selected two adjacent cookies");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // delete all cookies and count
+    var deleteAllButton = win.document.getElementById("removeAllCookies");
+    EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
+    is(win.gCookiesWindow._view.rowCount, 0, "Deleted all matching cookies");
+
+    // "delete all cookies" should be disabled
+    isDisabled(win, true);
+
+    // clear filter and count should be cookies-matches
+    win.gCookiesWindow.setFilter("");
+    is(win.gCookiesWindow._view.rowCount, cookies-matches, "Unmatched cookies remain");
+
+    // "delete all cookies" should be enabled
+    isDisabled(win, false);
+
+    // delete all cookies and count should be 0
+    EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
+    is(win.gCookiesWindow._view.rowCount, 0, "Deleted all cookies");
+
+    // check that datastore is also at 0
+    var cnt = 0,
+        enumerator = cm.enumerator;
+    while (enumerator.hasMoreElements()) {
+        cnt++;
+        enumerator.getNext();
+    }
+    is(cnt, 0, "Zero cookies remain");
+
+    // "delete all cookies" should be disabled
+    isDisabled(win, true);
+
+    // clean up
+    win.close();
+    finish();
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_chunk_permissions.js
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+  "resource://testing-common/PlacesTestUtils.jsm");
+
+const ABOUT_PERMISSIONS_SPEC = "about:permissions";
+
+const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
+const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
+const TEST_URI_3 = NetUtil.newURI("http://wikipedia.org/");
+
+// values from DefaultPermissions object
+const PERM_UNKNOWN = 0;
+const PERM_ALLOW = 1;
+const PERM_DENY = 2;
+
+// used to set permissions on test sites
+const TEST_PERMS = {
+  "password": PERM_ALLOW,
+  "cookie": PERM_ALLOW,
+  "geo": PERM_UNKNOWN,
+  "indexedDB": PERM_UNKNOWN,
+  "popup": PERM_DENY
+};
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(cleanUp);
+  setup(function() {
+    runNextTest();
+  });
+}
+
+function setup(aCallback) {
+  // add test history visit
+  PlacesTestUtils.addVisits(TEST_URI_1).then(() => {
+    // set permissions ourselves to avoid problems with different defaults
+    // from test harness configuration
+    for (let type in TEST_PERMS) {
+      if (type == "password") {
+        Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
+      } else {
+        // set permissions on a site without history visits to test enumerateServices
+        Services.perms.add(TEST_URI_2, type, TEST_PERMS[type]);
+      }
+    }
+
+    Services.perms.add(TEST_URI_3, "popup", TEST_PERMS["popup"]);
+    aCallback();
+  });
+}
+
+function cleanUp() {
+  for (let type in TEST_PERMS) {
+    if (type != "password") {
+      Services.perms.remove(TEST_URI_1, type);
+      Services.perms.remove(TEST_URI_2, type);
+      Services.perms.remove(TEST_URI_3, type);
+    }
+  }
+}
+
+function runNextTest() {
+  if (gTestIndex == tests.length) {
+    PlacesTestUtils.clearHistory().then(finish);
+    return;
+  }
+
+  let nextTest = tests[gTestIndex++];
+  info(nextTest.desc);
+
+  function preinit_observer() {
+    Services.obs.removeObserver(preinit_observer, "browser-permissions-preinit");
+    nextTest.preInit();
+  }
+  Services.obs.addObserver(preinit_observer, "browser-permissions-preinit", false);
+
+  function init_observer() {
+    Services.obs.removeObserver(init_observer, "browser-permissions-initialized");
+    nextTest.run();
+  }
+  Services.obs.addObserver(init_observer, "browser-permissions-initialized", false);
+
+  // open about:permissions
+  let tab = gBrowser.selectedTab = gBrowser.addTab("about:permissions");
+  registerCleanupFunction(function() {
+    gBrowser.removeTab(tab);
+  });
+}
+
+var gSitesList;
+
+var gTestIndex = 0;
+var tests = [
+  // 'preInit' occurs after opening about:permissions, before sites-list is populated
+  // 'run' occurs after sites-list is populated
+  {
+    desc: "test filtering before sites-list is fully constructed.",
+    preInit: function() {
+      let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
+      sitesFilter.value = TEST_URI_2.host;
+      sitesFilter.doCommand();
+    },
+    run: function() {
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
+      ok(testSite1.collapsed, "test site 1 is collapsed after early filtering");
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
+      ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering");
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
+      ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
+
+      runNextTest();
+    }
+  },
+  {
+    desc: "test removing from sites-list before it is fully constructed.",
+    preInit: function() {
+      ForgetAboutSite.removeDataFromDomain(TEST_URI_2.host);
+    },
+    run: function() {
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
+      ok(testSite1, "test site 1 was not removed from sites list");
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
+      ok(!testSite2, "test site 2 was pre-removed from sites list");
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
+      ok(testSite3, "test site 3 was not removed from sites list");
+
+      runNextTest();
+    }
+  }
+];
+
+function getSiteItem(aPrePath) {
+  return gBrowser.contentDocument.
+                  querySelector(".site[value='" + aPrePath + "']");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_cookies_exceptions.js
@@ -0,0 +1,304 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+  testRunner.runTests();
+}
+
+var testRunner = {
+
+  tests:
+    [
+      {
+        test: function(params) {
+          params.url.value = "test.com";
+          params.btnAllow.doCommand();
+          is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission text should be set correctly");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://test.com", data: "added",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "test.com";
+          params.btnBlock.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
+                                          "permission should change to deny in UI");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
+                        capability: Ci.nsIPermissionManager.DENY_ACTION  }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "test.com";
+          params.btnAllow.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission should revert back to allow");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "test.com";
+          params.btnRemove.doCommand();
+          is(params.tree.view.rowCount, 0, "exception should be removed");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }],
+      },
+      {
+        expectPermObservancesDuringTestFunction: true,
+        test: function(params) {
+          let uri = params.ioService.newURI("http://test.com", null, null);
+          params.pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
+          is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "popup", origin: "http://test.com", data: "added",
+                        capability: Ci.nsIPermissionManager.DENY_ACTION }],
+        cleanUp: function(params) {
+          let uri = params.ioService.newURI("http://test.com", null, null);
+          params.pm.remove(uri, "popup");
+        },
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission text should be set correctly");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "added",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnBlock.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
+                                          "permission should change to deny in UI");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.DENY_ACTION  }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission should revert back to allow");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnRemove.doCommand();
+          is(params.tree.view.rowCount, 0, "exception should be removed");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "deleted" }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "localhost:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
+          is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission text should be set correctly");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://localhost:12345", data: "added",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "localhost:12345";
+          params.btnBlock.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
+                                          "permission should change to deny in UI");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.DENY_ACTION  }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "localhost:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission should revert back to allow");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "localhost:12345";
+          params.btnRemove.doCommand();
+          is(params.tree.view.rowCount, 0, "exception should be removed");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "http://localhost:12345", data: "deleted" }],
+      },
+    ],
+
+  _currentTest: -1,
+
+  runTests: function() {
+    this._currentTest++;
+
+    info("Running test #" + (this._currentTest + 1) + "\n");
+    let that = this;
+    let p = this.runCurrentTest(this._currentTest + 1);
+    p.then(function() {
+      if (that._currentTest == that.tests.length - 1) {
+        finish();
+      }
+      else {
+        that.runTests();
+      }
+    });
+  },
+
+  runCurrentTest: function(testNumber) {
+    return new Promise(function(resolve, reject) {
+
+      let helperFunctions = {
+        windowLoad: function(win) {
+          let doc = win.document;
+          let params = {
+            doc,
+            tree: doc.getElementById("permissionsTree"),
+            nameCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(0),
+            statusCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
+            url: doc.getElementById("url"),
+            btnAllow: doc.getElementById("btnAllow"),
+            btnBlock: doc.getElementById("btnBlock"),
+            btnApplyChanges: doc.getElementById("btnApplyChanges"),
+            btnRemove: doc.getElementById("removePermission"),
+            pm: Cc["@mozilla.org/permissionmanager;1"]
+                       .getService(Ci.nsIPermissionManager),
+            ioService: Cc["@mozilla.org/network/io-service;1"]
+                              .getService(Ci.nsIIOService),
+            allowText: win.gPermissionManager._getCapabilityString(
+                                Ci.nsIPermissionManager.ALLOW_ACTION),
+            denyText: win.gPermissionManager._getCapabilityString(
+                               Ci.nsIPermissionManager.DENY_ACTION),
+            allow: Ci.nsIPermissionManager.ALLOW_ACTION,
+            deny: Ci.nsIPermissionManager.DENY_ACTION,
+          };
+
+          let permObserver = {
+            observe: function(aSubject, aTopic, aData) {
+              if (aTopic != "perm-changed")
+                return;
+
+              if (testRunner.tests[testRunner._currentTest].observances.length == 0) {
+                // Should fail here as we are not expecting a notification, but we don't.
+                // See bug 1063410.
+                return;
+              }
+
+              let permission = aSubject.QueryInterface(Ci.nsIPermission);
+              let expected = testRunner.tests[testRunner._currentTest].observances.shift();
+
+              is(aData, expected.data, "type of message should be the same");
+              for each (let prop in ["type", "capability"]) {
+                if (expected[prop])
+                  is(permission[prop], expected[prop],
+                    "property: \"" + prop  + "\" should be equal");
+              }
+
+              if (expected.origin) {
+                is(permission.principal.origin, expected.origin,
+                   "property: \"origin\" should be equal");
+              }
+
+              os.removeObserver(permObserver, "perm-changed");
+
+              let test = testRunner.tests[testRunner._currentTest];
+              if (!test.expectPermObservancesDuringTestFunction) {
+                if (test.cleanUp) {
+                  test.cleanUp(params);
+                }
+
+                gBrowser.removeCurrentTab();
+                resolve();
+              }
+            },
+          };
+
+          let os = Cc["@mozilla.org/observer-service;1"]
+                     .getService(Ci.nsIObserverService);
+
+          os.addObserver(permObserver, "perm-changed", false);
+
+          if (testRunner._currentTest == 0) {
+            is(params.tree.view.rowCount, 0, "no cookie exceptions");
+          }
+
+          try {
+            let test = testRunner.tests[testRunner._currentTest];
+            test.test(params);
+            if (test.expectPermObservancesDuringTestFunction) {
+              if (test.cleanUp) {
+                test.cleanUp(params);
+              }
+
+              gBrowser.removeCurrentTab();
+              resolve();
+            }
+          } catch (ex) {
+            ok(false, "exception while running test #" +
+                      testNumber + ": " + ex);
+          }
+        },
+      };
+
+      openPreferencesViaOpenPreferencesAPI("panePrivacy", null, {leaveOpen: true}).then(function() {
+        let doc = gBrowser.contentDocument;
+        let historyMode = doc.getElementById("historyMode");
+        historyMode.value = "custom";
+        historyMode.doCommand();
+        doc.getElementById("cookieExceptions").doCommand();
+
+        let subDialogURL = "chrome://browser/content/preferences/permissions.xul";
+        promiseLoadSubDialog(subDialogURL).then(function(win) {
+          helperFunctions.windowLoad(win);
+        });
+      });
+    });
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_permissions.js
@@ -0,0 +1,334 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+  "resource://testing-common/PlacesTestUtils.jsm");
+
+const ABOUT_PERMISSIONS_SPEC = "about:permissions";
+
+const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
+const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
+
+const TEST_PRINCIPAL_1 =
+  Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_1, {});
+const TEST_PRINCIPAL_2 =
+  Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_2, {});
+
+// values from DefaultPermissions object
+const PERM_UNKNOWN = 0;
+const PERM_ALLOW = 1;
+const PERM_DENY = 2;
+// cookie specific permissions
+const PERM_FIRST_PARTY_ONLY = 9;
+
+// used to set permissions on test sites
+const TEST_PERMS = {
+  "password": PERM_ALLOW,
+  "cookie": PERM_ALLOW,
+  "geo": PERM_UNKNOWN,
+  "push": PERM_DENY,
+  "indexedDB": PERM_UNKNOWN,
+  "popup": PERM_DENY,
+  "camera": PERM_UNKNOWN,
+  "microphone": PERM_UNKNOWN
+};
+
+const NO_GLOBAL_ALLOW = [
+  "geo",
+  "indexedDB",
+];
+
+// number of managed permissions in the interface
+const TEST_PERMS_COUNT = 8;
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(cleanUp);
+
+  // add test history visit
+  PlacesTestUtils.addVisits(TEST_URI_1).then(() => {
+    // set permissions ourselves to avoid problems with different defaults
+    // from test harness configuration
+    for (let type in TEST_PERMS) {
+      if (type == "password") {
+        Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
+      } else {
+        // set permissions on a site without history visits to test enumerateServices
+        Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, type, TEST_PERMS[type]);
+      }
+    }
+
+    // open about:permissions
+    gBrowser.selectedTab = gBrowser.addTab("about:permissions");
+  });
+
+  function observer() {
+    Services.obs.removeObserver(observer, "browser-permissions-initialized");
+    runNextTest();
+  }
+  Services.obs.addObserver(observer, "browser-permissions-initialized", false);
+}
+
+function cleanUp() {
+  for (let type in TEST_PERMS) {
+    if (type != "password") {
+      Services.perms.removeFromPrincipal(TEST_PRINCIPAL_1, type);
+      Services.perms.removeFromPrincipal(TEST_PRINCIPAL_2, type);
+    }
+  }
+
+  gBrowser.removeTab(gBrowser.selectedTab);
+}
+
+function runNextTest() {
+  if (gTestIndex == tests.length) {
+    PlacesTestUtils.clearHistory().then(finish);
+    return;
+  }
+
+  let nextTest = tests[gTestIndex++];
+  info("[" + nextTest.name + "] running test");
+  nextTest();
+}
+
+var gSitesList;
+var gHeaderDeck;
+var gSiteLabel;
+
+var gTestIndex = 0;
+var tests = [
+  function test_page_load() {
+    is(gBrowser.currentURI.spec, ABOUT_PERMISSIONS_SPEC, "about:permissions loaded");
+
+    gSitesList = gBrowser.contentDocument.getElementById("sites-list");
+    ok(gSitesList, "got sites list");
+
+    gHeaderDeck = gBrowser.contentDocument.getElementById("header-deck");
+    ok(gHeaderDeck, "got header deck");
+
+    gSiteLabel = gBrowser.contentDocument.getElementById("site-label");
+    ok(gSiteLabel, "got site label");
+
+    runNextTest();
+  },
+
+  function test_sites_list() {
+    is(gSitesList.firstChild.id, "all-sites-item",
+       "all sites is the first item in the sites list");
+
+    ok(getSiteItem(TEST_URI_1.prePath), "site item from places db exists");
+    ok(getSiteItem(TEST_URI_2.prePath), "site item from enumerating services exists");
+
+    runNextTest();
+  },
+
+  function test_filter_sites_list() {
+    // set filter to test host
+    let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
+    sitesFilter.value = TEST_URI_1.host;
+    sitesFilter.doCommand();
+
+    // make sure correct sites are collapsed/showing
+    let testSite1 = getSiteItem(TEST_URI_1.prePath);
+    ok(!testSite1.collapsed, "test site 1 is not collapsed");
+    let testSite2 = getSiteItem(TEST_URI_2.prePath);
+    ok(testSite2.collapsed, "test site 2 is collapsed");
+
+    // clear filter
+    sitesFilter.value = "";
+    sitesFilter.doCommand();
+
+    runNextTest();
+  },
+
+  function test_all_sites() {
+    // "All Sites" item should be selected when the page is first loaded
+    is(gSitesList.selectedItem, gBrowser.contentDocument.getElementById("all-sites-item"),
+       "all sites item is selected");
+
+    let defaultsHeader = gBrowser.contentDocument.getElementById("defaults-header");
+    is(defaultsHeader, gHeaderDeck.selectedPanel,
+       "correct header shown for all sites");
+
+    ok(gBrowser.contentDocument.getElementById("passwords-count").hidden,
+       "passwords count is hidden");
+    ok(gBrowser.contentDocument.getElementById("cookies-count").hidden,
+       "cookies count is hidden");
+
+    // Test to make sure "Allow" items hidden for certain permission types
+    NO_GLOBAL_ALLOW.forEach(function(aType) {
+      let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
+      ok(menuitem.hidden, aType + " allow menuitem hidden for all sites");
+    });
+
+    runNextTest();
+  },
+
+  function test_all_sites_permission() {
+    // apply the old default of allowing all cookies
+    Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+    // there should be no user-set pref for cookie behavior
+    is(Services.prefs.getIntPref("network.cookie.cookieBehavior"), PERM_UNKNOWN,
+       "network.cookie.cookieBehavior is expected default");
+
+    // the default behavior is to allow cookies
+    let cookieMenulist = getPermissionMenulist("cookie");
+    is(cookieMenulist.value, PERM_ALLOW,
+       "menulist correctly shows that cookies are allowed");
+
+    // set the pref to block cookies
+    Services.prefs.setIntPref("network.cookie.cookieBehavior", PERM_DENY);
+    // check to make sure this change is reflected in the UI
+    is(cookieMenulist.value, PERM_DENY, "menulist correctly shows that cookies are blocked");
+
+    // clear the pref
+    Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+
+    runNextTest();
+  },
+
+  function test_manage_all_passwords() {
+    // make sure "Manage All Passwords..." button opens the correct dialog
+    addWindowListener("chrome://passwordmgr/content/passwordManager.xul", runNextTest);
+    gBrowser.contentDocument.getElementById("passwords-manage-all-button").doCommand();
+
+  },
+
+  function test_manage_all_cookies() {
+    // make sure "Manage All Cookies..." button opens the correct dialog
+    addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
+    gBrowser.contentDocument.getElementById("cookies-manage-all-button").doCommand();
+  },
+
+  function test_select_site() {
+    // select the site that has the permissions we set at the beginning of the test
+    let testSiteItem = getSiteItem(TEST_URI_2.prePath);
+    gSitesList.selectedItem = testSiteItem;
+
+    let siteHeader = gBrowser.contentDocument.getElementById("site-header");
+    is(siteHeader, gHeaderDeck.selectedPanel,
+       "correct header shown for a specific site");
+    is(gSiteLabel.value, TEST_URI_2.prePath, "header updated for selected site");
+
+    ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden,
+       "passwords count is not hidden");
+    ok(!gBrowser.contentDocument.getElementById("cookies-count").hidden,
+       "cookies count is not hidden");
+
+    // Test to make sure "Allow" items are *not* hidden for certain permission types
+    NO_GLOBAL_ALLOW.forEach(function(aType) {
+      let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
+      ok(!menuitem.hidden, aType  + " allow menuitem not hidden for single site");
+    });
+
+    runNextTest();
+  },
+
+  function test_permissions() {
+    let menulists = gBrowser.contentDocument.getElementsByClassName("pref-menulist");
+    is(menulists.length, TEST_PERMS_COUNT, "got expected number of managed permissions");
+
+    for (let i = 0; i < menulists.length; i++) {
+      let permissionMenulist = menulists.item(i);
+      let permissionType = permissionMenulist.getAttribute("type");
+
+      // permissions should reflect what we set at the beginning of the test
+      is(permissionMenulist.value, TEST_PERMS[permissionType],
+        "got expected value for " + permissionType + " permission");
+    }
+
+    runNextTest();
+  },
+
+  function test_permission_change() {
+    let geoMenulist = getPermissionMenulist("geo");
+    is(geoMenulist.value, PERM_UNKNOWN, "menulist correctly shows that geolocation permission is unspecified");
+
+    // change a permission programatically
+    Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, "geo", PERM_DENY);
+    // check to make sure this change is reflected in the UI
+    is(geoMenulist.value, PERM_DENY, "menulist shows that geolocation is blocked");
+
+    // change a permisssion in the UI
+    let geoAllowItem = gBrowser.contentDocument.getElementById("geo-" + PERM_ALLOW);
+    geoMenulist.selectedItem = geoAllowItem;
+    geoMenulist.doCommand();
+    // check to make sure this change is reflected in the permission manager
+    is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "geo"), PERM_ALLOW,
+       "permission manager shows that geolocation is allowed");
+
+
+    // change a site-specific cookie permission, just for fun
+    let cookieMenuList = getPermissionMenulist("cookie");
+    let cookieItem = gBrowser.contentDocument.getElementById("cookie-" + PERM_FIRST_PARTY_ONLY);
+    cookieMenuList.selectedItem = cookieItem;
+    cookieMenuList.doCommand();
+    is(cookieMenuList.value, PERM_FIRST_PARTY_ONLY, "menulist correctly shows that " +
+       "first party only cookies are allowed");
+    is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "cookie"),
+       PERM_FIRST_PARTY_ONLY, "permission manager shows that first party cookies " +
+       "are allowed");
+
+    runNextTest();
+  },
+
+  function test_forget_site() {
+    // click "Forget About This Site" button
+    gBrowser.contentDocument.getElementById("forget-site-button").doCommand();
+    PlacesTestUtils.clearHistory().then(() => {
+      is(gSiteLabel.value, "", "site label cleared");
+
+      let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
+      is(gSitesList.selectedItem, allSitesItem,
+         "all sites item selected after forgetting selected site");
+
+      // check to make sure site is gone from sites list
+      let testSiteItem = getSiteItem(TEST_URI_2.prePath);
+      ok(!testSiteItem, "site removed from sites list");
+
+      // check to make sure we forgot all permissions corresponding to site
+      for (let type in TEST_PERMS) {
+        if (type == "password") {
+          ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
+             "password saving should be enabled by default");
+        } else {
+          is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
+             type + " permission should not be set for test site 2");
+        }
+      }
+
+      runNextTest();
+    });
+  }
+];
+
+function getPermissionMenulist(aType) {
+  return gBrowser.contentDocument.getElementById(aType + "-menulist");
+}
+
+function getSiteItem(aHost) {
+  return gBrowser.contentDocument.
+                  querySelector(".site[value='" + aHost + "']");
+}
+
+function addWindowListener(aURL, aCallback) {
+  Services.wm.addListener({
+    onOpenWindow: function(aXULWindow) {
+      info("window opened, waiting for focus");
+      Services.wm.removeListener(this);
+
+      var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDOMWindow);
+      waitForFocus(function() {
+        is(domwindow.document.location.href, aURL, "should have seen the right window open");
+        domwindow.close();
+        aCallback();
+      }, domwindow);
+    },
+    onCloseWindow: function(aXULWindow) { },
+    onWindowTitleChange: function(aXULWindow, aNewTitle) { }
+  });
+}