Bug 1379560 - Part 1 - Add a default permission pref in the permission manager. r=mystor,Paolo draft
authorJohann Hofmann <jhofmann@mozilla.com>
Mon, 10 Jul 2017 23:13:43 +0200
changeset 681934 9fbcfc740a85c02cf4245956e69ae13c8f90b5ab
parent 681681 7b75416fb54c6733b7403e340457007658c42c14
child 681935 2c8da24f849cee53e17be8897c0b320ca9e39e7e
push id84952
push userbmo:jhofmann@mozilla.com
push dateTue, 17 Oct 2017 22:01:10 +0000
reviewersmystor, Paolo
bugs1379560
milestone58.0a1
Bug 1379560 - Part 1 - Add a default permission pref in the permission manager. r=mystor,Paolo This patch enables support for setting prefs with the pattern permissions.default.* to provide a custom default permission for arbitrary permission types in nsPermissionManager. The previous default of UNKNOWN_ACTION is honored if no pref is set. A default value is provided if no permission entry can be found in the db. Accordingly, the patch does not affect the behavior of functions that return permission objects from the db such as GetPermissionObject, which returns null if no entry was found. MozReview-Commit-ID: 3JECI6kXqGf
browser/app/profile/firefox.js
extensions/cookie/nsPermissionManager.cpp
extensions/cookie/nsPermissionManager.h
extensions/cookie/test/unit/test_permmanager_default_pref.js
extensions/cookie/test/unit/xpcshell.ini
netwerk/base/nsIPermissionManager.idl
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -415,16 +415,23 @@ pref("browser.search.widget.inNavBar", f
 pref("browser.search.reset.enabled", true);
 #endif
 
 pref("browser.sessionhistory.max_entries", 50);
 
 // Built-in default permissions.
 pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");
 
+// Set default fallback values for site permissions we want
+// the user to be able to globally change.
+pref("permissions.default.camera", 0);
+pref("permissions.default.microphone", 0);
+pref("permissions.default.geo", 0);
+pref("permissions.default.desktop-notification", 0);
+
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
 
 // handle external links (i.e. links opened from a different application)
 // default: use browser.link.open_newwindow
 // 1-3: see browser.link.open_newwindow for interpretation
 pref("browser.link.open_newwindow.override.external", -1);
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -124,16 +124,41 @@ static const char* kPreloadPermissions[]
   "csp_report",
   "xslt",
   "beacon",
   "fetch",
   "image",
   "manifest"
 };
 
