Bug 1337056 - Part 2: Replace the synchronous ReadPermissions API with async APIs, r=baku
authorMichael Layzell <michael@thelayzells.com>
Thu, 02 Mar 2017 16:53:33 -0500
changeset 348694 215ca1b54ff1d363624d81460fe9a7e95d8e4ffd
parent 348693 ff6d2ebc113fe5fc3a1f91aba8236c9dfba5ea65
child 348695 c2b8ead5376b5f5a3689e4faef9d814c27189ab6
push id31533
push userkwierso@gmail.com
push dateTue, 21 Mar 2017 23:08:53 +0000
treeherdermozilla-central@8744e9f8eb99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1337056
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1337056 - Part 2: Replace the synchronous ReadPermissions API with async APIs, r=baku These APIs are intended to use the mechanism defined in Part 1. Part 3 implements the usage of these APIs to synchronize permissions. MozReview-Commit-ID: HNKyDPtoaHl
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
extensions/cookie/nsPermissionManager.cpp
extensions/cookie/nsPermissionManager.h
netwerk/base/nsIPermissionManager.idl
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3256,11 +3256,21 @@ ContentChild::AsyncOpenAnonymousTemporar
   }
 
   // Remember the association with the callback.
   MOZ_ASSERT(!mPendingAnonymousTemporaryFiles.Get(newID));
   mPendingAnonymousTemporaryFiles.LookupOrAdd(newID, aCallback);
   return NS_OK;
 }
 
+mozilla::ipc::IPCResult
+ContentChild::RecvSetPermissionsWithKey(const nsCString& aPermissionKey,
+                                        nsTArray<IPC::Permission>&& aPerms)
+{
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    services::GetPermissionManager();
+  permissionManager->SetPermissionsWithKey(aPermissionKey, aPerms);
+
+  return IPC_OK();
+}
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -584,16 +584,20 @@ public:
   virtual mozilla::ipc::IPCResult
   RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
                                 const StructuredCloneData& aInitialData,
                                 nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
 
   virtual mozilla::ipc::IPCResult
   RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override;
 
+  mozilla::ipc::IPCResult
+  RecvSetPermissionsWithKey(const nsCString& aPermissionKey,
+                            nsTArray<IPC::Permission>&& aPerms) override;
+
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   bool
   SendGetA11yContentId();
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
   // Get a reference to the font family list passed from the chrome process,
   // for use during gfx initialization.
   InfallibleTArray<mozilla::dom::FontFamilyListEntry>&
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2459,67 +2459,16 @@ ContentParent::RecvReadFontList(Infallib
 {
 #ifdef ANDROID
   gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue);
 #endif
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions)
-{
-#ifdef MOZ_PERMISSIONS
-  nsCOMPtr<nsIPermissionManager> permissionManagerIface =
-    services::GetPermissionManager();
-  nsPermissionManager* permissionManager =
-    static_cast<nsPermissionManager*>(permissionManagerIface.get());
-  MOZ_ASSERT(permissionManager,
-             "We have no permissionManager in the Chrome process !");
-
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
-  MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!");
-  while(1) {
-    bool hasMore;
-    enumerator->HasMoreElements(&hasMore);
-    if (!hasMore)
-      break;
-
-    nsCOMPtr<nsISupports> supp;
-    enumerator->GetNext(getter_AddRefs(supp));
-    nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);
-
-    nsCOMPtr<nsIPrincipal> principal;
-    perm->GetPrincipal(getter_AddRefs(principal));
-    nsCString origin;
-    if (principal) {
-      principal->GetOrigin(origin);
-    }
-    nsCString type;
-    perm->GetType(type);
-    uint32_t capability;
-    perm->GetCapability(&capability);
-    uint32_t expireType;
-    perm->GetExpireType(&expireType);
-    int64_t expireTime;
-    perm->GetExpireTime(&expireTime);
-
-    aPermissions->AppendElement(IPC::Permission(origin, type,
-                                                capability, expireType,
-                                                expireTime));
-  }
-
-  // Ask for future changes
-  mSendPermissionUpdates = true;
-#endif
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
                                 const bool& aIsPrivateData,
                                 const IPC::Principal& aRequestingPrincipal,
                                 const int32_t& aWhichClipboard)
 {
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
   NS_ENSURE_SUCCESS(rv, IPC_OK());
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -921,18 +921,16 @@ private:
 
   virtual bool
   DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
 
   virtual mozilla::ipc::IPCResult RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) override;
 
   virtual mozilla::ipc::IPCResult RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) override;
 
