summaryrefslogtreecommitdiffstats
path: root/extensions/permissions/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /extensions/permissions/test
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--extensions/permissions/test/PermissionTestUtils.jsm108
-rw-r--r--extensions/permissions/test/browser.ini8
-rw-r--r--extensions/permissions/test/browser_permmgr_sync.js449
-rw-r--r--extensions/permissions/test/browser_permmgr_viewsrc.js27
-rw-r--r--extensions/permissions/test/gtest/PermissionManagerTest.cpp52
-rw-r--r--extensions/permissions/test/gtest/moz.build11
-rw-r--r--extensions/permissions/test/moz.build15
-rw-r--r--extensions/permissions/test/unit/head.js26
-rw-r--r--extensions/permissions/test/unit/test_permmanager_cleardata.js93
-rw-r--r--extensions/permissions/test/unit/test_permmanager_default_pref.js75
-rw-r--r--extensions/permissions/test/unit/test_permmanager_defaults.js489
-rw-r--r--extensions/permissions/test/unit/test_permmanager_expiration.js189
-rw-r--r--extensions/permissions/test/unit/test_permmanager_getAllByTypeSince.js77
-rw-r--r--extensions/permissions/test/unit/test_permmanager_getAllByTypes.js122
-rw-r--r--extensions/permissions/test/unit/test_permmanager_getAllForPrincipal.js70
-rw-r--r--extensions/permissions/test/unit/test_permmanager_getAllWithTypePrefix.js82
-rw-r--r--extensions/permissions/test/unit/test_permmanager_getPermissionObject.js98
-rw-r--r--extensions/permissions/test/unit/test_permmanager_idn.js75
-rw-r--r--extensions/permissions/test/unit/test_permmanager_ipc.js89
-rw-r--r--extensions/permissions/test/unit/test_permmanager_load_invalid_entries.js264
-rw-r--r--extensions/permissions/test/unit/test_permmanager_local_files.js74
-rw-r--r--extensions/permissions/test/unit/test_permmanager_matches.js203
-rw-r--r--extensions/permissions/test/unit/test_permmanager_matchesuri.js252
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_10-11.js196
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_11-12.js230
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_4-7.js264
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js279
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js365
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js203
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js366
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js199
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_7-8.js328
-rw-r--r--extensions/permissions/test/unit/test_permmanager_migrate_9-10.js262
-rw-r--r--extensions/permissions/test/unit/test_permmanager_notifications.js139
-rw-r--r--extensions/permissions/test/unit/test_permmanager_oa_strip.js220
-rw-r--r--extensions/permissions/test/unit/test_permmanager_remove_add_update.js83
-rw-r--r--extensions/permissions/test/unit/test_permmanager_removeall.js47
-rw-r--r--extensions/permissions/test/unit/test_permmanager_removebytype.js73
-rw-r--r--extensions/permissions/test/unit/test_permmanager_removebytypesince.js86
-rw-r--r--extensions/permissions/test/unit/test_permmanager_removepermission.js56
-rw-r--r--extensions/permissions/test/unit/test_permmanager_removesince.js83
-rw-r--r--extensions/permissions/test/unit/test_permmanager_site_scope.js120
-rw-r--r--extensions/permissions/test/unit/test_permmanager_subdomains.js106
-rw-r--r--extensions/permissions/test/unit/xpcshell.ini59
44 files changed, 6712 insertions, 0 deletions
diff --git a/extensions/permissions/test/PermissionTestUtils.jsm b/extensions/permissions/test/PermissionTestUtils.jsm
new file mode 100644
index 0000000000..5d36c31a4b
--- /dev/null
+++ b/extensions/permissions/test/PermissionTestUtils.jsm
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Utility module for tests to access the PermissionManager
+ * with uri or origin string parameters.
+ */
+
+"use strict";
+
+let pm = Services.perms;
+
+let secMan = Services.scriptSecurityManager;
+
+const EXPORTED_SYMBOLS = ["PermissionTestUtils"];
+
+/**
+ * Convert origin string or uri to principal.
+ * If passed an nsIPrincipal it will be returned without conversion.
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject - Subject to convert to principal
+ * @returns {Ci.nsIPrincipal} Principal created from subject
+ */
+function convertToPrincipal(subject) {
+ if (subject instanceof Ci.nsIPrincipal) {
+ return subject;
+ }
+ if (typeof subject === "string") {
+ return secMan.createContentPrincipalFromOrigin(subject);
+ }
+ if (subject === null || subject instanceof Ci.nsIURI) {
+ return secMan.createContentPrincipal(subject, {});
+ }
+ throw new Error(
+ "subject parameter must be an nsIURI an origin string or a principal."
+ );
+}
+
+let PermissionTestUtils = {
+ /**
+ * Add permission information for a given subject.
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ add(subject, ...args) {
+ return pm.addFromPrincipal(convertToPrincipal(subject), ...args);
+ },
+ /**
+ * Get all custom permissions for a given subject.
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ getAll(subject, ...args) {
+ return pm.getAllForPrincipal(convertToPrincipal(subject), ...args);
+ },
+ /**
+ * Remove permission information for a given subject and permission type
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ remove(subject, ...args) {
+ return pm.removeFromPrincipal(convertToPrincipal(subject), ...args);
+ },
+ /**
+ * Test whether a website has permission to perform the given action.
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ testPermission(subject, ...args) {
+ return pm.testPermissionFromPrincipal(convertToPrincipal(subject), ...args);
+ },
+ /**
+ * Test whether a website has permission to perform the given action.
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ testExactPermission(subject, ...args) {
+ return pm.testExactPermissionFromPrincipal(
+ convertToPrincipal(subject),
+ ...args
+ );
+ },
+ /**
+ * Get the permission object associated with the given subject and action.
+ * Subject can be a principal, uri or origin string.
+ * @see nsIPermissionManager for documentation
+ *
+ * @param {Ci.nsIPrincipal|Ci.nsIURI|string} subject
+ * @param {*} args
+ */
+ getPermissionObject(subject, type, exactHost = false) {
+ return pm.getPermissionObject(convertToPrincipal(subject), type, exactHost);
+ },
+};
diff --git a/extensions/permissions/test/browser.ini b/extensions/permissions/test/browser.ini
new file mode 100644
index 0000000000..b71b10dfaf
--- /dev/null
+++ b/extensions/permissions/test/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+
+[browser_permmgr_sync.js]
+# The browser_permmgr_sync test runs code
+# paths which would hit the debug only assertion in
+# PermissionManager::PermissionKey::CreateFromPrincipal.
+skip-if = debug
+[browser_permmgr_viewsrc.js]
diff --git a/extensions/permissions/test/browser_permmgr_sync.js b/extensions/permissions/test/browser_permmgr_sync.js
new file mode 100644
index 0000000000..d4ada33394
--- /dev/null
+++ b/extensions/permissions/test/browser_permmgr_sync.js
@@ -0,0 +1,449 @@
+function addPerm(aOrigin, aName) {
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ aOrigin
+ );
+ Services.perms.addFromPrincipal(
+ principal,
+ aName,
+ Services.perms.ALLOW_ACTION
+ );
+}
+
+add_task(async function() {
+ // Make sure that we get a new process for the tab which we create. This is
+ // important, because we want to assert information about the initial state
+ // of the local permissions cache.
+
+ addPerm("http://example.com", "perm1");
+ addPerm("http://foo.bar.example.com", "perm2");
+ addPerm("about:home", "perm3");
+ addPerm("https://example.com", "perm4");
+ // NOTE: This permission is a preload permission, so it should be available in
+ // the content process from startup.
+ addPerm("https://somerandomwebsite.com", "cookie");
+
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank", forceNewProcess: true },
+ async function(aBrowser) {
+ await SpecialPowers.spawn(aBrowser, [], async function() {
+ // Before the load http URIs shouldn't have been sent down yet
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm1-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "perm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm2-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "perm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm3-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm4-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://somerandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "cookie-1"
+ );
+
+ let iframe = content.document.createElement("iframe");
+
+ // Perform a load of example.com
+ await new Promise(resolve => {
+ iframe.setAttribute("src", "http://example.com");
+ iframe.onload = resolve;
+ content.document.body.appendChild(iframe);
+ });
+
+ // After the load finishes, the iframe process should know about example.com, but not foo.bar.example.com
+ await content.SpecialPowers.spawn(iframe, [], async function() {
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm1-2"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "perm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm2-2"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "perm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm3-2"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm4-2"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://somerandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "cookie-2"
+ );
+ });
+
+ // In Fission only, the parent process should have no knowledge about the child
+ // process permissions
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "perm1-3"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "perm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm2-3"
+ );
+
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm4-3"
+ );
+ });
+
+ addPerm("http://example.com", "newperm1");
+ addPerm("http://foo.bar.example.com", "newperm2");
+ addPerm("about:home", "newperm3");
+ addPerm("https://example.com", "newperm4");
+ addPerm("https://someotherrandomwebsite.com", "cookie");
+
+ await SpecialPowers.spawn(aBrowser, [], async function() {
+ // The new permissions should be available, but only for
+ // http://example.com (without Fission), and about:home.
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "perm1-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "newperm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "newperm1-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "perm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm2-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "newperm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "newperm2-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "perm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm3-3"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "newperm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "newperm3-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm4-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "newperm4"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "newperm4-1"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://somerandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "cookie-3"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://someotherrandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "othercookie-3"
+ );
+
+ let iframe = content.document.createElement("iframe");
+ // Loading a subdomain now, on https
+ await new Promise(resolve => {
+ iframe.setAttribute("src", "https://sub1.test1.example.com");
+ iframe.onload = resolve;
+ content.document.body.appendChild(iframe);
+ });
+
+ // After the load finishes, the iframe process should not know about
+ // the permissions of its base domain.
+ await content.SpecialPowers.spawn(iframe, [], async function() {
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm4-5"
+ );
+
+ // In Fission not across schemes, though.
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "perm1-5"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "newperm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "newperm1-2"
+ );
+ });
+
+ // The parent process should still have no permission under Fission.
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "perm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "perm1-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ ),
+ "newperm1"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "newperm1-3"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ ),
+ "perm4"
+ ),
+ SpecialPowers.useRemoteSubframes
+ ? Services.perms.UNKNOWN_ACTION
+ : Services.perms.ALLOW_ACTION,
+ "perm4-6"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "perm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "perm2-5"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://foo.bar.example.com"
+ ),
+ "newperm2"
+ ),
+ Services.perms.UNKNOWN_ACTION,
+ "newperm2-2"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "perm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "perm3-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "about:home"
+ ),
+ "newperm3"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "newperm3-2"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://somerandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "cookie-4"
+ );
+ is(
+ Services.perms.testPermissionFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://someotherrandomwebsite.com"
+ ),
+ "cookie"
+ ),
+ Services.perms.ALLOW_ACTION,
+ "othercookie-4"
+ );
+ });
+ }
+ );
+});
diff --git a/extensions/permissions/test/browser_permmgr_viewsrc.js b/extensions/permissions/test/browser_permmgr_viewsrc.js
new file mode 100644
index 0000000000..ee095c56fb
--- /dev/null
+++ b/extensions/permissions/test/browser_permmgr_viewsrc.js
@@ -0,0 +1,27 @@
+add_task(async function() {
+ // Add a permission for example.com, start a new content process, and make
+ // sure that the permission has been sent down.
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ );
+ Services.perms.addFromPrincipal(
+ principal,
+ "viewsourceTestingPerm",
+ Services.perms.ALLOW_ACTION
+ );
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "view-source:https://example.com",
+ /* waitForLoad */ true,
+ /* waitForStateStop */ false,
+ /* forceNewProcess */ true
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [principal], async function(p) {
+ is(
+ Services.perms.testPermissionFromPrincipal(p, "viewsourceTestingPerm"),
+ Services.perms.ALLOW_ACTION
+ );
+ });
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/extensions/permissions/test/gtest/PermissionManagerTest.cpp b/extensions/permissions/test/gtest/PermissionManagerTest.cpp
new file mode 100644
index 0000000000..b69a7d46f2
--- /dev/null
+++ b/extensions/permissions/test/gtest/PermissionManagerTest.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNetUtil.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/OriginAttributes.h"
+#include "mozilla/PermissionManager.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Unused.h"
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+
+using namespace mozilla;
+
+class PermissionManagerTester : public ::testing::Test {
+ protected:
+ PermissionManagerTester()
+ : mNonExistentType("permissionTypeThatIsGuaranteedToNeverExist"_ns) {}
+ void SetUp() override {
+ mPermissionManager = PermissionManager::GetInstance();
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv =
+ NS_NewURI(getter_AddRefs(uri),
+ "https://test.origin.with.subdomains.example.com"_ns);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ mPrincipal =
+ mozilla::BasePrincipal::CreateContentPrincipal(uri, OriginAttributes());
+ }
+
+ void TearDown() override {
+ mPermissionManager = nullptr;
+ mPrincipal = nullptr;
+ }
+
+ static const unsigned kNumIterations = 100000;
+
+ nsLiteralCString mNonExistentType;
+ RefPtr<PermissionManager> mPermissionManager;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+MOZ_GTEST_BENCH_F(PermissionManagerTester,
+ TestNonExistentPermissionFromPrincipal, [this] {
+ for (unsigned i = 0; i < kNumIterations; ++i) {
+ uint32_t result = 0;
+ Unused << mPermissionManager->TestPermissionFromPrincipal(
+ mPrincipal, mNonExistentType, &result);
+ }
+ });
diff --git a/extensions/permissions/test/gtest/moz.build b/extensions/permissions/test/gtest/moz.build
new file mode 100644
index 0000000000..132c384597
--- /dev/null
+++ b/extensions/permissions/test/gtest/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "PermissionManagerTest.cpp",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/extensions/permissions/test/moz.build b/extensions/permissions/test/moz.build
new file mode 100644
index 0000000000..5c68459575
--- /dev/null
+++ b/extensions/permissions/test/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+TEST_DIRS += [
+ "gtest",
+]
+
+XPCSHELL_TESTS_MANIFESTS += [
+ "unit/xpcshell.ini",
+]
+
+BROWSER_CHROME_MANIFESTS += ["browser.ini"]
diff --git a/extensions/permissions/test/unit/head.js b/extensions/permissions/test/unit/head.js
new file mode 100644
index 0000000000..48ef75c81e
--- /dev/null
+++ b/extensions/permissions/test/unit/head.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+// Helper to step a generator function and catch a StopIteration exception.
+function do_run_generator(generator) {
+ try {
+ generator.next();
+ } catch (e) {
+ do_throw("caught exception " + e, Components.stack.caller);
+ }
+}
+
+// Helper to finish a generator function test.
+function do_finish_generator_test(generator) {
+ executeSoon(function() {
+ generator.return();
+ do_test_finished();
+ });
+}
+
+function do_count_array(all) {
+ return all.length;
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_cleardata.js b/extensions/permissions/test/unit/test_permmanager_cleardata.js
new file mode 100644
index 0000000000..2bd4d11319
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_cleardata.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var pm;
+
+// Create a principal based on the { origin, originAttributes }.
+function createPrincipal(aOrigin, aOriginAttributes) {
+ return Services.scriptSecurityManager.createContentPrincipal(
+ NetUtil.newURI(aOrigin),
+ aOriginAttributes
+ );
+}
+
+function getData(aPattern) {
+ return JSON.stringify(aPattern);
+}
+
+// Use aEntries to create principals, add permissions to them and check that they have them.
+// Then, it is removing origin attributes with the given aData and check if the permissions
+// of principals[i] matches the permission in aResults[i].
+function test(aEntries, aData, aResults) {
+ let principals = [];
+
+ for (const entry of aEntries) {
+ principals.push(createPrincipal(entry.origin, entry.originAttributes));
+ }
+
+ for (const principal of principals) {
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, "test/clear-origin"),
+ pm.UNKNOWN_ACTION
+ );
+ pm.addFromPrincipal(
+ principal,
+ "test/clear-origin",
+ pm.ALLOW_ACTION,
+ pm.EXPIRE_NEVER,
+ 0
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, "test/clear-origin"),
+ pm.ALLOW_ACTION
+ );
+ }
+
+ // `clear-origin-attributes-data` notification is removed from permission
+ // manager
+ pm.removePermissionsWithAttributes(aData);
+
+ var length = aEntries.length;
+ for (let i = 0; i < length; ++i) {
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principals[i], "test/clear-origin"),
+ aResults[i]
+ );
+
+ // Remove allowed actions.
+ if (aResults[i] == pm.ALLOW_ACTION) {
+ pm.removeFromPrincipal(principals[i], "test/clear-origin");
+ }
+ }
+}
+
+function run_test() {
+ do_get_profile();
+
+ pm = Services.perms;
+
+ let entries = [
+ { origin: "http://example.com", originAttributes: {} },
+ {
+ origin: "http://example.com",
+ originAttributes: { inIsolatedMozBrowser: true },
+ },
+ ];
+
+ // In that case, all permissions should be removed.
+ test(entries, getData({}), [
+ pm.UNKNOWN_ACTION,
+ pm.UNKNOWN_ACTION,
+ pm.ALLOW_ACTION,
+ pm.ALLOW_ACTION,
+ ]);
+
+ // In that case, only the permissions related to a browserElement should be removed.
+ // All the other permissions should stay.
+ test(entries, getData({ inIsolatedMozBrowser: true }), [
+ pm.ALLOW_ACTION,
+ pm.UNKNOWN_ACTION,
+ pm.ALLOW_ACTION,
+ pm.ALLOW_ACTION,
+ ]);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_default_pref.js b/extensions/permissions/test/unit/test_permmanager_default_pref.js
new file mode 100644
index 0000000000..b2bae241f0
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_default_pref.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.org"
+ );
+
+ // Check that without a pref the default return value is UNKNOWN.
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.UNKNOWN_ACTION
+ );
+
+ // Check that the default return value changed after setting the pref.
+ Services.prefs.setIntPref(
+ "permissions.default.camera",
+ Services.perms.DENY_ACTION
+ );
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.DENY_ACTION
+ );
+
+ // Check that functions that do not directly return a permission value still
+ // consider the permission as being set to its default.
+ Assert.equal(
+ null,
+ Services.perms.getPermissionObject(principal, "camera", false)
+ );
+
+ // Check that other permissions still return UNKNOWN.
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "geo"),
+ Services.perms.UNKNOWN_ACTION
+ );
+
+ // Check that the default return value changed after changing the pref.
+ Services.prefs.setIntPref(
+ "permissions.default.camera",
+ Services.perms.ALLOW_ACTION
+ );
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.ALLOW_ACTION
+ );
+
+ // Check that the preference is ignored if there is a value.
+ Services.perms.addFromPrincipal(
+ principal,
+ "camera",
+ Services.perms.DENY_ACTION
+ );
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.DENY_ACTION
+ );
+ Assert.ok(
+ Services.perms.getPermissionObject(principal, "camera", false) != null
+ );
+
+ // The preference should be honored again, after resetting the permissions.
+ Services.perms.removeAll();
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.ALLOW_ACTION
+ );
+
+ // Should be UNKNOWN after clearing the pref.
+ Services.prefs.clearUserPref("permissions.default.camera");
+ Assert.equal(
+ Services.perms.testPermissionFromPrincipal(principal, "camera"),
+ Services.perms.UNKNOWN_ACTION
+ );
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_defaults.js b/extensions/permissions/test/unit/test_permmanager_defaults.js
new file mode 100644
index 0000000000..5a817a813b
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_defaults.js
@@ -0,0 +1,489 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The origin we use in most of the tests.
+const TEST_ORIGIN = NetUtil.newURI("http://example.org");
+const TEST_ORIGIN_HTTPS = NetUtil.newURI("https://example.org");
+const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com");
+const TEST_ORIGIN_3 = NetUtil.newURI("https://example2.com:8080");
+const TEST_PERMISSION = "test-permission";
+
+function promiseTimeout(delay) {
+ return new Promise(resolve => {
+ do_timeout(delay, resolve);
+ });
+}
+
+add_task(async function do_test() {
+ // setup a profile.
+ do_get_profile();
+
+ // create a file in the temp directory with the defaults.
+ let file = do_get_tempdir();
+ file.append("test_default_permissions");
+
+ // write our test data to it.
+ let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ ostream.init(file, -1, 0o666, 0);
+ let conv = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(
+ Ci.nsIConverterOutputStream
+ );
+ conv.init(ostream, "UTF-8");
+
+ conv.writeString("# this is a comment\n");
+ conv.writeString("\n"); // a blank line!
+ conv.writeString(
+ "host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n"
+ );
+ conv.writeString(
+ "host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n"
+ );
+ conv.writeString(
+ "origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n"
+ );
+ conv.writeString(
+ "origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "^inBrowser=1\n"
+ );
+ ostream.close();
+
+ // Set the preference used by the permission manager so the file is read.
+ Services.prefs.setCharPref(
+ "permissions.manager.defaultsUrl",
+ "file://" + file.path
+ );
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ let permIsolateUserContext = Services.prefs.getBoolPref(
+ "permissions.isolateBy.userContext"
+ );
+ let permIsolatePrivateBrowsing = Services.prefs.getBoolPref(
+ "permissions.isolateBy.privateBrowsing"
+ );
+
+ let pm = Services.perms;
+
+ // test the default permission was applied.
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ {}
+ );
+ let principalHttps = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN_HTTPS,
+ {}
+ );
+ let principal2 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN_2,
+ {}
+ );
+ let principal3 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN_3,
+ {}
+ );
+
+ let attrs = { inIsolatedMozBrowser: true };
+ let principal4 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ attrs
+ );
+ let principal5 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN_3,
+ attrs
+ );
+
+ attrs = { userContextId: 1 };
+ let principal1UserContext = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ attrs
+ );
+ attrs = { privateBrowsingId: 1 };
+ let principal1PrivateBrowsing = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ attrs
+ );
+ attrs = { firstPartyDomain: "cnn.com" };
+ let principal7 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ attrs
+ );
+ attrs = { userContextId: 1, firstPartyDomain: "cnn.com" };
+ let principal8 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_ORIGIN,
+ attrs
+ );
+
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principalHttps, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION)
+ );
+ // Depending on the prefs there are two scenarios here:
+ // 1. We isolate by private browsing: The permission mgr should
+ // add default permissions for these principals too.
+ // 2. We don't isolate by private browsing: The permission
+ // check will strip the private browsing origin attribute.
+ // In this case the used internally for the lookup is always principal1.
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+
+ // Didn't add
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal5, TEST_PERMISSION)
+ );
+
+ // the permission should exist in the enumerator.
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ findCapabilityViaEnum(TEST_ORIGIN)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ findCapabilityViaEnum(TEST_ORIGIN_3)
+ );
+
+ // but should not have been written to the DB
+ await checkCapabilityViaDB(null);
+
+ // remove all should not throw and the default should remain
+ pm.removeAll();
+
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION)
+ );
+ // Default permission should have also been added for private browsing.
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+ // make sure principals with userContextId use the same / different permissions
+ // depending on pref state
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal1UserContext, TEST_PERMISSION)
+ );
+ // make sure principals with a firstPartyDomain use different permissions
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION)
+ );
+
+ // Asking for this permission to be removed should result in that permission
+ // having UNKNOWN_ACTION
+ pm.removeFromPrincipal(principal, TEST_PERMISSION);
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ // make sure principals with userContextId use the correct permissions
+ // (Should be unknown with and without OA stripping )
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal1UserContext, TEST_PERMISSION)
+ );
+ // If we isolate by private browsing, the permission should still be present
+ // for the private browsing principal.
+ Assert.equal(
+ permIsolatePrivateBrowsing
+ ? Ci.nsIPermissionManager.ALLOW_ACTION
+ : Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+ // and we should have this UNKNOWN_ACTION reflected in the DB
+ await checkCapabilityViaDB(Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ // but the permission should *not* appear in the enumerator.
+ Assert.equal(null, findCapabilityViaEnum());
+
+ // and a subsequent RemoveAll should restore the default
+ pm.removeAll();
+
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ // Make sure default imports work for private browsing after removeAll.
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+ // make sure principals with userContextId share permissions depending on pref state
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal1UserContext, TEST_PERMISSION)
+ );
+ // make sure principals with firstPartyDomain use different permissions
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION)
+ );
+ // and allow it to again be seen in the enumerator.
+ Assert.equal(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum());
+
+ // now explicitly add a permission - this too should override the default.
+ pm.addFromPrincipal(
+ principal,
+ TEST_PERMISSION,
+ Ci.nsIPermissionManager.DENY_ACTION
+ );
+
+ // it should be reflected in a permission check, in the enumerator and the DB
+ Assert.equal(
+ Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ // make sure principals with userContextId use the same / different permissions
+ // depending on pref state
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal1UserContext, TEST_PERMISSION)
+ );
+ // If we isolate by private browsing, we should still have the default perm
+ // for the private browsing principal.
+ Assert.equal(
+ permIsolatePrivateBrowsing
+ ? Ci.nsIPermissionManager.ALLOW_ACTION
+ : Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+ // make sure principals with firstPartyDomain use different permissions
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION)
+ );
+ Assert.equal(Ci.nsIPermissionManager.DENY_ACTION, findCapabilityViaEnum());
+ await checkCapabilityViaDB(Ci.nsIPermissionManager.DENY_ACTION);
+
+ // explicitly add a different permission - in this case we are no longer
+ // replacing the default, but instead replacing the replacement!
+ pm.addFromPrincipal(
+ principal,
+ TEST_PERMISSION,
+ Ci.nsIPermissionManager.PROMPT_ACTION
+ );
+
+ // it should be reflected in a permission check, in the enumerator and the DB
+ Assert.equal(
+ Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ // make sure principals with userContextId use the same / different permissions
+ // depending on pref state
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal1UserContext, TEST_PERMISSION)
+ );
+ // If we isolate by private browsing, we should still have the default perm
+ // for the private browsing principal.
+ Assert.equal(
+ permIsolatePrivateBrowsing
+ ? Ci.nsIPermissionManager.ALLOW_ACTION
+ : Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal1PrivateBrowsing, TEST_PERMISSION)
+ );
+ // make sure principals with firstPartyDomain use different permissions
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION)
+ );
+ Assert.equal(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum());
+ await checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION);
+
+ // --------------------------------------------------------------
+ // check default permissions and removeAllSince work as expected.
+ pm.removeAll(); // ensure only defaults are there.
+
+ // default for both principals is allow.
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)
+ );
+
+ // Add a default override for TEST_ORIGIN_2 - this one should *not* be
+ // restored in removeAllSince()
+ pm.addFromPrincipal(
+ principal2,
+ TEST_PERMISSION,
+ Ci.nsIPermissionManager.DENY_ACTION
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)
+ );
+ await promiseTimeout(20);
+
+ let since = Number(Date.now());
+ await promiseTimeout(20);
+
+ // explicitly add a permission which overrides the default for the first
+ // principal - this one *should* be removed by removeAllSince.
+ pm.addFromPrincipal(
+ principal,
+ TEST_PERMISSION,
+ Ci.nsIPermissionManager.DENY_ACTION
+ );
+ Assert.equal(
+ Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+
+ // do a removeAllSince.
+ pm.removeAllSince(since);
+
+ // the default for the first principal should re-appear as we modified it
+ // later then |since|
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+
+ // but the permission for principal2 should remain as we added that before |since|.
+ Assert.equal(
+ Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)
+ );
+
+ // remove the temp file we created.
+ file.remove(false);
+});
+
+// use an enumerator to find the requested permission. Returns the permission
+// value (ie, the "capability" in nsIPermission parlance) or null if it can't
+// be found.
+function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+ let result = undefined;
+ for (let perm of Services.perms.all) {
+ if (perm.matchesURI(origin, true) && perm.type == type) {
+ if (result !== undefined) {
+ // we've already found one previously - that's bad!
+ do_throw("enumerator found multiple entries");
+ }
+ result = perm.capability;
+ }
+ }
+ return result || null;
+}
+
+// A function to check the DB has the specified capability. As the permission
+// manager uses async DB operations without a completion callback, the
+// distinct possibility exists that our checking of the DB will happen before
+// the permission manager update has completed - so we just retry a few times.
+// Returns a promise.
+function checkCapabilityViaDB(
+ expected,
+ origin = TEST_ORIGIN,
+ type = TEST_PERMISSION
+) {
+ return new Promise(resolve => {
+ let count = 0;
+ let max = 20;
+ let do_check = () => {
+ let got = findCapabilityViaDB(origin, type);
+ if (got == expected) {
+ // the do_check_eq() below will succeed - which is what we want.
+ Assert.equal(got, expected, "The database has the expected value");
+ resolve();
+ return;
+ }
+ // value isn't correct - see if we've retried enough
+ if (count++ == max) {
+ // the do_check_eq() below will fail - which is what we want.
+ Assert.equal(
+ got,
+ expected,
+ "The database wasn't updated with the expected value"
+ );
+ resolve();
+ return;
+ }
+ // we can retry...
+ do_timeout(100, do_check);
+ };
+ do_check();
+ });
+}
+
+// use the DB to find the requested permission. Returns the permission
+// value (ie, the "capability" in nsIPermission parlance) or null if it can't
+// be found.
+function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ origin,
+ {}
+ );
+ let originStr = principal.origin;
+
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("permissions.sqlite");
+
+ let connection = Services.storage.openDatabase(file);
+
+ let query = connection.createStatement(
+ "SELECT permission FROM moz_perms WHERE origin = :origin AND type = :type"
+ );
+ query.bindByName("origin", originStr);
+ query.bindByName("type", type);
+
+ if (!query.executeStep()) {
+ // no row
+ return null;
+ }
+ let result = query.getInt32(0);
+ if (query.executeStep()) {
+ // this is bad - we never expect more than 1 row here.
+ do_throw("More than 1 row found!");
+ }
+ return result;
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_expiration.js b/extensions/permissions/test/unit/test_permmanager_expiration.js
new file mode 100644
index 0000000000..24ff366730
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_expiration.js
@@ -0,0 +1,189 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that permissions with specific expiry times behave as expected.
+var test_generator = do_run_test();
+
+function run_test() {
+ do_test_pending();
+ test_generator.next();
+}
+
+function continue_test() {
+ do_run_generator(test_generator);
+}
+
+function* do_run_test() {
+ let pm = Services.perms;
+ let permURI = NetUtil.newURI("http://example.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ permURI,
+ {}
+ );
+
+ let now = Number(Date.now());
+
+ // add a permission with *now* expiration
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-perm-exp",
+ 1,
+ pm.EXPIRE_TIME,
+ now
+ );
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-session-exp",
+ 1,
+ pm.EXPIRE_SESSION,
+ now
+ );
+
+ // add a permission with future expiration (100 milliseconds)
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-perm-exp2",
+ 1,
+ pm.EXPIRE_TIME,
+ now + 100
+ );
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-session-exp2",
+ 1,
+ pm.EXPIRE_SESSION,
+ now + 100
+ );
+
+ // add a permission with future expiration (1000 seconds)
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-perm-exp3",
+ 1,
+ pm.EXPIRE_TIME,
+ now + 1e6
+ );
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-session-exp3",
+ 1,
+ pm.EXPIRE_SESSION,
+ now + 1e6
+ );
+
+ // add a permission without expiration
+ pm.addFromPrincipal(
+ principal,
+ "test/expiration-perm-nexp",
+ 1,
+ pm.EXPIRE_NEVER,
+ 0
+ );
+
+ // check that the second two haven't expired yet
+ Assert.equal(
+ 1,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp3")
+ );
+ Assert.equal(
+ 1,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp3")
+ );
+ Assert.equal(
+ 1,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-perm-nexp")
+ );
+ Assert.equal(1, pm.getAllWithTypePrefix("test/expiration-perm-exp3").length);
+ Assert.equal(
+ 1,
+ pm.getAllWithTypePrefix("test/expiration-session-exp3").length
+ );
+ Assert.equal(1, pm.getAllWithTypePrefix("test/expiration-perm-nexp").length);
+ Assert.equal(5, pm.getAllForPrincipal(principal).length);
+
+ // ... and the first one has
+ do_timeout(10, continue_test);
+ yield;
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp")
+ );
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp")
+ );
+
+ // ... and that the short-term one will
+ do_timeout(200, continue_test);
+ yield;
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp2")
+ );
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp2")
+ );
+ Assert.equal(0, pm.getAllWithTypePrefix("test/expiration-perm-exp2").length);
+ Assert.equal(
+ 0,
+ pm.getAllWithTypePrefix("test/expiration-session-exp2").length
+ );
+
+ Assert.equal(3, pm.getAllForPrincipal(principal).length);
+
+ // Check that .getPermission returns a matching result
+ Assert.equal(
+ null,
+ pm.getPermissionObject(principal, "test/expiration-perm-exp", false)
+ );
+ Assert.equal(
+ null,
+ pm.getPermissionObject(principal, "test/expiration-session-exp", false)
+ );
+ Assert.equal(
+ null,
+ pm.getPermissionObject(principal, "test/expiration-perm-exp2", false)
+ );
+ Assert.equal(
+ null,
+ pm.getPermissionObject(principal, "test/expiration-session-exp2", false)
+ );
+
+ // Add a persistent permission for private browsing
+ let principalPB = Services.scriptSecurityManager.createContentPrincipal(
+ permURI,
+ { privateBrowsingId: 1 }
+ );
+ pm.addFromPrincipal(
+ principalPB,
+ "test/expiration-session-pb",
+ pm.ALLOW_ACTION
+ );
+
+ // The permission should be set to session expiry
+ let perm = pm.getPermissionObject(
+ principalPB,
+ "test/expiration-session-pb",
+ true
+ );
+ Assert.equal(perm.expireType, pm.EXPIRE_SESSION);
+
+ // Add a persistent permission for private browsing using
+ // addFromPrincipalAndPersistInPrivateBrowsing
+ pm.addFromPrincipalAndPersistInPrivateBrowsing(
+ principalPB,
+ "test/expiration-session-pb",
+ pm.ALLOW_ACTION
+ );
+
+ // The permission should be set to never expire
+ perm = pm.getPermissionObject(
+ principalPB,
+ "test/expiration-session-pb",
+ true
+ );
+ Assert.equal(perm.expireType, pm.EXPIRE_NEVER);
+
+ do_finish_generator_test(test_generator);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_getAllByTypeSince.js b/extensions/permissions/test/unit/test_permmanager_getAllByTypeSince.js
new file mode 100644
index 0000000000..0d328d0a12
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_getAllByTypeSince.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function check_enumerator(prefix, from, permissions) {
+ let pm = Services.perms;
+
+ let array = pm.getAllByTypeSince(prefix, from);
+ Assert.equal(array.length, permissions.length);
+ for (let [principal, type, capability] of permissions) {
+ let perm = array.find(p => p.principal.equals(principal));
+ Assert.ok(perm != null);
+ Assert.equal(perm.type, type);
+ Assert.equal(perm.capability, capability);
+ Assert.equal(perm.expireType, pm.EXPIRE_NEVER);
+ }
+}
+
+add_task(async function test() {
+ let pm = Services.perms;
+
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ );
+ let subPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://sub.example.com"
+ );
+
+ check_enumerator("test/", 0, []);
+
+ pm.addFromPrincipal(principal, "test/getAllByTypeSince", pm.ALLOW_ACTION);
+
+ // These shouldn't show up anywhere, the name doesn't match.
+ pm.addFromPrincipal(
+ subPrincipal,
+ "other-test/getAllByTypeSince",
+ pm.PROMPT_ACTION
+ );
+ pm.addFromPrincipal(principal, "test/getAllByTypeSince1", pm.PROMPT_ACTION);
+
+ check_enumerator("test/getAllByTypeSince", 0, [
+ [principal, "test/getAllByTypeSince", pm.ALLOW_ACTION],
+ ]);
+
+ // Add some time in between taking the snapshot of the timestamp
+ // to avoid flakyness.
+ await new Promise(c => do_timeout(100, c));
+ let timestamp = Date.now();
+ await new Promise(c => do_timeout(100, c));
+
+ pm.addFromPrincipal(subPrincipal, "test/getAllByTypeSince", pm.DENY_ACTION);
+
+ check_enumerator("test/getAllByTypeSince", 0, [
+ [subPrincipal, "test/getAllByTypeSince", pm.DENY_ACTION],
+ [principal, "test/getAllByTypeSince", pm.ALLOW_ACTION],
+ ]);
+
+ check_enumerator("test/getAllByTypeSince", timestamp, [
+ [subPrincipal, "test/getAllByTypeSince", pm.DENY_ACTION],
+ ]);
+
+ // check that UNKNOWN_ACTION permissions are ignored
+ pm.addFromPrincipal(
+ subPrincipal,
+ "test/getAllByTypeSince",
+ pm.UNKNOWN_ACTION
+ );
+
+ check_enumerator("test/getAllByTypeSince", 0, [
+ [principal, "test/getAllByTypeSince", pm.ALLOW_ACTION],
+ ]);
+
+ // check that permission removals are reflected
+ pm.removeFromPrincipal(principal, "test/getAllByTypeSince");
+ check_enumerator("test/", 0, []);
+
+ pm.removeAll();
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_getAllByTypes.js b/extensions/permissions/test/unit/test_permmanager_getAllByTypes.js
new file mode 100644
index 0000000000..f4c5251065
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_getAllByTypes.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function check_enumerator(permissionTypes, expectedPermissions) {
+ const permissions = Services.perms.getAllByTypes(permissionTypes);
+
+ Assert.equal(
+ permissions.length,
+ expectedPermissions.length,
+ `getAllByTypes returned the expected number of permissions for ${JSON.stringify(
+ permissionTypes
+ )}`
+ );
+
+ for (const perm of permissions) {
+ Assert.ok(perm != null);
+
+ // For some reason, the order in which we get the permissions doesn't seem to be
+ // stable when running the test with --verify. As a result, we need to retrieve the
+ // expected permission for the origin and type.
+ const expectedPermission = expectedPermissions.find(
+ ([expectedPrincipal, expectedType]) =>
+ perm.principal.equals(expectedPrincipal) && perm.type === expectedType
+ );
+
+ Assert.ok(expectedPermission !== null, "Found the expected permission");
+ Assert.equal(perm.capability, expectedPermission[2]);
+ Assert.equal(perm.expireType, Services.perms.EXPIRE_NEVER);
+ }
+}
+
+function run_test() {
+ let pm = Services.perms;
+
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ );
+ let subPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://sub.example.com"
+ );
+
+ const PERM_TYPE_1 = "test/getallbytypes_1";
+ const PERM_TYPE_2 = "test/getallbytypes_2";
+
+ info("check default state");
+ check_enumerator([], []);
+ check_enumerator([PERM_TYPE_1], []);
+ check_enumerator([PERM_TYPE_2], []);
+ check_enumerator([PERM_TYPE_1, PERM_TYPE_2], []);
+
+ info("check that expected permissions are retrieved");
+ pm.addFromPrincipal(principal, PERM_TYPE_1, pm.ALLOW_ACTION);
+ pm.addFromPrincipal(
+ subPrincipal,
+ "other-test/getallbytypes_1",
+ pm.PROMPT_ACTION
+ );
+ check_enumerator([PERM_TYPE_1], [[principal, PERM_TYPE_1, pm.ALLOW_ACTION]]);
+ check_enumerator(
+ [PERM_TYPE_1, PERM_TYPE_2],
+ [[principal, PERM_TYPE_1, pm.ALLOW_ACTION]]
+ );
+ check_enumerator([], []);
+ check_enumerator([PERM_TYPE_2], []);
+
+ pm.addFromPrincipal(subPrincipal, PERM_TYPE_1, pm.PROMPT_ACTION);
+ check_enumerator(
+ [PERM_TYPE_1],
+ [
+ [subPrincipal, PERM_TYPE_1, pm.PROMPT_ACTION],
+ [principal, PERM_TYPE_1, pm.ALLOW_ACTION],
+ ]
+ );
+ check_enumerator(
+ [PERM_TYPE_1, PERM_TYPE_2],
+ [
+ [subPrincipal, PERM_TYPE_1, pm.PROMPT_ACTION],
+ [principal, PERM_TYPE_1, pm.ALLOW_ACTION],
+ ]
+ );
+ check_enumerator([], []);
+ check_enumerator([PERM_TYPE_2], []);
+
+ pm.addFromPrincipal(principal, PERM_TYPE_2, pm.PROMPT_ACTION);
+ check_enumerator(
+ [PERM_TYPE_1, PERM_TYPE_2],
+ [
+ [subPrincipal, PERM_TYPE_1, pm.PROMPT_ACTION],
+ [principal, PERM_TYPE_1, pm.ALLOW_ACTION],
+ [principal, PERM_TYPE_2, pm.PROMPT_ACTION],
+ ]
+ );
+ check_enumerator([], []);
+ check_enumerator([PERM_TYPE_2], [[principal, PERM_TYPE_2, pm.PROMPT_ACTION]]);
+
+ info("check that UNKNOWN_ACTION permissions are ignored");
+ pm.addFromPrincipal(subPrincipal, PERM_TYPE_2, pm.UNKNOWN_ACTION);
+ check_enumerator([PERM_TYPE_2], [[principal, PERM_TYPE_2, pm.PROMPT_ACTION]]);
+
+ info("check that permission updates are reflected");
+ pm.addFromPrincipal(subPrincipal, PERM_TYPE_2, pm.PROMPT_ACTION);
+ check_enumerator(
+ [PERM_TYPE_2],
+ [
+ [subPrincipal, PERM_TYPE_2, pm.PROMPT_ACTION],
+ [principal, PERM_TYPE_2, pm.PROMPT_ACTION],
+ ]
+ );
+
+ info("check that permission removals are reflected");
+ pm.removeFromPrincipal(principal, PERM_TYPE_1);
+ check_enumerator(
+ [PERM_TYPE_1],
+ [[subPrincipal, PERM_TYPE_1, pm.PROMPT_ACTION]]
+ );
+
+ pm.removeAll();
+ check_enumerator([], []);
+ check_enumerator([PERM_TYPE_1], []);
+ check_enumerator([PERM_TYPE_2], []);
+ check_enumerator([PERM_TYPE_1, PERM_TYPE_2], []);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_getAllForPrincipal.js b/extensions/permissions/test/unit/test_permmanager_getAllForPrincipal.js
new file mode 100644
index 0000000000..b8cfe26549
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_getAllForPrincipal.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function check_enumerator(principal, permissions) {
+ let perms = Services.perms.getAllForPrincipal(principal);
+ for (let [type, capability] of permissions) {
+ let perm = perms.shift();
+ Assert.ok(perm != null);
+ Assert.equal(perm.type, type);
+ Assert.equal(perm.capability, capability);
+ Assert.equal(perm.expireType, Services.perms.EXPIRE_NEVER);
+ }
+ Assert.ok(!perms.length);
+}
+
+function run_test() {
+ let pm = Services.perms;
+
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ );
+ let subPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://sub.example.com"
+ );
+
+ check_enumerator(principal, []);
+
+ pm.addFromPrincipal(principal, "test/getallforuri", pm.ALLOW_ACTION);
+ check_enumerator(principal, [["test/getallforuri", pm.ALLOW_ACTION]]);
+
+ // check that uris are matched exactly
+ check_enumerator(subPrincipal, []);
+
+ pm.addFromPrincipal(subPrincipal, "test/getallforuri", pm.PROMPT_ACTION);
+ pm.addFromPrincipal(subPrincipal, "test/getallforuri2", pm.DENY_ACTION);
+
+ check_enumerator(subPrincipal, [
+ ["test/getallforuri", pm.PROMPT_ACTION],
+ ["test/getallforuri2", pm.DENY_ACTION],
+ ]);
+
+ // check that the original uri list has not changed
+ check_enumerator(principal, [["test/getallforuri", pm.ALLOW_ACTION]]);
+
+ // check that UNKNOWN_ACTION permissions are ignored
+ pm.addFromPrincipal(principal, "test/getallforuri2", pm.UNKNOWN_ACTION);
+ pm.addFromPrincipal(principal, "test/getallforuri3", pm.DENY_ACTION);
+
+ check_enumerator(principal, [
+ ["test/getallforuri", pm.ALLOW_ACTION],
+ ["test/getallforuri3", pm.DENY_ACTION],
+ ]);
+
+ // check that permission updates are reflected
+ pm.addFromPrincipal(principal, "test/getallforuri", pm.PROMPT_ACTION);
+
+ check_enumerator(principal, [
+ ["test/getallforuri", pm.PROMPT_ACTION],
+ ["test/getallforuri3", pm.DENY_ACTION],
+ ]);
+
+ // check that permission removals are reflected
+ pm.removeFromPrincipal(principal, "test/getallforuri");
+
+ check_enumerator(principal, [["test/getallforuri3", pm.DENY_ACTION]]);
+
+ pm.removeAll();
+ check_enumerator(principal, []);
+ check_enumerator(subPrincipal, []);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_getAllWithTypePrefix.js b/extensions/permissions/test/unit/test_permmanager_getAllWithTypePrefix.js
new file mode 100644
index 0000000000..b6a99fbdbe
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_getAllWithTypePrefix.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function check_enumerator(prefix, permissions) {
+ let pm = Services.perms;
+
+ let array = pm.getAllWithTypePrefix(prefix);
+ for (let [principal, type, capability] of permissions) {
+ let perm = array.shift();
+ Assert.ok(perm != null);
+ Assert.ok(perm.principal.equals(principal));
+ Assert.equal(perm.type, type);
+ Assert.equal(perm.capability, capability);
+ Assert.equal(perm.expireType, pm.EXPIRE_NEVER);
+ }
+ Assert.equal(array.length, 0);
+}
+
+function run_test() {
+ let pm = Services.perms;
+
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ );
+ let subPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://sub.example.com"
+ );
+
+ check_enumerator("test/", []);
+
+ pm.addFromPrincipal(principal, "test/getallwithtypeprefix", pm.ALLOW_ACTION);
+ pm.addFromPrincipal(
+ subPrincipal,
+ "other-test/getallwithtypeprefix",
+ pm.PROMPT_ACTION
+ );
+ check_enumerator("test/", [
+ [principal, "test/getallwithtypeprefix", pm.ALLOW_ACTION],
+ ]);
+
+ pm.addFromPrincipal(
+ subPrincipal,
+ "test/getallwithtypeprefix",
+ pm.PROMPT_ACTION
+ );
+ check_enumerator("test/", [
+ [subPrincipal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ [principal, "test/getallwithtypeprefix", pm.ALLOW_ACTION],
+ ]);
+
+ check_enumerator("test/getallwithtypeprefix", [
+ [subPrincipal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ [principal, "test/getallwithtypeprefix", pm.ALLOW_ACTION],
+ ]);
+
+ // check that UNKNOWN_ACTION permissions are ignored
+ pm.addFromPrincipal(
+ principal,
+ "test/getallwithtypeprefix2",
+ pm.UNKNOWN_ACTION
+ );
+ check_enumerator("test/", [
+ [subPrincipal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ [principal, "test/getallwithtypeprefix", pm.ALLOW_ACTION],
+ ]);
+
+ // check that permission updates are reflected
+ pm.addFromPrincipal(principal, "test/getallwithtypeprefix", pm.PROMPT_ACTION);
+ check_enumerator("test/", [
+ [subPrincipal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ [principal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ ]);
+
+ // check that permission removals are reflected
+ pm.removeFromPrincipal(principal, "test/getallwithtypeprefix");
+ check_enumerator("test/", [
+ [subPrincipal, "test/getallwithtypeprefix", pm.PROMPT_ACTION],
+ ]);
+
+ pm.removeAll();
+ check_enumerator("test/", []);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_getPermissionObject.js b/extensions/permissions/test/unit/test_permmanager_getPermissionObject.js
new file mode 100644
index 0000000000..78ef9ab08a
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_getPermissionObject.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getPrincipalFromURI(aURI) {
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI(aURI);
+ return ssm.createContentPrincipal(uri, {});
+}
+
+function getSystemPrincipal() {
+ return Services.scriptSecurityManager.getSystemPrincipal();
+}
+
+function run_test() {
+ var pm = Services.perms;
+
+ Assert.equal(
+ null,
+ pm.getPermissionObject(getSystemPrincipal(), "test/pobject", false)
+ );
+
+ let principal = getPrincipalFromURI("http://example.com");
+ let subPrincipal = getPrincipalFromURI("http://sub.example.com");
+ let subSubPrincipal = getPrincipalFromURI("http://sub.sub.example.com");
+
+ Assert.equal(null, pm.getPermissionObject(principal, "test/pobject", false));
+ Assert.equal(null, pm.getPermissionObject(principal, "test/pobject", true));
+
+ pm.addFromPrincipal(principal, "test/pobject", pm.ALLOW_ACTION);
+ var rootPerm = pm.getPermissionObject(principal, "test/pobject", false);
+ Assert.ok(rootPerm != null);
+ Assert.equal(rootPerm.principal.origin, "http://example.com");
+ Assert.equal(rootPerm.type, "test/pobject");
+ Assert.equal(rootPerm.capability, pm.ALLOW_ACTION);
+ Assert.equal(rootPerm.expireType, pm.EXPIRE_NEVER);
+
+ Assert.ok(rootPerm != null);
+ Assert.equal(rootPerm.principal.origin, "http://example.com");
+
+ var subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
+ Assert.equal(null, subPerm);
+ subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
+ Assert.ok(subPerm != null);
+ Assert.equal(subPerm.principal.origin, "http://example.com");
+ Assert.equal(subPerm.type, "test/pobject");
+ Assert.equal(subPerm.capability, pm.ALLOW_ACTION);
+
+ subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true);
+ Assert.equal(null, subPerm);
+ subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
+ Assert.ok(subPerm != null);
+ Assert.equal(subPerm.principal.origin, "http://example.com");
+
+ pm.addFromPrincipal(
+ principal,
+ "test/pobject",
+ pm.DENY_ACTION,
+ pm.EXPIRE_SESSION
+ );
+
+ // make sure permission objects are not dynamic
+ Assert.equal(rootPerm.capability, pm.ALLOW_ACTION);
+
+ // but do update on change
+ rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
+ Assert.equal(rootPerm.capability, pm.DENY_ACTION);
+ Assert.equal(rootPerm.expireType, pm.EXPIRE_SESSION);
+
+ subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
+ Assert.equal(subPerm.principal.origin, "http://example.com");
+ Assert.equal(subPerm.capability, pm.DENY_ACTION);
+ Assert.equal(subPerm.expireType, pm.EXPIRE_SESSION);
+
+ pm.addFromPrincipal(subPrincipal, "test/pobject", pm.PROMPT_ACTION);
+ rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
+ Assert.equal(rootPerm.principal.origin, "http://example.com");
+ Assert.equal(rootPerm.capability, pm.DENY_ACTION);
+
+ subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
+ Assert.equal(subPerm.principal.origin, "http://sub.example.com");
+ Assert.equal(subPerm.capability, pm.PROMPT_ACTION);
+
+ subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
+ Assert.equal(subPerm.principal.origin, "http://sub.example.com");
+ Assert.equal(subPerm.capability, pm.PROMPT_ACTION);
+
+ subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true);
+ Assert.equal(null, subPerm);
+
+ subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
+ Assert.equal(subPerm.principal.origin, "http://sub.example.com");
+ Assert.equal(subPerm.capability, pm.PROMPT_ACTION);
+
+ pm.removeFromPrincipal(principal, "test/pobject");
+
+ rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
+ Assert.equal(null, rootPerm);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_idn.js b/extensions/permissions/test/unit/test_permmanager_idn.js
new file mode 100644
index 0000000000..5719131245
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_idn.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getPrincipalFromDomain(aDomain) {
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI("http://" + aDomain);
+ return ssm.createContentPrincipal(uri, {});
+}
+
+function run_test() {
+ let pm = Services.perms;
+ let perm = "test-idn";
+
+ // We create three principal linked to IDN.
+ // One with just a domain, one with a subdomain and one with the TLD
+ // containing a UTF-8 character.
+ let mainDomainPrincipal = getPrincipalFromDomain("fôû.com");
+ let subDomainPrincipal = getPrincipalFromDomain("fôô.bàr.com");
+ let tldPrincipal = getPrincipalFromDomain("fôû.bàr.côm");
+
+ // We add those to the permission manager.
+ pm.addFromPrincipal(mainDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
+ pm.addFromPrincipal(subDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
+ pm.addFromPrincipal(tldPrincipal, perm, pm.ALLOW_ACTION, 0, 0);
+
+ // They should obviously be there now..
+ Assert.equal(
+ pm.testPermissionFromPrincipal(mainDomainPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subDomainPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(tldPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+
+ // We do the same thing with the puny-encoded versions of the IDN.
+ let punyMainDomainPrincipal = getPrincipalFromDomain("xn--f-xgav.com");
+ let punySubDomainPrincipal = getPrincipalFromDomain(
+ "xn--f-xgaa.xn--br-jia.com"
+ );
+ let punyTldPrincipal = getPrincipalFromDomain(
+ "xn--f-xgav.xn--br-jia.xn--cm-8ja"
+ );
+
+ // Those principals should have the permission granted too.
+ Assert.equal(
+ pm.testPermissionFromPrincipal(punyMainDomainPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(punySubDomainPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(punyTldPrincipal, perm),
+ pm.ALLOW_ACTION
+ );
+
+ // However, those two principals shouldn't be allowed because they are like
+ // the IDN but without the UT8-8 characters.
+ let witnessPrincipal = getPrincipalFromDomain("foo.com");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, perm),
+ pm.UNKNOWN_ACTION
+ );
+ witnessPrincipal = getPrincipalFromDomain("foo.bar.com");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, perm),
+ pm.UNKNOWN_ACTION
+ );
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_ipc.js b/extensions/permissions/test/unit/test_permmanager_ipc.js
new file mode 100644
index 0000000000..0247ef9b72
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_ipc.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+add_task(async function test_permissions_sent_over_ipc_on_bloburl() {
+ const ssm = Services.scriptSecurityManager;
+ const pm = Services.perms;
+
+ // setup a profile.
+ do_get_profile();
+
+ async function assertExpectedContentPage(contentPage) {
+ const [processType, remoteType, principalSpec] = await page.spawn(
+ [],
+ async () => {
+ return [
+ Services.appinfo.processType,
+ Services.appinfo.remoteType,
+ this.content.document.nodePrincipal.spec,
+ ];
+ }
+ );
+
+ equal(
+ processType,
+ Services.appinfo.PROCESS_TYPE_CONTENT,
+ "Got a content process"
+ );
+ equal(remoteType, "file", "Got a file child process");
+ equal(principalSpec, principal.spec, "Got the expected document principal");
+ }
+
+ function getChildProcessID(contentPage) {
+ return contentPage.spawn([], () => Services.appinfo.processID);
+ }
+
+ async function assertHasAllowedPermission(contentPage, perm) {
+ const isPermissionAllowed = await contentPage.spawn(
+ [perm],
+ permName =>
+ Services.perms.getPermissionObject(
+ this.content.document.nodePrincipal,
+ permName,
+ true
+ )?.capability === Services.perms.ALLOW_ACTION
+ );
+ ok(isPermissionAllowed, `Permission "${perm}" allowed as expected`);
+ }
+
+ let file = do_get_file(".", true);
+ let fileURI = Services.io.newFileURI(file);
+ const principal = ssm.createContentPrincipal(fileURI, {});
+ info(`Add a test permission to the document principal: ${principal.spec}`);
+ pm.addFromPrincipal(principal, "test/perm", pm.ALLOW_ACTION);
+
+ info("Test expected permission is propagated into the child process");
+ let page = await ExtensionTestUtils.loadContentPage(fileURI.spec);
+ const childID1 = await getChildProcessID(page);
+ await assertExpectedContentPage(page);
+ await assertHasAllowedPermission(page, "test/perm");
+ await page.close();
+
+ // Ensure this blob url does not prevent permissions to be propagated
+ // to a new child process.
+ info("Create a blob url for a non http/https principal");
+ const blob = new Blob();
+ const blobURL = URL.createObjectURL(blob);
+ ok(blobURL, "Got a blob URL");
+
+ info("Test expected permission is still propagated");
+ page = await ExtensionTestUtils.loadContentPage(fileURI.spec);
+ const childID2 = await getChildProcessID(page);
+ await assertExpectedContentPage(page);
+ Assert.notEqual(childID1, childID2, "Got a new child process as expected");
+ await assertHasAllowedPermission(page, "test/perm");
+ await page.close();
+
+ URL.revokeObjectURL(blobURL);
+
+ page = await ExtensionTestUtils.loadContentPage(fileURI.spec);
+ const childID3 = await getChildProcessID(page);
+ await assertExpectedContentPage(page);
+ Assert.notEqual(childID2, childID3, "Got a new child process as expected");
+ await assertHasAllowedPermission(page, "test/perm");
+ await page.close();
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_load_invalid_entries.js b/extensions/permissions/test/unit/test_permmanager_load_invalid_entries.js
new file mode 100644
index 0000000000..decbce81a0
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_load_invalid_entries.js
@@ -0,0 +1,264 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+var DEBUG_TEST = false;
+
+function run_test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ // Setup a profile directory.
+ var dir = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ // Get the db file.
+ var file = dir.clone();
+ file.append("permissions.sqlite");
+
+ var storage = Services.storage;
+
+ // Create database.
+ var connection = storage.openDatabase(file);
+ // The file should now exist.
+ Assert.ok(file.exists());
+
+ connection.schemaVersion = 3;
+ connection.executeSimpleSQL("DROP TABLE moz_hosts");
+ connection.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ // Now we can inject garbadge in the database.
+ var garbadge = [
+ // Regular entry.
+ {
+ host: "42",
+ type: "0",
+ permission: 1,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // Special values in host (some being invalid).
+ {
+ host: "scheme:file",
+ type: "1",
+ permission: 0,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "192.168.0.1",
+ type: "2",
+ permission: 0,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+ type: "3",
+ permission: 0,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "::1",
+ type: "4",
+ permission: 0,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // Permission is UNKNOWN_ACTION.
+ {
+ host: "42",
+ type: "5",
+ permission: Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // Permission is out of range.
+ {
+ host: "42",
+ type: "6",
+ permission: 100,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "42",
+ type: "7",
+ permission: -100,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // ExpireType is out of range.
+ {
+ host: "42",
+ type: "8",
+ permission: 1,
+ expireType: -100,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "42",
+ type: "9",
+ permission: 1,
+ expireType: 100,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // ExpireTime is at 0 with ExpireType = Time.
+ {
+ host: "42",
+ type: "10",
+ permission: 1,
+ expireType: Ci.nsIPermissionManager.EXPIRE_TIME,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+
+ // ExpireTime has a value with ExpireType != Time
+ {
+ host: "42",
+ type: "11",
+ permission: 1,
+ expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
+ expireTime: 1000,
+ isInBrowserElement: 0,
+ },
+ {
+ host: "42",
+ type: "12",
+ permission: 1,
+ expireType: Ci.nsIPermissionManager.EXPIRE_NEVER,
+ expireTime: 1000,
+ isInBrowserElement: 0,
+ },
+
+ // ExpireTime is negative.
+ {
+ host: "42",
+ type: "13",
+ permission: 1,
+ expireType: Ci.nsIPermissionManager.EXPIRE_TIME,
+ expireTime: -1,
+ isInBrowserElement: 0,
+ },
+
+ // IsInBrowserElement is negative or higher than 1.
+ {
+ host: "42",
+ type: "15",
+ permission: 1,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: -1,
+ },
+ {
+ host: "42",
+ type: "16",
+ permission: 1,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 10,
+ },
+
+ // This insertion should be the last one. It is used to make sure we always
+ // load it regardless of the previous entries validities.
+ {
+ host: "example.org",
+ type: "test-load-invalid-entries",
+ permission: Ci.nsIPermissionManager.ALLOW_ACTION,
+ expireType: 0,
+ expireTime: 0,
+ isInBrowserElement: 0,
+ },
+ ];
+
+ for (var i = 0; i < garbadge.length; ++i) {
+ if (DEBUG_TEST) {
+ dump("\n value #" + i + "\n\n");
+ }
+ var data = garbadge[i];
+ connection.executeSimpleSQL(
+ "INSERT INTO moz_hosts " +
+ " (id, host, type, permission, expireType, expireTime, isInBrowserElement, appId) " +
+ "VALUES (" +
+ i +
+ ", '" +
+ data.host +
+ "', '" +
+ data.type +
+ "', " +
+ data.permission +
+ ", " +
+ data.expireType +
+ ", " +
+ data.expireTime +
+ ", " +
+ data.isInBrowserElement +
+ ", 0)"
+ );
+ }
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Let's do something in order to be sure the DB is read.
+ Assert.greater(pm.all.length, 0);
+
+ // The schema should be upgraded to 11, and a 'modificationTime' column should
+ // exist with all records having a value of 0.
+ Assert.equal(connection.schemaVersion, 12);
+
+ let select = connection.createStatement(
+ "SELECT modificationTime FROM moz_perms"
+ );
+ let numMigrated = 0;
+ while (select.executeStep()) {
+ let thisModTime = select.getInt64(0);
+ Assert.ok(
+ thisModTime > 0,
+ "new modifiedTime field is correct (but it's not 0!)"
+ );
+ numMigrated += 1;
+ }
+ // check we found at least 1 record that was migrated.
+ Assert.greater(
+ numMigrated,
+ 0,
+ "we found at least 1 record that was migrated"
+ );
+
+ // This permission should always be there.
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI("http://example.org");
+ let principal = ssm.createContentPrincipal(uri, {});
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, "test-load-invalid-entries"),
+ Ci.nsIPermissionManager.ALLOW_ACTION
+ );
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_local_files.js b/extensions/permissions/test/unit/test_permmanager_local_files.js
new file mode 100644
index 0000000000..389eb77916
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_local_files.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that permissions work for file:// URIs (aka local files).
+
+function getPrincipalFromURIString(uriStr) {
+ let uri = NetUtil.newURI(uriStr);
+ return Services.scriptSecurityManager.createContentPrincipal(uri, {});
+}
+
+function run_test() {
+ let pm = Services.perms;
+
+ // If we add a permission to a file:// URI, the test should return true.
+ let principal = getPrincipalFromURIString("file:///foo/bar");
+ pm.addFromPrincipal(principal, "test/local-files", pm.ALLOW_ACTION, 0, 0);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, "test/local-files"),
+ pm.ALLOW_ACTION
+ );
+
+ // Another file:// URI should have the same permission.
+ let witnessPrincipal = getPrincipalFromURIString("file:///bar/foo");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Giving "file:///" a permission shouldn't give it to all file:// URIs.
+ let rootPrincipal = getPrincipalFromURIString("file:///");
+ pm.addFromPrincipal(rootPrincipal, "test/local-files", pm.ALLOW_ACTION, 0, 0);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Giving "file://" a permission shouldn't give it to all file:// URIs.
+ let schemeRootPrincipal = getPrincipalFromURIString("file://");
+ pm.addFromPrincipal(
+ schemeRootPrincipal,
+ "test/local-files",
+ pm.ALLOW_ACTION,
+ 0,
+ 0
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Giving 'node' a permission shouldn't give it to its 'children'.
+ let fileInDirPrincipal = getPrincipalFromURIString(
+ "file:///foo/bar/foobar.txt"
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Revert "file:///foo/bar" permission and check that it has been correctly taken into account.
+ pm.removeFromPrincipal(principal, "test/local-files");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"),
+ pm.UNKNOWN_ACTION
+ );
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_matches.js b/extensions/permissions/test/unit/test_permmanager_matches.js
new file mode 100644
index 0000000000..937a1ce750
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_matches.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var attrs;
+
+function matches_always(perm, principals) {
+ principals.forEach(principal => {
+ Assert.ok(
+ perm.matches(principal, true),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ Assert.ok(
+ perm.matches(principal, false),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ });
+}
+
+function matches_weak(perm, principals) {
+ principals.forEach(principal => {
+ Assert.ok(
+ !perm.matches(principal, true),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ Assert.ok(
+ perm.matches(principal, false),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ });
+}
+
+function matches_never(perm, principals) {
+ principals.forEach(principal => {
+ Assert.ok(
+ !perm.matches(principal, true),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ Assert.ok(
+ !perm.matches(principal, false),
+ "perm: " + perm.principal.origin + ", princ: " + principal.origin
+ );
+ });
+}
+
+function run_test() {
+ // initialize the permission manager service
+ let pm = Services.perms;
+
+ let secMan = Services.scriptSecurityManager;
+
+ // Add some permissions
+ let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag");
+ let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir");
+ let uri2 = NetUtil.newURI("http://google.org/");
+ let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory");
+ let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout");
+ let uri5 = NetUtil.newURI("http://google.com:8096/");
+
+ let uri0_n = secMan.createContentPrincipal(uri0, {});
+ let uri1_n = secMan.createContentPrincipal(uri1, {});
+ let uri2_n = secMan.createContentPrincipal(uri2, {});
+ let uri3_n = secMan.createContentPrincipal(uri3, {});
+ let uri4_n = secMan.createContentPrincipal(uri4, {});
+ let uri5_n = secMan.createContentPrincipal(uri5, {});
+
+ attrs = { inIsolatedMozBrowser: true };
+ let uri0_y_ = secMan.createContentPrincipal(uri0, attrs);
+ let uri1_y_ = secMan.createContentPrincipal(uri1, attrs);
+ let uri2_y_ = secMan.createContentPrincipal(uri2, attrs);
+ let uri3_y_ = secMan.createContentPrincipal(uri3, attrs);
+ let uri4_y_ = secMan.createContentPrincipal(uri4, attrs);
+ let uri5_y_ = secMan.createContentPrincipal(uri5, attrs);
+
+ attrs = { userContextId: 1 };
+ let uri0_1 = secMan.createContentPrincipal(uri0, attrs);
+ let uri1_1 = secMan.createContentPrincipal(uri1, attrs);
+ let uri2_1 = secMan.createContentPrincipal(uri2, attrs);
+ let uri3_1 = secMan.createContentPrincipal(uri3, attrs);
+ let uri4_1 = secMan.createContentPrincipal(uri4, attrs);
+ let uri5_1 = secMan.createContentPrincipal(uri5, attrs);
+
+ attrs = { firstPartyDomain: "cnn.com" };
+ let uri0_cnn = secMan.createContentPrincipal(uri0, attrs);
+ let uri1_cnn = secMan.createContentPrincipal(uri1, attrs);
+ let uri2_cnn = secMan.createContentPrincipal(uri2, attrs);
+ let uri3_cnn = secMan.createContentPrincipal(uri3, attrs);
+ let uri4_cnn = secMan.createContentPrincipal(uri4, attrs);
+ let uri5_cnn = secMan.createContentPrincipal(uri5, attrs);
+
+ pm.addFromPrincipal(uri0_n, "test/matches", pm.ALLOW_ACTION);
+ let perm_n = pm.getPermissionObject(uri0_n, "test/matches", true);
+ pm.addFromPrincipal(uri0_y_, "test/matches", pm.ALLOW_ACTION);
+ let perm_y_ = pm.getPermissionObject(uri0_y_, "test/matches", true);
+ pm.addFromPrincipal(uri0_1, "test/matches", pm.ALLOW_ACTION);
+ let perm_1 = pm.getPermissionObject(uri0_n, "test/matches", true);
+ pm.addFromPrincipal(uri0_cnn, "test/matches", pm.ALLOW_ACTION);
+ let perm_cnn = pm.getPermissionObject(uri0_n, "test/matches", true);
+
+ matches_always(perm_n, [uri0_n, uri0_1]);
+ matches_weak(perm_n, [uri1_n, uri1_1]);
+ matches_never(perm_n, [
+ uri2_n,
+ uri3_n,
+ uri4_n,
+ uri5_n,
+ uri0_y_,
+ uri1_y_,
+ uri2_y_,
+ uri3_y_,
+ uri4_y_,
+ uri5_y_,
+ uri2_1,
+ uri3_1,
+ uri4_1,
+ uri5_1,
+ uri0_cnn,
+ uri1_cnn,
+ uri2_cnn,
+ uri3_cnn,
+ uri4_cnn,
+ uri5_cnn,
+ ]);
+
+ matches_always(perm_y_, [uri0_y_]);
+ matches_weak(perm_y_, [uri1_y_]);
+ matches_never(perm_y_, [
+ uri2_y_,
+ uri3_y_,
+ uri4_y_,
+ uri5_y_,
+ uri0_n,
+ uri1_n,
+ uri2_n,
+ uri3_n,
+ uri4_n,
+ uri5_n,
+ uri0_1,
+ uri1_1,
+ uri2_1,
+ uri3_1,
+ uri4_1,
+ uri5_1,
+ uri0_cnn,
+ uri1_cnn,
+ uri2_cnn,
+ uri3_cnn,
+ uri4_cnn,
+ uri5_cnn,
+ ]);
+
+ matches_always(perm_1, [uri0_n, uri0_1]);
+ matches_weak(perm_1, [uri1_n, uri1_1]);
+ matches_never(perm_1, [
+ uri2_n,
+ uri3_n,
+ uri4_n,
+ uri5_n,
+ uri0_y_,
+ uri1_y_,
+ uri2_y_,
+ uri3_y_,
+ uri4_y_,
+ uri5_y_,
+ uri2_1,
+ uri3_1,
+ uri4_1,
+ uri5_1,
+ uri0_cnn,
+ uri1_cnn,
+ uri2_cnn,
+ uri3_cnn,
+ uri4_cnn,
+ uri5_cnn,
+ ]);
+
+ matches_always(perm_cnn, [uri0_n, uri0_1]);
+ matches_weak(perm_cnn, [uri1_n, uri1_1]);
+ matches_never(perm_cnn, [
+ uri2_n,
+ uri3_n,
+ uri4_n,
+ uri5_n,
+ uri0_y_,
+ uri1_y_,
+ uri2_y_,
+ uri3_y_,
+ uri4_y_,
+ uri5_y_,
+ uri2_1,
+ uri3_1,
+ uri4_1,
+ uri5_1,
+ uri0_cnn,
+ uri1_cnn,
+ uri2_cnn,
+ uri3_cnn,
+ uri4_cnn,
+ uri5_cnn,
+ ]);
+
+ // Clean up!
+ pm.removeAll();
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_matchesuri.js b/extensions/permissions/test/unit/test_permmanager_matchesuri.js
new file mode 100644
index 0000000000..1218fbf9ca
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_matchesuri.js
@@ -0,0 +1,252 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function matches_always(perm, uris) {
+ uris.forEach(uri => {
+ Assert.ok(
+ perm.matchesURI(uri, true),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ Assert.ok(
+ perm.matchesURI(uri, false),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ });
+}
+
+function matches_weak(perm, uris) {
+ uris.forEach(uri => {
+ Assert.ok(
+ !perm.matchesURI(uri, true),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ Assert.ok(
+ perm.matchesURI(uri, false),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ });
+}
+
+function matches_never(perm, uris) {
+ uris.forEach(uri => {
+ Assert.ok(
+ !perm.matchesURI(uri, true),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ Assert.ok(
+ !perm.matchesURI(uri, false),
+ "perm: " + perm.principal.origin + ", URI: " + uri.spec
+ );
+ });
+}
+
+function mk_permission(uri) {
+ let pm = Services.perms;
+
+ let secMan = Services.scriptSecurityManager;
+
+ // Get the permission from the principal!
+ let principal = secMan.createContentPrincipal(uri, {});
+
+ pm.addFromPrincipal(principal, "test/matchesuri", pm.ALLOW_ACTION);
+ let permission = pm.getPermissionObject(principal, "test/matchesuri", true);
+
+ return permission;
+}
+
+function run_test() {
+ // initialize the permission manager service
+ let pm = Services.perms;
+
+ let fileprefix = "file:///";
+ if (Services.appinfo.OS == "WINNT") {
+ // Windows rejects files if they don't have a drive. See Bug 1180870
+ fileprefix += "c:/";
+ }
+
+ // Add some permissions
+ let uri0 = NetUtil.newURI("http://google.com:9091/just/a/path");
+ let uri1 = NetUtil.newURI("http://hangouts.google.com:9091/some/path");
+ let uri2 = NetUtil.newURI("http://google.com:9091/");
+ let uri3 = NetUtil.newURI("http://google.org:9091/");
+ let uri4 = NetUtil.newURI("http://deeper.hangouts.google.com:9091/");
+ let uri5 = NetUtil.newURI("https://google.com/just/a/path");
+ let uri6 = NetUtil.newURI("https://hangouts.google.com");
+ let uri7 = NetUtil.newURI("https://google.com/");
+
+ let fileuri1 = NetUtil.newURI(fileprefix + "a/file/path");
+ let fileuri2 = NetUtil.newURI(fileprefix + "a/file/path/deeper");
+ let fileuri3 = NetUtil.newURI(fileprefix + "a/file/otherpath");
+
+ {
+ let perm = mk_permission(uri0);
+ matches_always(perm, [uri0, uri2]);
+ matches_weak(perm, [uri1, uri4]);
+ matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
+ }
+
+ {
+ let perm = mk_permission(uri1);
+ matches_always(perm, [uri1]);
+ matches_weak(perm, [uri4]);
+ matches_never(perm, [
+ uri0,
+ uri2,
+ uri3,
+ uri5,
+ uri6,
+ uri7,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(uri2);
+ matches_always(perm, [uri0, uri2]);
+ matches_weak(perm, [uri1, uri4]);
+ matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
+ }
+
+ {
+ let perm = mk_permission(uri3);
+ matches_always(perm, [uri3]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri1,
+ uri2,
+ uri4,
+ uri5,
+ uri6,
+ uri7,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(uri4);
+ matches_always(perm, [uri4]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri1,
+ uri2,
+ uri3,
+ uri5,
+ uri6,
+ uri7,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(uri5);
+ matches_always(perm, [uri5, uri7]);
+ matches_weak(perm, [uri6]);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(uri6);
+ matches_always(perm, [uri6]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ uri5,
+ uri7,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(uri7);
+ matches_always(perm, [uri5, uri7]);
+ matches_weak(perm, [uri6]);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ fileuri1,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(fileuri1);
+ matches_always(perm, [fileuri1]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ uri5,
+ uri6,
+ uri7,
+ fileuri2,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(fileuri2);
+ matches_always(perm, [fileuri2]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ uri5,
+ uri6,
+ uri7,
+ fileuri1,
+ fileuri3,
+ ]);
+ }
+
+ {
+ let perm = mk_permission(fileuri3);
+ matches_always(perm, [fileuri3]);
+ matches_weak(perm, []);
+ matches_never(perm, [
+ uri0,
+ uri1,
+ uri2,
+ uri3,
+ uri4,
+ uri5,
+ uri6,
+ uri7,
+ fileuri1,
+ fileuri2,
+ ]);
+ }
+
+ // Clean up!
+ pm.removeAll();
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_10-11.js b/extensions/permissions/test/unit/test_permmanager_migrate_10-11.js
new file mode 100644
index 0000000000..c0d30865ee
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_10-11.js
@@ -0,0 +1,196 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 10;
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ insertOrigin(
+ "https://foo.com",
+ "storageAccessAPI^https://foo.com",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+ insertOrigin(
+ "http://foo.com",
+ "storageAccessAPI^https://bar.com^https://foo.com",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+ insertOrigin(
+ "http://foo.com",
+ "storageAccessAPI^https://bar.com^https://baz.com",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+ insertOrigin("http://foo.com^inBrowser=1", "A", 2, 0, 0, 0);
+
+ // CLose the db connection
+ stmt6Insert.finalize();
+ db.close();
+ db = null;
+
+ let expected = [
+ ["https://foo.com", "storageAccessAPI^https://foo.com", 2, 0, 0, 0],
+ ["http://foo.com", "storageAccessAPI^https://bar.com", 2, 0, 0, 0],
+ ["http://foo.com", "storageAccessAPI^https://bar.com", 2, 0, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 2, 0, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
+ await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
+ try {
+ mozPermsCount.executeStep();
+ Assert.equal(mozPermsCount.getInt64(0), expected.length);
+ } finally {
+ mozPermsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_11-12.js b/extensions/permissions/test/unit/test_permmanager_migrate_11-12.js
new file mode 100644
index 0000000000..97170a9240
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_11-12.js
@@ -0,0 +1,230 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 11;
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ insertOrigin("https://a.com", "3rdPartyStorage^https://b.com", 2, 0, 0, 0);
+ insertOrigin(
+ "https://www.a.com",
+ "3rdPartyStorage^https://www.c.com",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+ insertOrigin(
+ "https://localhost",
+ "3rdPartyStorage^http://www.c.com",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+
+ insertOrigin(
+ "https://www.b.co.uk",
+ "3rdPartyStorage^https://www.a.co.uk",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+
+ insertOrigin(
+ "https://sub.www.b.co.uk",
+ "3rdPartyStorage^https://sub.www.a.co.uk",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+
+ insertOrigin(
+ "https://example.b.co.uk",
+ "3rdPartyStorage^https://www.a.co.uk",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+
+ insertOrigin(
+ "https://[::1]",
+ "3rdPartyStorage^https://www.a.co.uk",
+ 2,
+ 0,
+ 0,
+ 0
+ );
+ // Close the db connection
+ stmt6Insert.finalize();
+ db.close();
+ db = null;
+ info(Services.perms.all);
+
+ let expected = [
+ ["https://a.com", "3rdPartyStorage^https://b.com", 2, 0, 0, 0],
+ ["https://a.com", "3rdPartyStorage^https://www.c.com", 2, 0, 0, 0],
+ ["https://localhost", "3rdPartyStorage^http://www.c.com", 2, 0, 0, 0],
+ ["https://b.co.uk", "3rdPartyStorage^https://www.a.co.uk", 2, 0, 0, 0],
+ ["https://b.co.uk", "3rdPartyStorage^https://sub.www.a.co.uk", 2, 0, 0, 0],
+ ["https://[::1]", "3rdPartyStorage^https://www.a.co.uk", 2, 0, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
+ await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ info(Services.perms.all);
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+ info(expected);
+ info(found);
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
+ try {
+ mozPermsCount.executeStep();
+ Assert.equal(mozPermsCount.getInt64(0), expected.length);
+ } finally {
+ mozPermsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_4-7.js b/extensions/permissions/test/unit/test_permmanager_migrate_4-7.js
new file mode 100644
index 0000000000..857e0a462c
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_4-7.js
@@ -0,0 +1,264 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 4;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
+ insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
+ insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
+ insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
+ insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost(
+ "moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}",
+ "A",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost(
+ "moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}",
+ "B",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
+ insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
+ ];
+
+ // CLose the db connection
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
+ // and http://foo.com or a subdomain are never visited.
+ // ["http://foo.com", "A", 1, 0, 0],
+ // ["http://foo.com^inBrowser=1", "A", 1, 0, 0],
+ //
+ // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries
+ // for subdomains of foo.com either
+ // ["http://sub.foo.com", "B", 1, 0, 0],
+ // ["http://subber.sub.foo.com", "B", 1, 0, 0],
+
+ ["https://foo.com", "A", 1, 0, 0],
+ ["https://foo.com", "C", 1, 0, 0],
+ ["https://foo.com^inBrowser=1", "A", 1, 0, 0],
+ ["https://sub.foo.com", "B", 1, 0, 0],
+ ["https://subber.sub.foo.com", "B", 1, 0, 0],
+
+ // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
+ ["http://bar.ca", "B", 1, 0, 0],
+ ["https://bar.ca", "B", 1, 0, 0],
+ ["http://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["https://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["file:///some/path/to/file.html", "A", 1, 0, 0],
+ ["file:///another/file.html", "A", 1, 0, 0],
+
+ // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
+ // also have these entries
+ ["ftp://foo.com:8000", "A", 1, 0, 0],
+ ["ftp://foo.com:8000", "C", 1, 0, 0],
+ ["ftp://foo.com:8000^inBrowser=1", "A", 1, 0, 0],
+
+ // In addition, because we search for port/scheme combinations under eTLD+1, we should have the
+ // following entries
+ ["ftp://sub.foo.com:8000", "B", 1, 0, 0],
+ ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0],
+
+ // Make sure that we also support localhost, and IP addresses
+ ["http://localhost", "A", 1, 0, 0],
+ ["https://localhost", "A", 1, 0, 0],
+ ["http://127.0.0.1", "A", 1, 0, 0],
+ ["https://127.0.0.1", "A", 1, 0, 0],
+ ["http://192.0.2.235", "A", 1, 0, 0],
+ ["https://192.0.2.235", "A", 1, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ // The moz_hosts table should still exist but be empty
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js b/extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js
new file mode 100644
index 0000000000..aa735e534a
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -0,0 +1,279 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+/*
+ * Prevent the nsINavHistoryService from being avaliable for the migration
+ */
+
+var CONTRACT_ID = "@mozilla.org/browser/nav-history-service;1";
+var factory = {
+ createInstance() {
+ throw new Error("There is no history service");
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
+};
+
+var newClassID = Services.uuid.generateUUID();
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+var oldClassID = registrar.contractIDToCID(CONTRACT_ID);
+// TODO: There was a var oldFactory = here causing linter errors as it
+// was unused. We should check if this function call is needed at all.
+Components.manager.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
+registrar.registerFactory(newClassID, "", CONTRACT_ID, factory);
+
+function cleanupFactory() {
+ registrar.unregisterFactory(newClassID, factory);
+ registrar.registerFactory(oldClassID, "", CONTRACT_ID, null);
+}
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+/*
+ * Done nsINavHistoryService code
+ */
+
+add_task(function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // Make sure that we can't resolve the nsINavHistoryService
+ try {
+ Cc["@mozilla.org/browser/nav-history-service;1"].getService(
+ Ci.nsINavHistoryService
+ );
+ Assert.ok(false, "There shouldn't have been a nsINavHistoryService");
+ } catch (e) {
+ Assert.ok(true, "There wasn't a nsINavHistoryService");
+ }
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.ok(pm.all.length >= 0, "Permission manager not initialized?");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 4;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
+ insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
+ insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
+ insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
+ insertHost("263.123.555.676", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost(
+ "moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}",
+ "A",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost(
+ "moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}",
+ "B",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
+ insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
+ ];
+
+ // CLose the db connection
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ ["http://foo.com", "A", 1, 0, 0],
+ ["http://foo.com", "C", 1, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 1, 0, 0],
+ ["http://sub.foo.com", "B", 1, 0, 0],
+ ["http://subber.sub.foo.com", "B", 1, 0, 0],
+
+ ["https://foo.com", "A", 1, 0, 0],
+ ["https://foo.com", "C", 1, 0, 0],
+ ["https://foo.com^inBrowser=1", "A", 1, 0, 0],
+ ["https://sub.foo.com", "B", 1, 0, 0],
+ ["https://subber.sub.foo.com", "B", 1, 0, 0],
+
+ // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
+ ["http://bar.ca", "B", 1, 0, 0],
+ ["https://bar.ca", "B", 1, 0, 0],
+ ["http://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["https://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["file:///some/path/to/file.html", "A", 1, 0, 0],
+ ["file:///another/file.html", "A", 1, 0, 0],
+
+ // Make sure that we also support localhost, and IP addresses
+ ["http://localhost", "A", 1, 0, 0],
+ ["https://localhost", "A", 1, 0, 0],
+ ["http://127.0.0.1", "A", 1, 0, 0],
+ ["https://127.0.0.1", "A", 1, 0, 0],
+ ["http://263.123.555.676", "A", 1, 0, 0],
+ ["https://263.123.555.676", "A", 1, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ // The moz_hosts table should still exist but be empty
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ db.close();
+ }
+
+ cleanupFactory();
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js b/extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js
new file mode 100644
index 0000000000..8ea03d5e16
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js
@@ -0,0 +1,365 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 5;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ /*
+ * V5 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt5Insert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ /*
+ * V4 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts_v4 (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts_v4 (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt5Insert.bindByName("id", thisId);
+ stmt5Insert.bindByName("origin", origin);
+ stmt5Insert.bindByName("type", type);
+ stmt5Insert.bindByName("permission", permission);
+ stmt5Insert.bindByName("expireType", expireType);
+ stmt5Insert.bindByName("expireTime", expireTime);
+ stmt5Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt5Insert.execute();
+ } finally {
+ stmt5Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+
+ let created5 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^inBrowser=1", "A", 2, 0, 0, 0),
+ ];
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
+ insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
+ insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
+ insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
+ insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost(
+ "moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}",
+ "A",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost(
+ "moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}",
+ "B",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
+ insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
+ ];
+
+ // CLose the db connection
+ stmt5Insert.finalize();
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
+ // and http://foo.com or a subdomain are never visited.
+ // ["http://foo.com", "A", 1, 0, 0],
+ // ["http://foo.com^inBrowser=1", "A", 1, 0, 0],
+ //
+ // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries
+ // for subdomains of foo.com either
+ // ["http://sub.foo.com", "B", 1, 0, 0],
+ // ["http://subber.sub.foo.com", "B", 1, 0, 0],
+
+ ["https://foo.com", "A", 1, 0, 0],
+ ["https://foo.com", "C", 1, 0, 0],
+ ["https://foo.com^inBrowser=1", "A", 1, 0, 0],
+ ["https://sub.foo.com", "B", 1, 0, 0],
+ ["https://subber.sub.foo.com", "B", 1, 0, 0],
+
+ // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
+ ["http://bar.ca", "B", 1, 0, 0],
+ ["https://bar.ca", "B", 1, 0, 0],
+ ["http://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["https://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["file:///some/path/to/file.html", "A", 1, 0, 0],
+ ["file:///another/file.html", "A", 1, 0, 0],
+
+ // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
+ // also have these entries
+ ["ftp://foo.com:8000", "A", 1, 0, 0],
+ ["ftp://foo.com:8000", "C", 1, 0, 0],
+ ["ftp://foo.com:8000^inBrowser=1", "A", 1, 0, 0],
+
+ // In addition, because we search for port/scheme combinations under eTLD+1, we should have the
+ // following entries
+ ["ftp://sub.foo.com:8000", "B", 1, 0, 0],
+ ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0],
+
+ // Make sure that we also support localhost, and IP addresses
+ ["http://localhost", "A", 1, 0, 0],
+ ["https://localhost", "A", 1, 0, 0],
+ ["http://127.0.0.1", "A", 1, 0, 0],
+ ["https://127.0.0.1", "A", 1, 0, 0],
+ ["http://192.0.2.235", "A", 1, 0, 0],
+ ["https://192.0.2.235", "A", 1, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(db.tableExists("moz_perms_v6"));
+
+ // The moz_hosts table should still exist but be empty
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ // Check that the moz_perms_v6 table contains the backup of the entry we created
+ let mozPermsV6Stmt = db.createStatement(
+ "SELECT " +
+ "origin, type, permission, expireType, expireTime, modificationTime " +
+ "FROM moz_perms_v6 WHERE id = :id"
+ );
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created5.forEach(it => {
+ mozPermsV6Stmt.reset();
+ mozPermsV6Stmt.bindByName("id", it.id);
+ mozPermsV6Stmt.executeStep();
+ Assert.equal(mozPermsV6Stmt.getUTF8String(0), it.origin);
+ Assert.equal(mozPermsV6Stmt.getUTF8String(1), it.type);
+ Assert.equal(mozPermsV6Stmt.getInt64(2), it.permission);
+ Assert.equal(mozPermsV6Stmt.getInt64(3), it.expireType);
+ Assert.equal(mozPermsV6Stmt.getInt64(4), it.expireTime);
+ Assert.equal(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+ });
+ } finally {
+ mozPermsV6Stmt.finalize();
+ }
+
+ // Check that there are the right number of values
+ let mozPermsV6Count = db.createStatement(
+ "SELECT count(*) FROM moz_perms_v6"
+ );
+ try {
+ mozPermsV6Count.executeStep();
+ Assert.equal(mozPermsV6Count.getInt64(0), created5.length);
+ } finally {
+ mozPermsV6Count.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js b/extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js
new file mode 100644
index 0000000000..8c330effa9
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 5;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ /*
+ * V5 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt5Insert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ let id = 0;
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt5Insert.bindByName("id", thisId);
+ stmt5Insert.bindByName("origin", origin);
+ stmt5Insert.bindByName("type", type);
+ stmt5Insert.bindByName("permission", permission);
+ stmt5Insert.bindByName("expireType", expireType);
+ stmt5Insert.bindByName("expireTime", expireTime);
+ stmt5Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt5Insert.execute();
+ } finally {
+ stmt5Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ host: origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ // eslint-disable-next-line no-unused-vars
+ let created5 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0),
+
+ insertOrigin("http://127.0.0.1", "B", 2, 0, 0, 0),
+ insertOrigin("http://localhost", "B", 2, 0, 0, 0),
+ ];
+
+ let created4 = []; // Didn't create any v4 entries, so the DB should be empty
+
+ // CLose the db connection
+ stmt5Insert.finalize();
+ db.close();
+ stmt5Insert = null;
+ db = null;
+
+ let expected = [
+ ["https://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 2, 0, 0, 0],
+
+ ["http://127.0.0.1", "B", 2, 0, 0, 0],
+ ["http://localhost", "B", 2, 0, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ let mozHostsStmt = db.createStatement(
+ "SELECT " +
+ "host, type, permission, expireType, expireTime, " +
+ "modificationTime, isInBrowserElement " +
+ "FROM moz_hosts WHERE id = :id"
+ );
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created4.forEach(it => {
+ mozHostsStmt.reset();
+ mozHostsStmt.bindByName("id", it.id);
+ mozHostsStmt.executeStep();
+ Assert.equal(mozHostsStmt.getUTF8String(0), it.host);
+ Assert.equal(mozHostsStmt.getUTF8String(1), it.type);
+ Assert.equal(mozHostsStmt.getInt64(2), it.permission);
+ Assert.equal(mozHostsStmt.getInt64(3), it.expireType);
+ Assert.equal(mozHostsStmt.getInt64(4), it.expireTime);
+ Assert.equal(mozHostsStmt.getInt64(5), it.modificationTime);
+ Assert.equal(mozHostsStmt.getInt64(6), it.isInBrowserElement);
+ });
+ } finally {
+ mozHostsStmt.finalize();
+ }
+
+ // Check that there are the right number of values
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), created4.length);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js b/extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js
new file mode 100644
index 0000000000..93b78e6478
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js
@@ -0,0 +1,366 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 6;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ /*
+ * V5 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_perms (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ /*
+ * V4 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+
+ let created6 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^inBrowser=1", "A", 2, 0, 0, 0),
+ ];
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
+ insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
+ insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
+ insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
+ insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost(
+ "moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}",
+ "A",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost(
+ "moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}",
+ "B",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
+ insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
+ ];
+
+ // CLose the db connection
+ stmt6Insert.finalize();
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
+ // and http://foo.com or a subdomain are never visited.
+ // ["http://foo.com", "A", 1, 0, 0],
+ // ["http://foo.com^inBrowser=1", "A", 1, 0, 0],
+ //
+ // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries
+ // for subdomains of foo.com either
+ // ["http://sub.foo.com", "B", 1, 0, 0],
+ // ["http://subber.sub.foo.com", "B", 1, 0, 0],
+
+ ["https://foo.com", "A", 1, 0, 0],
+ ["https://foo.com", "C", 1, 0, 0],
+ ["https://foo.com^inBrowser=1", "A", 1, 0, 0],
+ ["https://sub.foo.com", "B", 1, 0, 0],
+ ["https://subber.sub.foo.com", "B", 1, 0, 0],
+
+ // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries
+ ["http://bar.ca", "B", 1, 0, 0],
+ ["https://bar.ca", "B", 1, 0, 0],
+ ["http://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["https://bar.ca^inBrowser=1", "A", 1, 0, 0],
+ ["file:///some/path/to/file.html", "A", 1, 0, 0],
+ ["file:///another/file.html", "A", 1, 0, 0],
+
+ // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
+ // also have these entries
+ ["ftp://foo.com:8000", "A", 1, 0, 0],
+ ["ftp://foo.com:8000", "C", 1, 0, 0],
+ ["ftp://foo.com:8000^inBrowser=1", "A", 1, 0, 0],
+
+ // In addition, because we search for port/scheme combinations under eTLD+1, we should have the
+ // following entries
+ ["ftp://sub.foo.com:8000", "B", 1, 0, 0],
+ ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0],
+
+ // Make sure that we also support localhost, and IP addresses
+ ["http://localhost", "A", 1, 0, 0],
+ ["https://localhost", "A", 1, 0, 0],
+ ["http://127.0.0.1", "A", 1, 0, 0],
+ ["https://127.0.0.1", "A", 1, 0, 0],
+ ["http://192.0.2.235", "A", 1, 0, 0],
+ ["https://192.0.2.235", "A", 1, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(db.tableExists("moz_perms_v6"));
+
+ // The moz_hosts table should still exist but be empty
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ // Check that the moz_perms_v6 table contains the backup of the entry we created
+ let mozPermsV6Stmt = db.createStatement(
+ "SELECT " +
+ "origin, type, permission, expireType, expireTime, modificationTime " +
+ "FROM moz_perms_v6 WHERE id = :id"
+ );
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created6.forEach(it => {
+ mozPermsV6Stmt.reset();
+ mozPermsV6Stmt.bindByName("id", it.id);
+ mozPermsV6Stmt.executeStep();
+ Assert.equal(mozPermsV6Stmt.getUTF8String(0), it.origin);
+ Assert.equal(mozPermsV6Stmt.getUTF8String(1), it.type);
+ Assert.equal(mozPermsV6Stmt.getInt64(2), it.permission);
+ Assert.equal(mozPermsV6Stmt.getInt64(3), it.expireType);
+ Assert.equal(mozPermsV6Stmt.getInt64(4), it.expireTime);
+ Assert.equal(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+ });
+ } finally {
+ mozPermsV6Stmt.finalize();
+ }
+
+ // Check that there are the right number of values
+ let mozPermsV6Count = db.createStatement(
+ "SELECT count(*) FROM moz_perms_v6"
+ );
+ try {
+ mozPermsV6Count.executeStep();
+ Assert.equal(mozPermsV6Count.getInt64(0), created6.length);
+ } finally {
+ mozPermsV6Count.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js b/extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js
new file mode 100644
index 0000000000..feed156d29
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js
@@ -0,0 +1,199 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 6;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ /*
+ * V5 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_perms (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ let id = 0;
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ host: origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ // eslint-disable-next-line no-unused-vars
+ let created6 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0),
+ ];
+
+ let created4 = []; // Didn't create any v4 entries, so the DB should be empty
+
+ // CLose the db connection
+ stmt6Insert.finalize();
+ db.close();
+ stmt6Insert = null;
+ db = null;
+
+ let expected = [
+ ["https://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 2, 0, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ let mozHostsStmt = db.createStatement(
+ "SELECT " +
+ "host, type, permission, expireType, expireTime, " +
+ "modificationTime, isInBrowserElement " +
+ "FROM moz_hosts WHERE id = :id"
+ );
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created4.forEach(it => {
+ mozHostsStmt.reset();
+ mozHostsStmt.bindByName("id", it.id);
+ mozHostsStmt.executeStep();
+ Assert.equal(mozHostsStmt.getUTF8String(0), it.host);
+ Assert.equal(mozHostsStmt.getUTF8String(1), it.type);
+ Assert.equal(mozHostsStmt.getInt64(2), it.permission);
+ Assert.equal(mozHostsStmt.getInt64(3), it.expireType);
+ Assert.equal(mozHostsStmt.getInt64(4), it.expireTime);
+ Assert.equal(mozHostsStmt.getInt64(5), it.modificationTime);
+ Assert.equal(mozHostsStmt.getInt64(6), it.isInBrowserElement);
+ });
+ } finally {
+ mozHostsStmt.finalize();
+ }
+
+ // Check that there are the right number of values
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), created4.length);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_7-8.js b/extensions/permissions/test/unit/test_permmanager_migrate_7-8.js
new file mode 100644
index 0000000000..cd8b0f86cc
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_7-8.js
@@ -0,0 +1,328 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 7;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE moz_hosts");
+
+ /*
+ * V5 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_perms (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ /*
+ * V4 table
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ /*
+ * The v4 table is a backup
+ */
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts_is_backup (dummy INTEGER PRIMARY KEY)"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+ // eslint-disable-next-line no-unused-vars
+ let created7 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^inBrowser=1", "A", 2, 0, 0, 0),
+ insertOrigin("https://192.0.2.235", "A", 2, 0, 0),
+ ];
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true),
+ insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false),
+ insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true),
+ insertHost("localhost", "A", 1, 0, 0, 0, 0, false),
+ insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false),
+ insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false),
+ // Although ipv6 addresses are written with [] around the IP address,
+ // the .host property doesn't contain these []s, which means that we
+ // write it like this
+ insertHost("2001:db8::ff00:42:8329", "C", 1, 0, 0, 0, 0, false),
+ insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
+ insertHost(
+ "moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}",
+ "A",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost(
+ "moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}",
+ "B",
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ false
+ ),
+ insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
+ insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
+ ];
+
+ // CLose the db connection
+ stmt6Insert.finalize();
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ // We should have kept the previously migrated entries
+ ["https://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 2, 0, 0, 0],
+
+ // Make sure that we also support localhost, and IP addresses
+ ["https://localhost:8080", "A", 1, 0, 0],
+ ["ftp://127.0.0.1:8080", "A", 1, 0, 0],
+
+ ["http://[2001:db8::ff00:42:8329]", "C", 1, 0, 0],
+ ["https://[2001:db8::ff00:42:8329]", "C", 1, 0, 0],
+ ["http://192.0.2.235", "A", 1, 0, 0],
+
+ // There should only be one entry of this type in the database
+ ["https://192.0.2.235", "A", 2, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
+ await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_hosts_is_backup"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ // The moz_hosts table should still exist but be empty
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ // Check that there are the right number of values in the permissions database
+ let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
+ try {
+ mozPermsCount.executeStep();
+ Assert.equal(mozPermsCount.getInt64(0), expected.length);
+ } finally {
+ mozPermsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_migrate_9-10.js b/extensions/permissions/test/unit/test_permmanager_migrate_9-10.js
new file mode 100644
index 0000000000..02aa3bb467
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_9-10.js
@@ -0,0 +1,262 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+});
+
+var PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile) {
+ let file = profile.clone();
+ file.append(PERMISSIONS_FILE_NAME);
+ return file;
+}
+
+add_task(async function test() {
+ // Create and set up the permissions database.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ let profile = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.equal(pm.all.length, 0, "No cookies");
+
+ let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ db.schemaVersion = 9;
+ db.executeSimpleSQL("DROP TABLE moz_perms");
+ db.executeSimpleSQL("DROP TABLE IF EXISTS moz_hosts");
+
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_perms (" +
+ " id INTEGER PRIMARY KEY" +
+ ",origin TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ")"
+ );
+
+ let stmt6Insert = db.createStatement(
+ "INSERT INTO moz_perms (" +
+ "id, origin, type, permission, expireType, expireTime, modificationTime" +
+ ") VALUES (" +
+ ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" +
+ ")"
+ );
+
+ db.executeSimpleSQL(
+ "CREATE TABLE moz_hosts (" +
+ " id INTEGER PRIMARY KEY" +
+ ",host TEXT" +
+ ",type TEXT" +
+ ",permission INTEGER" +
+ ",expireType INTEGER" +
+ ",expireTime INTEGER" +
+ ",modificationTime INTEGER" +
+ ",appId INTEGER" +
+ ",isInBrowserElement INTEGER" +
+ ")"
+ );
+
+ let stmtInsert = db.createStatement(
+ "INSERT INTO moz_hosts (" +
+ "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+ ") VALUES (" +
+ ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+ ")"
+ );
+
+ let id = 0;
+
+ function insertOrigin(
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime
+ ) {
+ let thisId = id++;
+
+ stmt6Insert.bindByName("id", thisId);
+ stmt6Insert.bindByName("origin", origin);
+ stmt6Insert.bindByName("type", type);
+ stmt6Insert.bindByName("permission", permission);
+ stmt6Insert.bindByName("expireType", expireType);
+ stmt6Insert.bindByName("expireTime", expireTime);
+ stmt6Insert.bindByName("modificationTime", modificationTime);
+
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
+
+ return {
+ id: thisId,
+ origin,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ };
+ }
+
+ function insertHost(
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement
+ ) {
+ let thisId = id++;
+
+ stmtInsert.bindByName("id", thisId);
+ stmtInsert.bindByName("host", host);
+ stmtInsert.bindByName("type", type);
+ stmtInsert.bindByName("permission", permission);
+ stmtInsert.bindByName("expireType", expireType);
+ stmtInsert.bindByName("expireTime", expireTime);
+ stmtInsert.bindByName("modificationTime", modificationTime);
+ stmtInsert.bindByName("appId", appId);
+ stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
+
+ return {
+ id: thisId,
+ host,
+ type,
+ permission,
+ expireType,
+ expireTime,
+ modificationTime,
+ appId,
+ isInBrowserElement,
+ };
+ }
+ // eslint-disable-next-line no-unused-vars
+ let created7 = [
+ insertOrigin("https://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com", "A", 2, 0, 0, 0),
+ insertOrigin("http://foo.com^inBrowser=1", "A", 2, 0, 0, 0),
+ ];
+
+ // Add some rows to the database
+ // eslint-disable-next-line no-unused-vars
+ let created = [
+ insertHost("foo.com", "A", 1, 0, 0, 0, 0, false),
+ insertHost("foo.com", "B", 1, 0, 0, 0, 1000, false),
+ insertHost("foo.com", "C", 1, 0, 0, 0, 2000, true),
+ ];
+
+ // CLose the db connection
+ stmt6Insert.finalize();
+ stmtInsert.finalize();
+ db.close();
+ stmtInsert = null;
+ db = null;
+
+ let expected = [
+ ["https://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com", "A", 2, 0, 0, 0],
+ ["http://foo.com^inBrowser=1", "A", 2, 0, 0, 0],
+ ];
+
+ let found = expected.map(it => 0);
+
+ // Add some places to the places database
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("https://foo.com/some/other/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(
+ Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
+ );
+ await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
+ await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
+
+ // This will force the permission-manager to reload the data.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Force initialization of the PermissionManager
+ for (let permission of Services.perms.all) {
+ let isExpected = false;
+
+ expected.forEach((it, i) => {
+ if (
+ permission.principal.origin == it[0] &&
+ permission.type == it[1] &&
+ permission.capability == it[2] &&
+ permission.expireType == it[3] &&
+ permission.expireTime == it[4]
+ ) {
+ isExpected = true;
+ found[i]++;
+ }
+ });
+
+ Assert.ok(
+ isExpected,
+ "Permission " +
+ (isExpected ? "should" : "shouldn't") +
+ " be in permission database: " +
+ permission.principal.origin +
+ ", " +
+ permission.type +
+ ", " +
+ permission.capability +
+ ", " +
+ permission.expireType +
+ ", " +
+ permission.expireTime
+ );
+ }
+
+ found.forEach((count, i) => {
+ Assert.ok(
+ count == 1,
+ "Expected count = 1, got count = " +
+ count +
+ " for permission " +
+ expected[i]
+ );
+ });
+
+ // Check to make sure that all of the tables which we care about are present
+ {
+ db = Services.storage.openDatabase(GetPermissionsFile(profile));
+ Assert.ok(db.tableExists("moz_perms"));
+ Assert.ok(db.tableExists("moz_hosts"));
+ Assert.ok(!db.tableExists("moz_perms_v6"));
+
+ let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
+ try {
+ mozHostsCount.executeStep();
+ Assert.equal(mozHostsCount.getInt64(0), 3);
+ } finally {
+ mozHostsCount.finalize();
+ }
+
+ let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
+ try {
+ mozPermsCount.executeStep();
+ Assert.equal(mozPermsCount.getInt64(0), expected.length);
+ } finally {
+ mozPermsCount.finalize();
+ }
+
+ db.close();
+ }
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_notifications.js b/extensions/permissions/test/unit/test_permmanager_notifications.js
new file mode 100644
index 0000000000..a84f79b6ec
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_notifications.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the permissionmanager 'added', 'changed', 'deleted', and 'cleared'
+// notifications behave as expected.
+
+var test_generator = do_run_test();
+
+function run_test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ do_test_pending();
+ test_generator.next();
+}
+
+function* do_run_test() {
+ let pm = Services.perms;
+ let now = Number(Date.now());
+ let permType = "test/expiration-perm";
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI("http://example.com");
+ let principal = ssm.createContentPrincipal(uri, {});
+
+ let observer = new permission_observer(test_generator, now, permType);
+ Services.obs.addObserver(observer, "perm-changed");
+
+ // Add a permission, to test the 'add' notification. Note that we use
+ // do_execute_soon() so that we can use our generator to continue the test
+ // where we left off.
+ executeSoon(function() {
+ pm.addFromPrincipal(
+ principal,
+ permType,
+ pm.ALLOW_ACTION,
+ pm.EXPIRE_TIME,
+ now + 100000
+ );
+ });
+ yield;
+
+ // Alter a permission, to test the 'changed' notification.
+ executeSoon(function() {
+ pm.addFromPrincipal(
+ principal,
+ permType,
+ pm.ALLOW_ACTION,
+ pm.EXPIRE_TIME,
+ now + 200000
+ );
+ });
+ yield;
+
+ // Remove a permission, to test the 'deleted' notification.
+ executeSoon(function() {
+ pm.removeFromPrincipal(principal, permType);
+ });
+ yield;
+
+ // Clear permissions, to test the 'cleared' notification.
+ executeSoon(function() {
+ pm.removeAll();
+ });
+ yield;
+
+ Services.obs.removeObserver(observer, "perm-changed");
+ Assert.equal(observer.adds, 1);
+ Assert.equal(observer.changes, 1);
+ Assert.equal(observer.deletes, 1);
+ Assert.ok(observer.cleared);
+
+ do_finish_generator_test(test_generator);
+}
+
+function permission_observer(generator, now, type) {
+ // Set up our observer object.
+ this.generator = generator;
+ this.pm = Services.perms;
+ this.now = now;
+ this.type = type;
+ this.adds = 0;
+ this.changes = 0;
+ this.deletes = 0;
+ this.cleared = false;
+}
+
+permission_observer.prototype = {
+ observe(subject, topic, data) {
+ Assert.equal(topic, "perm-changed");
+
+ // "deleted" means a permission was deleted. aPermission is the deleted permission.
+ // "added" means a permission was added. aPermission is the added permission.
+ // "changed" means a permission was altered. aPermission is the new permission.
+ // "cleared" means the entire permission list was cleared. aPermission is null.
+ if (data == "added") {
+ let perm = subject.QueryInterface(Ci.nsIPermission);
+ this.adds++;
+ switch (this.adds) {
+ case 1:
+ Assert.equal(this.type, perm.type);
+ Assert.equal(this.pm.EXPIRE_TIME, perm.expireType);
+ Assert.equal(this.now + 100000, perm.expireTime);
+ break;
+ default:
+ do_throw("too many add notifications posted.");
+ }
+ } else if (data == "changed") {
+ let perm = subject.QueryInterface(Ci.nsIPermission);
+ this.changes++;
+ switch (this.changes) {
+ case 1:
+ Assert.equal(this.type, perm.type);
+ Assert.equal(this.pm.EXPIRE_TIME, perm.expireType);
+ Assert.equal(this.now + 200000, perm.expireTime);
+ break;
+ default:
+ do_throw("too many change notifications posted.");
+ }
+ } else if (data == "deleted") {
+ let perm = subject.QueryInterface(Ci.nsIPermission);
+ this.deletes++;
+ switch (this.deletes) {
+ case 1:
+ Assert.equal(this.type, perm.type);
+ break;
+ default:
+ do_throw("too many delete notifications posted.");
+ }
+ } else if (data == "cleared") {
+ // only clear once: at the end
+ Assert.ok(!this.cleared);
+ Assert.equal(do_count_array(Services.perms.all), 0);
+ this.cleared = true;
+ } else {
+ do_throw("unexpected data '" + data + "'!");
+ }
+
+ // Continue the test.
+ do_run_generator(this.generator);
+ },
+};
diff --git a/extensions/permissions/test/unit/test_permmanager_oa_strip.js b/extensions/permissions/test/unit/test_permmanager_oa_strip.js
new file mode 100644
index 0000000000..7c791928cd
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_oa_strip.js
@@ -0,0 +1,220 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_URI = Services.io.newURI("http://example.com");
+const TEST_PERMISSION = "test/oastrip";
+const TEST_PERMISSION2 = "test/oastrip2";
+const TEST_PERMISSION3 = "test/oastrip3";
+
+// List of permissions which are not isolated by private browsing or user context
+// as per array kStripOAPermissions in PermissionManager.cpp
+const STRIPPED_PERMS = ["cookie"];
+
+let principal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_URI,
+ {}
+);
+let principalPrivateBrowsing = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_URI,
+ { privateBrowsingId: 1 }
+);
+let principalUserContext1 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_URI,
+ { userContextId: 1 }
+);
+let principalUserContext2 = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_URI,
+ { userContextId: 2 }
+);
+
+function testOAIsolation(permIsolateUserContext, permIsolatePrivateBrowsing) {
+ info(
+ `testOAIsolation: permIsolateUserContext: ${permIsolateUserContext}; permIsolatePrivateBrowsing: ${permIsolatePrivateBrowsing}`
+ );
+
+ let pm = Services.perms;
+
+ Services.prefs.setBoolPref(
+ "permissions.isolateBy.userContext",
+ permIsolateUserContext
+ );
+ Services.prefs.setBoolPref(
+ "permissions.isolateBy.privateBrowsing",
+ permIsolatePrivateBrowsing
+ );
+
+ // Set test permission for normal browsing
+ pm.addFromPrincipal(principal, TEST_PERMISSION, pm.ALLOW_ACTION);
+
+ // Check normal browsing permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+ // normal browsing => user context 1
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext1, TEST_PERMISSION)
+ );
+ // normal browsing => user context 2
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext2, TEST_PERMISSION)
+ );
+ // normal browsing => private browsing
+ Assert.equal(
+ permIsolatePrivateBrowsing
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principalPrivateBrowsing, TEST_PERMISSION)
+ );
+
+ // Set permission for private browsing
+ pm.addFromPrincipal(
+ principalPrivateBrowsing,
+ TEST_PERMISSION2,
+ pm.DENY_ACTION
+ );
+
+ // Check private browsing permission
+ Assert.equal(
+ Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principalPrivateBrowsing, TEST_PERMISSION2)
+ );
+ // private browsing => normal browsing
+ Assert.equal(
+ permIsolatePrivateBrowsing
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION2)
+ );
+ // private browsing => user context 1
+ Assert.equal(
+ permIsolatePrivateBrowsing || permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext1, TEST_PERMISSION2)
+ );
+ // private browsing => user context 2
+ Assert.equal(
+ permIsolatePrivateBrowsing || permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext2, TEST_PERMISSION2)
+ );
+
+ // Set permission for user context 1
+ pm.addFromPrincipal(
+ principalUserContext1,
+ TEST_PERMISSION3,
+ pm.PROMPT_ACTION
+ );
+
+ // Check user context 1 permission
+ Assert.equal(
+ Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext1, TEST_PERMISSION3)
+ );
+
+ // user context 1 => normal browsing
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION3)
+ );
+ // user context 1 => user context 2
+ Assert.equal(
+ permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principalUserContext2, TEST_PERMISSION3)
+ );
+ // user context 1 => private browsing
+ Assert.equal(
+ permIsolatePrivateBrowsing || permIsolateUserContext
+ ? Ci.nsIPermissionManager.UNKNOWN_ACTION
+ : Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principalPrivateBrowsing, TEST_PERMISSION3)
+ );
+
+ pm.removeAll();
+
+ // Modifying an non-isolated/stripped permission should affect all browsing contexts,
+ // independently of permission isolation pref state
+ STRIPPED_PERMS.forEach(perm => {
+ info("Testing stripped permission " + perm);
+
+ // Add a permission for the normal window
+ pm.addFromPrincipal(principal, perm, pm.ALLOW_ACTION);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principalPrivateBrowsing, perm),
+ Ci.nsIPermissionManager.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principalUserContext1, perm),
+ Ci.nsIPermissionManager.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principalUserContext2, perm),
+ Ci.nsIPermissionManager.ALLOW_ACTION
+ );
+
+ // Remove the permission from private window
+ pm.removeFromPrincipal(principalPrivateBrowsing, perm);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, perm),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principalUserContext1, perm),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principalUserContext2, perm),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+
+ // Set a permission for a normal window and then override it by adding it to container 2 again
+ pm.addFromPrincipal(principal, perm, pm.PROMPT_ACTION);
+ pm.addFromPrincipal(principal, TEST_PERMISSION, pm.ALLOW_ACTION);
+ pm.addFromPrincipal(principalUserContext2, perm, pm.DENY_ACTION);
+
+ let principalPerms = pm.getAllForPrincipal(principalPrivateBrowsing, perm);
+
+ Assert.ok(
+ principalPerms.some(p => p.type == perm && p.capability == pm.DENY_ACTION)
+ );
+ if (permIsolatePrivateBrowsing) {
+ Assert.equal(principalPerms.length, 1);
+ Assert.ok(
+ principalPerms.some(
+ p => p.type == perm && p.capability == pm.DENY_ACTION
+ )
+ );
+ } else {
+ Assert.equal(principalPerms.length, 2);
+ Assert.ok(
+ principalPerms.some(
+ p => p.type == TEST_PERMISSION && p.capability == pm.ALLOW_ACTION
+ )
+ );
+ }
+ });
+
+ // Cleanup
+ pm.removeAll();
+}
+
+add_task(async function do_test() {
+ // Test all pref combinations and check if principals with different origin attributes
+ // are isolated.
+ testOAIsolation(true, true);
+ testOAIsolation(true, false);
+ testOAIsolation(false, true);
+ testOAIsolation(false, false);
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_remove_add_update.js b/extensions/permissions/test/unit/test_permmanager_remove_add_update.js
new file mode 100644
index 0000000000..815468e0e8
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_remove_add_update.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function check_enumerator(principal, permissions) {
+ let perms = Services.perms.getAllForPrincipal(principal);
+ for (let [type, capability, expireType] of permissions) {
+ let perm = perms.shift();
+ Assert.ok(perm != null);
+ Assert.equal(perm.type, type);
+ Assert.equal(perm.capability, capability);
+ Assert.equal(perm.expireType, expireType);
+ }
+ Assert.ok(!perms.length);
+}
+
+add_task(async function test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+
+ // setup a profile directory
+ do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.ok(pm.all.length === 0);
+
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://example.com"
+ );
+
+ info("From session to persistent");
+ pm.addFromPrincipal(
+ principal,
+ "test/foo",
+ pm.ALLOW_ACTION,
+ pm.EXPIRE_SESSION
+ );
+
+ check_enumerator(principal, [
+ ["test/foo", pm.ALLOW_ACTION, pm.EXPIRE_SESSION],
+ ]);
+
+ pm.addFromPrincipal(principal, "test/foo", pm.ALLOW_ACTION, pm.EXPIRE_NEVER);
+
+ check_enumerator(principal, [["test/foo", pm.ALLOW_ACTION, pm.EXPIRE_NEVER]]);
+
+ // Let's reload the DB.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ Assert.ok(pm.all.length === 1);
+ check_enumerator(principal, [["test/foo", pm.ALLOW_ACTION, pm.EXPIRE_NEVER]]);
+
+ info("From persistent to session");
+ pm.addFromPrincipal(
+ principal,
+ "test/foo",
+ pm.ALLOW_ACTION,
+ pm.EXPIRE_SESSION
+ );
+
+ check_enumerator(principal, [
+ ["test/foo", pm.ALLOW_ACTION, pm.EXPIRE_SESSION],
+ ]);
+
+ // Let's reload the DB.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+ Assert.ok(pm.all.length === 0);
+
+ info("From persistent to persistent");
+ pm.addFromPrincipal(principal, "test/foo", pm.ALLOW_ACTION, pm.EXPIRE_NEVER);
+ pm.addFromPrincipal(principal, "test/foo", pm.DENY_ACTION, pm.EXPIRE_NEVER);
+
+ check_enumerator(principal, [["test/foo", pm.DENY_ACTION, pm.EXPIRE_NEVER]]);
+
+ // Let's reload the DB.
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+ Assert.ok(pm.all.length === 1);
+ check_enumerator(principal, [["test/foo", pm.DENY_ACTION, pm.EXPIRE_NEVER]]);
+
+ info("Cleanup");
+ pm.removeAll();
+ check_enumerator(principal, []);
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_removeall.js b/extensions/permissions/test/unit/test_permmanager_removeall.js
new file mode 100644
index 0000000000..e6faea1369
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_removeall.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+ // setup a profile directory
+ var dir = do_get_profile();
+
+ // We need to execute a pm method to be sure that the DB is fully
+ // initialized.
+ var pm = Services.perms;
+ Assert.ok(pm.all.length === 0);
+
+ Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
+
+ // Let's force the completion of the DB reading.
+ Assert.ok(pm.all.length === 0);
+
+ // get the db file
+ var file = dir.clone();
+ file.append("permissions.sqlite");
+
+ Assert.ok(file.exists());
+
+ // corrupt the file
+ var ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ ostream.init(file, 0x02, 0o666, 0);
+ var conv = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(
+ Ci.nsIConverterOutputStream
+ );
+ conv.init(ostream, "UTF-8");
+ for (var i = 0; i < file.fileSize; ++i) {
+ conv.writeString("a");
+ }
+ conv.close();
+
+ // prepare an empty hostperm.1 file so that it can be used for importing
+ var hostperm = dir.clone();
+ hostperm.append("hostperm.1");
+ ostream.init(hostperm, 0x02 | 0x08, 0o666, 0);
+ ostream.close();
+
+ // remove all should not throw
+ pm.removeAll();
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_removebytype.js b/extensions/permissions/test/unit/test_permmanager_removebytype.js
new file mode 100644
index 0000000000..12a05c2902
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_removebytype.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+
+ // initialize the permission manager service
+ let pm = Services.perms;
+
+ Assert.equal(pm.all.length, 0);
+
+ // add some permissions
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://amazon.com:8080"
+ );
+ let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://google.com:2048"
+ );
+ let principal3 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://google.com"
+ );
+
+ pm.addFromPrincipal(principal, "apple", 3);
+ pm.addFromPrincipal(principal, "pear", 1);
+ pm.addFromPrincipal(principal, "cucumber", 1);
+
+ pm.addFromPrincipal(principal2, "apple", 2);
+ pm.addFromPrincipal(principal2, "pear", 2);
+
+ pm.addFromPrincipal(principal3, "cucumber", 3);
+ pm.addFromPrincipal(principal3, "apple", 1);
+
+ Assert.equal(pm.all.length, 7);
+
+ pm.removeByType("apple");
+ Assert.equal(pm.all.length, 4);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 3);
+
+ pm.removeByType("cucumber");
+ Assert.equal(pm.all.length, 2);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+
+ pm.removeByType("pear");
+ Assert.equal(pm.all.length, 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_removebytypesince.js b/extensions/permissions/test/unit/test_permmanager_removebytypesince.js
new file mode 100644
index 0000000000..294ce410c2
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_removebytypesince.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test() {
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+
+ // initialize the permission manager service
+ let pm = Services.perms;
+
+ Assert.equal(pm.all.length, 0);
+
+ // add some permissions
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://amazon.com:8080"
+ );
+ let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://google.com:2048"
+ );
+ let principal3 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://google.com"
+ );
+
+ pm.addFromPrincipal(principal, "apple", 3);
+ pm.addFromPrincipal(principal, "pear", 1);
+ pm.addFromPrincipal(principal, "cucumber", 1);
+
+ // sleep briefly, then record the time - we'll remove some permissions since then.
+ await new Promise(resolve => do_timeout(20, resolve));
+
+ let since = Date.now();
+
+ // *sob* - on Windows at least, the now recorded by PermissionManager.cpp
+ // might be a couple of ms *earlier* than what JS sees. So another sleep
+ // to ensure our |since| is greater than the time of the permissions we
+ // are now adding. Sadly this means we'll never be able to test when since
+ // exactly equals the modTime, but there you go...
+ await new Promise(resolve => do_timeout(20, resolve));
+
+ pm.addFromPrincipal(principal2, "apple", 2);
+ pm.addFromPrincipal(principal2, "pear", 2);
+
+ pm.addFromPrincipal(principal3, "cucumber", 3);
+ pm.addFromPrincipal(principal3, "apple", 1);
+
+ Assert.equal(pm.all.length, 7);
+
+ pm.removeByTypeSince("apple", since);
+
+ Assert.equal(pm.all.length, 5);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 3);
+
+ pm.removeByTypeSince("cucumber", since);
+ Assert.equal(pm.all.length, 4);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+
+ pm.removeByTypeSince("pear", since);
+ Assert.equal(pm.all.length, 3);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
+ Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
+
+ Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
+ Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_removepermission.js b/extensions/permissions/test/unit/test_permmanager_removepermission.js
new file mode 100644
index 0000000000..6f454d8f35
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_removepermission.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ // initialize the permission manager service
+ let pm = Services.perms;
+
+ Assert.equal(pm.all.length, 0);
+
+ // add some permissions
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://amazon.com:8080"
+ );
+ let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "http://google.com:2048"
+ );
+
+ pm.addFromPrincipal(principal, "apple", 0);
+ pm.addFromPrincipal(principal, "apple", 3);
+ pm.addFromPrincipal(principal, "pear", 3);
+ pm.addFromPrincipal(principal, "pear", 1);
+ pm.addFromPrincipal(principal, "cucumber", 1);
+ pm.addFromPrincipal(principal, "cucumber", 1);
+ pm.addFromPrincipal(principal, "cucumber", 1);
+
+ pm.addFromPrincipal(principal2, "apple", 2);
+ pm.addFromPrincipal(principal2, "pear", 0);
+ pm.addFromPrincipal(principal2, "pear", 2);
+
+ // Make sure that removePermission doesn't remove more than one permission each time
+ Assert.equal(pm.all.length, 5);
+
+ remove_one_by_type("apple");
+ Assert.equal(pm.all.length, 4);
+
+ remove_one_by_type("apple");
+ Assert.equal(pm.all.length, 3);
+
+ remove_one_by_type("pear");
+ Assert.equal(pm.all.length, 2);
+
+ remove_one_by_type("cucumber");
+ Assert.equal(pm.all.length, 1);
+
+ remove_one_by_type("pear");
+ Assert.equal(pm.all.length, 0);
+
+ function remove_one_by_type(type) {
+ for (let perm of pm.all) {
+ if (perm.type == type) {
+ pm.removePermission(perm);
+ break;
+ }
+ }
+ }
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_removesince.js b/extensions/permissions/test/unit/test_permmanager_removesince.js
new file mode 100644
index 0000000000..c33d02b08b
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_removesince.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that removing permissions since a specified time behaves as expected.
+
+var test_generator = do_run_test();
+
+function run_test() {
+ do_test_pending();
+ test_generator.next();
+}
+
+function continue_test() {
+ do_run_generator(test_generator);
+}
+
+function* do_run_test() {
+ let pm = Services.perms;
+
+ // to help with testing edge-cases, we will arrange for .removeAllSince to
+ // remove *all* permissions from one principal and one permission from another.
+ let permURI1 = NetUtil.newURI("http://example.com");
+ let principal1 = Services.scriptSecurityManager.createContentPrincipal(
+ permURI1,
+ {}
+ );
+
+ let permURI2 = NetUtil.newURI("http://example.org");
+ let principal2 = Services.scriptSecurityManager.createContentPrincipal(
+ permURI2,
+ {}
+ );
+
+ // add a permission now - this isn't going to be removed.
+ pm.addFromPrincipal(principal1, "test/remove-since", 1);
+
+ // sleep briefly, then record the time - we'll remove all since then.
+ do_timeout(20, continue_test);
+ yield;
+
+ let since = Number(Date.now());
+
+ // *sob* - on Windows at least, the now recorded by PermissionManager.cpp
+ // might be a couple of ms *earlier* than what JS sees. So another sleep
+ // to ensure our |since| is greater than the time of the permissions we
+ // are now adding. Sadly this means we'll never be able to test when since
+ // exactly equals the modTime, but there you go...
+ do_timeout(20, continue_test);
+ yield;
+
+ // add another item - this second one should get nuked.
+ pm.addFromPrincipal(principal1, "test/remove-since-2", 1);
+
+ // add 2 items for the second principal - both will be removed.
+ pm.addFromPrincipal(principal2, "test/remove-since", 1);
+ pm.addFromPrincipal(principal2, "test/remove-since-2", 1);
+
+ // do the removal.
+ pm.removeAllSince(since);
+
+ // principal1 - the first one should remain.
+ Assert.equal(
+ 1,
+ pm.testPermissionFromPrincipal(principal1, "test/remove-since")
+ );
+ // but the second should have been removed.
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal1, "test/remove-since-2")
+ );
+
+ // principal2 - both should have been removed.
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal2, "test/remove-since")
+ );
+ Assert.equal(
+ 0,
+ pm.testPermissionFromPrincipal(principal2, "test/remove-since-2")
+ );
+
+ do_finish_generator_test(test_generator);
+}
diff --git a/extensions/permissions/test/unit/test_permmanager_site_scope.js b/extensions/permissions/test/unit/test_permmanager_site_scope.js
new file mode 100644
index 0000000000..ca11385f75
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_site_scope.js
@@ -0,0 +1,120 @@
+const TEST_SITE_URI = Services.io.newURI("http://example.com");
+const TEST_FQDN_1_URI = Services.io.newURI("http://test1.example.com");
+const TEST_FQDN_2_URI = Services.io.newURI("http://test2.example.com");
+const TEST_OTHER_URI = Services.io.newURI("http://example.net");
+const TEST_PERMISSION = "3rdPartyStorage^https://example.org";
+
+add_task(async function do_test() {
+ let pm = Services.perms;
+
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_SITE_URI,
+ {}
+ );
+
+ let subdomain1Principal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_FQDN_1_URI,
+ {}
+ );
+
+ let subdomain2Principal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_FQDN_2_URI,
+ {}
+ );
+
+ let otherPrincipal = Services.scriptSecurityManager.createContentPrincipal(
+ TEST_OTHER_URI,
+ {}
+ );
+
+ // Set test permission for site
+ pm.addFromPrincipal(principal, TEST_PERMISSION, pm.ALLOW_ACTION);
+
+ // Check normal site permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+
+ // Check subdomain permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(subdomain1Principal, TEST_PERMISSION)
+ );
+
+ // Check other site permission
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(otherPrincipal, TEST_PERMISSION)
+ );
+
+ // Remove the permission from the site
+ pm.removeFromPrincipal(principal, TEST_PERMISSION);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subdomain1Principal, TEST_PERMISSION),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+
+ // Set test permission for subdomain
+ pm.addFromPrincipal(subdomain1Principal, TEST_PERMISSION, pm.ALLOW_ACTION);
+
+ // Check normal site permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)
+ );
+
+ // Check subdomain permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(subdomain1Principal, TEST_PERMISSION)
+ );
+
+ // Check other subdomain permission
+ Assert.equal(
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(subdomain2Principal, TEST_PERMISSION)
+ );
+
+ // Check other site permission
+ Assert.equal(
+ Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(otherPrincipal, TEST_PERMISSION)
+ );
+
+ // Check that subdomains include the site-scoped in the getAllForPrincipal
+ let sitePerms = pm.getAllForPrincipal(principal, TEST_PERMISSION);
+ let subdomain1Perms = pm.getAllForPrincipal(
+ subdomain1Principal,
+ TEST_PERMISSION
+ );
+ let subdomain2Perms = pm.getAllForPrincipal(
+ subdomain2Principal,
+ TEST_PERMISSION
+ );
+ let otherSitePerms = pm.getAllForPrincipal(otherPrincipal, TEST_PERMISSION);
+
+ Assert.equal(sitePerms.length, 1);
+ Assert.equal(subdomain1Perms.length, 1);
+ Assert.equal(subdomain2Perms.length, 1);
+ Assert.equal(otherSitePerms.length, 0);
+
+ // Remove the permission from the subdomain
+ pm.removeFromPrincipal(subdomain1Principal, TEST_PERMISSION);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subdomain1Principal, TEST_PERMISSION),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subdomain2Principal, TEST_PERMISSION),
+ Ci.nsIPermissionManager.UNKNOWN_ACTION
+ );
+});
diff --git a/extensions/permissions/test/unit/test_permmanager_subdomains.js b/extensions/permissions/test/unit/test_permmanager_subdomains.js
new file mode 100644
index 0000000000..4d30372332
--- /dev/null
+++ b/extensions/permissions/test/unit/test_permmanager_subdomains.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getPrincipalFromURI(aURI) {
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI(aURI);
+ return ssm.createContentPrincipal(uri, {});
+}
+
+function run_test() {
+ var pm = Services.perms;
+
+ // Adds a permission to a sub-domain. Checks if it is working.
+ let sub1Principal = getPrincipalFromURI("http://sub1.example.com");
+ pm.addFromPrincipal(sub1Principal, "test/subdomains", pm.ALLOW_ACTION, 0, 0);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+
+ // A sub-sub-domain should get the permission.
+ let subsubPrincipal = getPrincipalFromURI("http://sub.sub1.example.com");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+
+ // Another sub-domain shouldn't get the permission.
+ let sub2Principal = getPrincipalFromURI("http://sub2.example.com");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Remove current permissions.
+ pm.removeFromPrincipal(sub1Principal, "test/subdomains");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // Adding the permission to the main domain. Checks if it is working.
+ let mainPrincipal = getPrincipalFromURI("http://example.com");
+ pm.addFromPrincipal(mainPrincipal, "test/subdomains", pm.ALLOW_ACTION, 0, 0);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+
+ // All sub-domains should have the permission now.
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+
+ // Remove current permissions.
+ pm.removeFromPrincipal(mainPrincipal, "test/subdomains");
+ Assert.equal(
+ pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+
+ // A sanity check that the previous implementation wasn't passing...
+ let crazyPrincipal = getPrincipalFromURI("http://com");
+ pm.addFromPrincipal(crazyPrincipal, "test/subdomains", pm.ALLOW_ACTION, 0, 0);
+ Assert.equal(
+ pm.testPermissionFromPrincipal(crazyPrincipal, "test/subdomains"),
+ pm.ALLOW_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+ Assert.equal(
+ pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"),
+ pm.UNKNOWN_ACTION
+ );
+}
diff --git a/extensions/permissions/test/unit/xpcshell.ini b/extensions/permissions/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..07ddf21953
--- /dev/null
+++ b/extensions/permissions/test/unit/xpcshell.ini
@@ -0,0 +1,59 @@
+[DEFAULT]
+head = head.js
+
+[test_permmanager_default_pref.js]
+[test_permmanager_defaults.js]
+[test_permmanager_expiration.js]
+skip-if =
+ win10_2004 # Bug 1718292
+ win11_2009 # Bug 1797751
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[test_permmanager_getAllByTypes.js]
+[test_permmanager_getAllByTypeSince.js]
+[test_permmanager_getAllForPrincipal.js]
+[test_permmanager_getAllWithTypePrefix.js]
+[test_permmanager_getPermissionObject.js]
+[test_permmanager_notifications.js]
+[test_permmanager_removeall.js]
+[test_permmanager_removebytype.js]
+[test_permmanager_removebytypesince.js]
+[test_permmanager_removesince.js]
+[test_permmanager_load_invalid_entries.js]
+skip-if = debug == true
+[test_permmanager_idn.js]
+[test_permmanager_subdomains.js]
+[test_permmanager_local_files.js]
+[test_permmanager_cleardata.js]
+[test_permmanager_removepermission.js]
+[test_permmanager_matchesuri.js]
+[test_permmanager_matches.js]
+[test_permmanager_migrate_4-7.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_5-7a.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_5-7b.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_6-7a.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_6-7b.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_4-7_no_history.js]
+skip-if =
+ apple_silicon || toolkit == 'android' # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+[test_permmanager_migrate_7-8.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_9-10.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_10-11.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_migrate_11-12.js]
+skip-if = toolkit == 'android' # Android doesn't use places
+[test_permmanager_oa_strip.js]
+[test_permmanager_site_scope.js]
+[test_permmanager_remove_add_update.js]
+skip-if = win10_2004 && bits == 64 # Bug 1718292
+[test_permmanager_ipc.js]
+# This test is meant to run on a multi process mode
+# and with file urls loaded in their own child process.
+skip-if = !e10s
+firefox-appdir = browser