+// A list of permissions that can have a fallback default permission
+// set under the permissions.default.* pref.
+static const char* kPermissionsWithDefaults[] = {
+  "camera",
+  "microphone",
+  "geo",
+  "desktop-notification"
+};
+
+// NOTE: nullptr can be passed as aType - if it is this function will return
+// "false" unconditionally.
+bool
+HasDefaultPref(const char* aType)
+{
+  if (aType) {
+    for (const char* perm : kPermissionsWithDefaults) {
+      if (!strcmp(aType, perm)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 // NOTE: nullptr can be passed as aType - if it is this function will return
 // "false" unconditionally.
 bool
 IsPreloadPermission(const char* aType)
 {
   if (aType) {
     for (uint32_t i = 0; i < mozilla::ArrayLength(kPreloadPermissions); ++i) {
       if (!strcmp(aType, kPreloadPermissions[i])) {
@@ -928,16 +953,23 @@ nsPermissionManager::GetXPCOMSingleton()
 
 nsresult
 nsPermissionManager::Init()
 {
   // If the 'permissions.memory_only' pref is set to true, then don't write any
   // permission settings to disk, but keep them in a memory-only database.
   mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
 
+  nsresult rv;
+  nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = prefService->GetBranch("permissions.default.", getter_AddRefs(mDefaultPrefBranch));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (IsChildProcess()) {
     // Stop here; we don't need the DB in the child process. Instead we will be
     // sent permissions as we need them by our parent process.
     return NS_OK;
   }
 
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
@@ -2227,16 +2259,27 @@ nsPermissionManager::CommonTestPermissio
   if (aPrincipal && nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     *aPermission = nsIPermissionManager::ALLOW_ACTION;
     return NS_OK;
   }
 
   // Set the default.
   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
 
+  // For some permissions, query the default from a pref. We want to avoid
+  // doing this for all permissions so that permissions can opt into having
+  // the pref lookup overhead on each call.
+  if (HasDefaultPref(aType)) {
+    int32_t defaultPermission = nsIPermissionManager::UNKNOWN_ACTION;
+    nsresult rv = mDefaultPrefBranch->GetIntPref(aType, &defaultPermission);
+    if (NS_SUCCEEDED(rv)) {
+      *aPermission = defaultPermission;
+    }
+  }
+
   // For expanded principals, we want to iterate over the whitelist and see
   // if the permission is granted for any of them.
   auto* basePrin = BasePrincipal::Cast(aPrincipal);
   if (basePrin && basePrin->Is<ExpandedPrincipal>()) {
     auto ep = basePrin->As<ExpandedPrincipal>();
     for (auto& prin : ep->WhiteList()) {
       uint32_t perm;
       nsresult rv = CommonTestPermission(prin, aType, &perm,
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -11,16 +11,17 @@
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsPermission.h"
+#include "nsIPrefBranch.h"
 #include "nsHashKeys.h"
 #include "nsCOMArray.h"
 #include "nsDataHashtable.h"
 #include "nsIRunnable.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/MozPromise.h"
 
 namespace mozilla {
@@ -384,16 +385,18 @@ private:
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.
   bool mIsShuttingDown;
 
+  nsCOMPtr<nsIPrefBranch> mDefaultPrefBranch;
+
   friend class DeleteFromMozHostListener;
   friend class CloseDatabaseListener;
 };
 
 // {4F6B5E00-0C36-11d5-A535-0010A401EB10}
 #define NS_PERMISSIONMANAGER_CID \
 { 0x4f6b5e00, 0xc36, 0x11d5, { 0xa5, 0x35, 0x0, 0x10, 0xa4, 0x1, 0xeb, 0x10 } }
 
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_default_pref.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+  let uri = Services.io.newURI("https://example.org");
+
+  // Check that without a pref the default return value is UNKNOWN.
+  do_check_eq(Services.perms.testPermission(uri, "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);
+  do_check_eq(Services.perms.testPermission(uri, "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.
+  do_check_null(Services.perms.getPermissionObjectForURI(uri, "camera", false));
+
+  // Check that other permissions still return UNKNOWN.
+  do_check_eq(Services.perms.testPermission(uri, "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);
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.ALLOW_ACTION);
+
+  // Check that the preference is ignored if there is a value.
+  Services.perms.add(uri, "camera", Services.perms.DENY_ACTION);
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.DENY_ACTION);
+  do_check_true(Services.perms.getPermissionObjectForURI(uri, "camera", false) != null);
+
+  // The preference should be honored again, after resetting the permissions.
+  Services.perms.removeAll();
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.ALLOW_ACTION);
+
+  // Should be UNKNOWN after clearing the pref.
+  Services.prefs.clearUserPref("permissions.default.camera");
+  do_check_eq(Services.perms.testPermission(uri, "camera"), Services.perms.UNKNOWN_ACTION);
+}
--- a/extensions/cookie/test/unit/xpcshell.ini
+++ b/extensions/cookie/test/unit/xpcshell.ini
@@ -11,16 +11,17 @@ skip-if = true # Bug 863738
 [test_cookies_privatebrowsing.js]
 [test_cookies_profile_close.js]
 [test_cookies_read.js]
 [test_cookies_sync_failure.js]
 [test_cookies_thirdparty.js]
 [test_cookies_thirdparty_session.js]
 [test_domain_eviction.js]
 [test_eviction.js]
+[test_permmanager_default_pref.js]
 [test_permmanager_defaults.js]
 [test_permmanager_expiration.js]
 [test_permmanager_getAllForURI.js]
 [test_permmanager_getPermissionObject.js]
 [test_permmanager_notifications.js]
 [test_permmanager_removeall.js]
 [test_permmanager_removesince.js]
 [test_permmanager_removeforapp.js]
--- a/netwerk/base/nsIPermissionManager.idl
+++ b/netwerk/base/nsIPermissionManager.idl
@@ -158,61 +158,73 @@ interface nsIPermissionManager : nsISupp
 
   /**
    * Clear all permission information added since the specified time.
    */
   void removeAllSince(in int64_t since);
 
   /**
    * Test whether a website has permission to perform the given action.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    * @param uri     the uri to be tested
    * @param type    a case-sensitive ASCII string, identifying the consumer
    * @param return  see add(), param permission. returns UNKNOWN_ACTION when
    *                there is no stored permission for this uri and / or type.
    */
   uint32_t testPermission(in nsIURI uri,
                           in string type);
 
   /**
    * Test whether the principal has the permission to perform a given action.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testPermissionFromPrincipal(in nsIPrincipal principal,
                                        in string type);
 
   /**
    * Test whether the principal associated with the window's document has the
    * permission to perform a given action.  System principals will always
    * have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testPermissionFromWindow(in mozIDOMWindow window,
                                     in string type);
 
   /**
    * Test whether a website has permission to perform the given action.
    * This requires an exact hostname match, subdomains are not a match.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    * @param uri     the uri to be tested
    * @param type    a case-sensitive ASCII string, identifying the consumer
    * @param return  see add(), param permission. returns UNKNOWN_ACTION when
    *                there is no stored permission for this uri and / or type.
    */
   uint32_t testExactPermission(in nsIURI uri,
                                in string type);
 
   /**
    * See testExactPermission() above.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    */
   uint32_t testExactPermissionFromPrincipal(in nsIPrincipal principal,
                                             in string type);
 
   /**
    * Test whether a website has permission to perform the given action
    * ignoring active sessions.
    * System principals will always have permissions granted.
+   * This function will perform a pref lookup to permissions.default.<type>
+   * if the specific permission type is part of the whitelist for that functionality.
    *
    * @param principal the principal
    * @param type      a case-sensitive ASCII string, identifying the consumer
    * @param return    see add(), param permission. returns UNKNOWN_ACTION when
    *                  there is no stored permission for this uri and / or type.
    */
   uint32_t testExactPermanentPermission(in nsIPrincipal principal,
                                         in string type);