-  virtual mozilla::ipc::IPCResult RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) override;
-
   virtual mozilla::ipc::IPCResult RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
                                                    const bool& aIsPrivateData,
                                                    const IPC::Principal& aRequestingPrincipal,
                                                    const int32_t& aWhichClipboard) override;
 
   virtual mozilla::ipc::IPCResult RecvGetClipboard(nsTArray<nsCString>&& aTypes,
                                                    const int32_t& aWhichClipboard,
                                                    IPCDataTransfer* aDataTransfer) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -588,16 +588,18 @@ child:
     async Deactivate(PBrowser aTab);
 
     async ParentActivated(PBrowser aTab, bool aActivated);
 
     async PParentToChildStream();
 
     async ProvideAnonymousTemporaryFile(uint64_t aID, FileDescOrError aFD);
 
+    async SetPermissionsWithKey(nsCString aPermissionKey, Permission[] aPermissions);
+
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId)
@@ -780,19 +782,16 @@ parent:
     async RemoveGeolocationListener();
     async SetGeolocationHigherAccuracy(bool enable);
 
     async ConsoleMessage(nsString message);
     async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                       uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
                       nsCString category);
 
-    // nsIPermissionManager messages
-    sync ReadPermissions() returns (Permission[] permissions);
-
     // Places the items within dataTransfer on the clipboard.
     async SetClipboard(IPCDataTransfer aDataTransfer,
                        bool aIsPrivateData,
                        Principal aRequestingPrincipal,
                        int32_t aWhichClipboard);
 
     // Given a list of supported types, returns the clipboard data for the
     // first type that matches.
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -38,42 +38,24 @@
 #include "nsIConsoleService.h"
 #include "nsINavHistoryService.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIObserverService.h"
 
 static nsPermissionManager *gPermissionManager = nullptr;
 
 using mozilla::dom::ContentParent;
-using mozilla::dom::ContentChild;
 using mozilla::Unused; // ha!
 
 static bool
 IsChildProcess()
 {
   return XRE_IsContentProcess();
 }
 
-/**
- * @returns The child process object, or if we are not in the child
- *          process, nullptr.
- */
-static ContentChild*
-ChildProcess()
-{
-  if (IsChildProcess()) {
-    ContentChild* cpc = ContentChild::GetSingleton();
-    if (!cpc)
-      MOZ_CRASH("Content Process is nullptr!");
-    return cpc;
-  }
-
-  return nullptr;
-}
-
 static void
 LogToConsole(const nsAString& aMsg)
 {
   nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
   if (!console) {
     NS_WARNING("Failed to log message to console.");
     return;
   }
@@ -793,18 +775,19 @@ 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);
 
   if (IsChildProcess()) {
-    // Stop here; we don't need the DB in the child process
-    return FetchPermissions();
+    // 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();
   if (observerService) {
     observerService->AddObserver(this, "profile-before-change", true);
     observerService->AddObserver(this, "profile-do-change", true);
   }
@@ -2904,29 +2887,97 @@ nsPermissionManager::UpdateExpireTime(ns
   if (perm.mExpireType == EXPIRE_TIME) {
     perm.mExpireTime = aPersistentExpireTime;
   } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) {
     perm.mExpireTime = aSessionExpireTime;
   }
   return NS_OK;
 }
 
-nsresult
-nsPermissionManager::FetchPermissions() {
-  MOZ_ASSERT(IsChildProcess(), "FetchPermissions can only be invoked in child process");
-  // Get the permissions from the parent process
-  InfallibleTArray<IPC::Permission> perms;
-  ChildProcess()->SendReadPermissions(&perms);
-
-  for (uint32_t i = 0; i < perms.Length(); i++) {
-    const IPC::Permission &perm = perms[i];
-
+NS_IMETHODIMP
+nsPermissionManager::GetPermissionsWithKey(const nsACString& aPermissionKey,
+                                           nsTArray<IPC::Permission>& aPerms)
+{
+  aPerms.Clear();
+  if (NS_WARN_IF(XRE_IsContentProcess())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
+    PermissionHashKey* entry = iter.Get();
+
+    // XXX: Is it worthwhile to have a shortcut Origin->Key implementation? as
+    // we could implement this without creating a codebase principal.
+
+    // Fetch the principal for the given origin.
+    nsCOMPtr<nsIPrincipal> principal;
+    nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin,
+                                         getter_AddRefs(principal));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    // Get the permission key and make sure that it matches the aPermissionKey
+    // passed in.
+    nsAutoCString permissionKey;
+    GetKeyForPrincipal(principal, permissionKey);
+
+    if (permissionKey != aPermissionKey) {
+      continue;
+    }
+
+    for (const auto& permEntry : entry->GetPermissions()) {
+      // Given how "default" permissions work and the possibility of them being
+      // overridden with UNKNOWN_ACTION, we might see this value here - but we
+      // do not want to send it to the content process.
+      if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
+        continue;
+      }
+
+      aPerms.AppendElement(IPC::Permission(entry->GetKey()->mOrigin,
+                                           mTypeArray.ElementAt(permEntry.mType),
+                                           permEntry.mPermission,
+                                           permEntry.mExpireType,
+                                           permEntry.mExpireTime));
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPermissionManager::SetPermissionsWithKey(const nsACString& aPermissionKey,
+                                           nsTArray<IPC::Permission>& aPerms)
+{
+  if (NS_WARN_IF(XRE_IsParentProcess())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // Record that we have seen the permissions with the given permission key.
+  if (NS_WARN_IF(mAvailablePermissionKeys.Contains(aPermissionKey))) {
+    // NOTE: We shouldn't be sent two InitializePermissionsWithKey for the same
+    // key, but it's possible.
+    return NS_OK;
+  }
+  mAvailablePermissionKeys.PutEntry(aPermissionKey);
+
+  // Add the permissions locally to our process
+  for (IPC::Permission& perm : aPerms) {
     nsCOMPtr<nsIPrincipal> principal;
     nsresult rv = GetPrincipalFromOrigin(perm.origin, getter_AddRefs(principal));
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+#ifdef DEBUG
+    nsAutoCString permissionKey;
+    GetKeyForPrincipal(principal, permissionKey);
+    MOZ_ASSERT(permissionKey == aPermissionKey,
+               "The permission keys which were sent over should match!");
+#endif
 
     // The child process doesn't care about modification times - it neither
     // reads nor writes, nor removes them based on the date - so 0 (which
     // will end up as now()) is fine.
     uint64_t modificationTime = 0;
     AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
                 perm.expireTime, modificationTime, eNotify, eNoDBOperation,
                 true /* ignoreSessionPermissions */);
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -271,36 +271,33 @@ private:
                        int64_t aModificationTime);
 
   /**
    * This method removes all permissions modified after the specified time.
    */
   nsresult
   RemoveAllModifiedSince(int64_t aModificationTime);
 
-  /**
-   * Retrieve permissions from chrome process.
-   */
-  nsresult
-  FetchPermissions();
-
   nsCOMPtr<mozIStorageConnection> mDBConn;
   nsCOMPtr<mozIStorageAsyncStatement> mStmtInsert;
   nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete;
   nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate;
 
   bool mMemoryOnlyDB;
 
   nsTHashtable<PermissionHashKey> mPermissionTable;
   // a unique, monotonically increasing id used to identify each database entry
   int64_t                      mLargestID;
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
+  // The base domains which have their permissions loaded in the current process.
+  nsTHashtable<nsCStringHashKey> mAvailablePermissionKeys;
+
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.
   bool mIsShuttingDown;
 
   friend class DeleteFromMozHostListener;
   friend class CloseDatabaseListener;
 };
 
--- a/netwerk/base/nsIPermissionManager.idl
+++ b/netwerk/base/nsIPermissionManager.idl
@@ -32,17 +32,25 @@
 
 interface nsIURI;
 interface nsIObserver;
 interface nsIPrincipal;
 interface mozIDOMWindow;
 interface nsIPermission;
 interface nsISimpleEnumerator;
 
-[scriptable, uuid(4dcb3851-eba2-4e42-b236-82d2596fca22)]
+%{ C++
+namespace IPC {
+struct Permission;
+}
+#include "nsTArrayForwardDeclare.h"
+%}
+[ref] native IPCPermissionArrayRef(nsTArray<IPC::Permission>);
+
+[scriptable, builtinclass, uuid(4dcb3851-eba2-4e42-b236-82d2596fca22)]
 interface nsIPermissionManager : nsISupports
 {
   /**
    * Predefined return values for the testPermission method and for
    * the permission param of the add method
    * NOTE: UNKNOWN_ACTION (0) is reserved to represent the
    * default permission when no entry is found for a host, and
    * should not be used by consumers to indicate otherwise.
@@ -268,15 +276,49 @@ interface nsIPermissionManager : nsISupp
    *                           Jan 1 1970 0:00:00), if it is currently
    *                           EXPIRE_TIME.
    */
   void updateExpireTime(in nsIPrincipal principal,
                         in string type,
                         in boolean exactHost,
                         in uint64_t sessionExpireTime,
                         in uint64_t persistentExpireTime);
+
+  /**
+   * The content process doesn't have access to every permission. Instead, when
+   * LOAD_DOCUMENT_URI channels for http://, https://, and ftp:// URIs are
+   * opened, the permissions for those channels are sent down to the content
+   * process before the OnStartRequest message. Permissions for principals with
+   * other schemes are sent down at process startup.
+   *
+   * Permissions are keyed and grouped by "Permission Key"s.
+   * `nsPermissionManager::GetKeyForPrincipal` provides the mechanism for
+   * determining the permission key for a given principal.
+   *
+   * This method may only be called in the parent process. It fills the nsTArray
+   * argument with the IPC::Permission objects which have a matching permission
+   * key.
+   *
+   * @param permissionKey  The key to use to find the permissions of interest.
+   * @param perms  An array which will be filled with the permissions which
+   *               match the given permission key.
+   */
+  void getPermissionsWithKey(in ACString permissionKey, out IPCPermissionArrayRef perms);
+
+  /**
+   * See `nsIPermissionManager::GetPermissionsWithKey` for more info on
+   * Permission keys.
+   *
+   * `SetPermissionsWithKey` may only be called in the Child process, and
+   * initializes the permission manager with the permissions for a given
+   * Permission key. marking permissions with that key as avaliable.
+   *
+   * @param permissionKey  The key for the permissions which have been sent over.
+   * @param perms  An array with the permissions which match the given key.
+   */
+  void setPermissionsWithKey(in ACString permissionKey, in IPCPermissionArrayRef perms);
 };
 
 %{ C++
 #define NS_PERMISSIONMANAGER_CONTRACTID "@mozilla.org/permissionmanager;1"
 
 #define PERM_CHANGE_NOTIFICATION "perm-changed"
 %}