Bug 1341506 - Part 2.1: Refined IPC. draft
authorHenry Chang <hchang@mozilla.com>
Mon, 27 Feb 2017 20:58:36 +0800
changeset 489958 05194f7f9407c84473f21071f4f5230b39f79dc9
parent 489957 1c83c11ea0da03da16fd65d25258018d36072874
child 547135 3688923c0d315064395bcbc09d3b6bd9ae0796d1
push id46962
push userhchang@mozilla.com
push dateMon, 27 Feb 2017 13:00:05 +0000
bugs1341506
milestone54.0a1
Bug 1341506 - Part 2.1: Refined IPC. MozReview-Commit-ID: KYwB5v7J7QR
dom/ipc/PURLClassifier.ipdl
dom/ipc/URLClassifierChild.cpp
dom/ipc/URLClassifierChild.h
dom/ipc/URLClassifierParent.cpp
dom/ipc/URLClassifierParent.h
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/nsUrlClassifierDBService.h
--- a/dom/ipc/PURLClassifier.ipdl
+++ b/dom/ipc/PURLClassifier.ipdl
@@ -21,25 +21,29 @@ union MaybeInfo {
 };
 
 sync protocol PURLClassifier
 {
   manager PContent;
 
 parent:
   // Moved from PContent.ipdl.
-  sync Classify(Principal principal, bool useTrackingProtection)
+  sync Classify(Principal principal, bool useTrackingProtection, uint32_t callbackId)
     returns (bool success);
 
   // Moved from PContent.ipdl.
   // TODO: Remove and relpace with the async version.
   // See Bug 1342333.
   sync ClassifyLocal(URIParams uri, nsCString tables)
     returns (nsresult rv, nsCString[] results);
 
-  async AsyncClassifyLocal(URIParams uri, nsCString tables);
+  async AsyncClassifyLocal(URIParams uri, nsCString tables, uint32_t callbackId);
+
+  async __delete__();
 
 child:
-  async __delete__(MaybeInfo info, nsresult errorCode);
+  async OnClassifyComplete(MaybeInfo info, nsresult errorCode, uint32_t callbackId);
+
+
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/URLClassifierChild.cpp
+++ b/dom/ipc/URLClassifierChild.cpp
@@ -5,20 +5,75 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "URLClassifierChild.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIURI.h"
 
 using namespace mozilla::dom;
 
+URLClassifierChild::~URLClassifierChild()
+{
+  if (mIPCOpen) {
+    Send__delete__(this);
+  }
+}
+
 mozilla::ipc::IPCResult
-URLClassifierChild::Recv__delete__(const MaybeInfo& aInfo,
-                                   const nsresult& aResult)
+URLClassifierChild::RecvOnClassifyComplete(const MaybeInfo& aInfo,
+                                           const nsresult& aResult,
+                                           const uint32_t& aCallbackId)
 {
-  MOZ_ASSERT(mCallback);
-  if (aInfo.type() == MaybeInfo::TClassifierInfo) {
-    mCallback->OnClassifyComplete(aResult, aInfo.get_ClassifierInfo().list(),
-                                  aInfo.get_ClassifierInfo().provider(),
-                                  aInfo.get_ClassifierInfo().prefix());
+  // Look up the actual callback by the given callback id.
+  RefPtr<nsIURIClassifierCallback> callback = mCallbacks.Get(aCallbackId);
+  if (!callback) {
+    // Junk callback id is given.
+    return IPC_OK();
   }
+  mCallbacks.Remove(aCallbackId);
+
+  callback->OnClassifyComplete(aResult,
+                               aInfo.get_ClassifierInfo().list(),
+                               aInfo.get_ClassifierInfo().provider(),
+                               aInfo.get_ClassifierInfo().prefix());
+
   return IPC_OK();
 }
+
+bool
+URLClassifierChild::Classify(const Principal& principal,
+                             const bool& useTrackingProtection,
+                             nsIURIClassifierCallback* callback,
+                             bool* success)
+{
+  if (!mIPCOpen) {
+    return false;
+  }
+  uint32_t callbackId = GenNextCallbackId(callback);
+  if (!SendClassify(principal, useTrackingProtection, callbackId, success)) {
+    mCallbacks.Remove(callbackId);
+    return false;
+  }
+  return true;
+}
+
+bool
+URLClassifierChild::AsyncClassifyLocal(const URIParams& uri,
+                                       const nsCString& tables,
+                                       nsIURIClassifierCallback* callback)
+{
+  if (!mIPCOpen) {
+    return false;
+  }
+  uint32_t callbackId = GenNextCallbackId(callback);
+  if (!SendAsyncClassifyLocal(uri, tables, callbackId)) {
+    return false;
+  }
+  return true;
+}
+
+uint32_t
+URLClassifierChild::GenNextCallbackId(nsIURIClassifierCallback* aCallback)
+{
+  uint32_t callbackId = mCurCallbackId++;
+  mCallbacks.Put(callbackId, aCallback);
+  return callbackId;
+}
\ No newline at end of file
--- a/dom/ipc/URLClassifierChild.h
+++ b/dom/ipc/URLClassifierChild.h
@@ -4,34 +4,51 @@
  * 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/. */
 
 #ifndef mozilla_dom_URLClassifierChild_h
 #define mozilla_dom_URLClassifierChild_h
 
 #include "mozilla/dom/PURLClassifierChild.h"
 #include "nsIURIClassifier.h"
+#include "nsDataHashtable.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLClassifierChild : public PURLClassifierChild
 {
  public:
   URLClassifierChild() = default;
 
-  void SetCallback(nsIURIClassifierCallback* aCallback)
-  {
-    mCallback = aCallback;
-  }
-  mozilla::ipc::IPCResult Recv__delete__(const MaybeInfo& aInfo,
-                                         const nsresult& aResult) override;
+  bool Classify(const Principal& principal,
+                const bool& useTrackingProtection,
+                nsIURIClassifierCallback* callback,
+                bool* success);
+
+  bool AsyncClassifyLocal(const URIParams& uri,
+                          const nsCString& tables,
+                          nsIURIClassifierCallback* callback);
+
+  mozilla::ipc::IPCResult
+  RecvOnClassifyComplete(const MaybeInfo& aInfo,
+                         const nsresult& aResult,
+                         const uint32_t& aCallbackId) override;
+
+  ~URLClassifierChild();
 
  private:
-  ~URLClassifierChild() = default;
+   void ActorDestroy (ActorDestroyReason aWhy) override { mIPCOpen = false; }
+
+   uint32_t GenNextCallbackId(nsIURIClassifierCallback* aCallback);
 
-  nsCOMPtr<nsIURIClassifierCallback> mCallback;
+   nsDataHashtable<nsUint32HashKey,
+                   RefPtr<nsIURIClassifierCallback>> mCallbacks;
+
+   uint32_t mCurCallbackId = 0;
+
+   bool mIPCOpen = true;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_URLClassifierChild_h
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -7,42 +7,86 @@
 #include "URLClassifierParent.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/Unused.h"
 #include "URIUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-NS_IMPL_ISUPPORTS(URLClassifierParent, nsIURIClassifierCallback)
+NS_IMPL_ISUPPORTS(URLClassifierParent, nsISupports)
+
+namespace {
+
+class MyCallback : public nsIURIClassifierCallback {
+public:
+  MyCallback(uint32_t aId, URLClassifierParent* aClosure)
+    : mId(aId)
+    , mClosure(aClosure)
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIURICLASSIFIERCALLBACK;
+
+private:
+  ~MyCallback() = default;
+
+  uint32_t mId;
+  RefPtr<URLClassifierParent> mClosure;
+};
+
+NS_IMPL_ISUPPORTS(MyCallback, nsIURIClassifierCallback)
+
+NS_IMETHODIMP
+MyCallback::OnClassifyComplete(nsresult aErrorCode,
+                               const nsACString& aList,
+                               const nsACString& aProvider,
+                               const nsACString& aPrefix)
+{
+  ClassifierInfo info;
+  info.list() = aList;
+  info.prefix() = aPrefix;
+  info.provider() = aProvider;
+
+  if (mClosure->IsIPCOpen()) {
+    Unused << mClosure->SendOnClassifyComplete(info, aErrorCode, mId);
+  }
+
+  // What can we do if IPC has been closed?
+
+  return NS_OK;
+}
+
+} // end of unnamed namespace.
 
 mozilla::ipc::IPCResult
 URLClassifierParent::StartClassify(nsIPrincipal* aPrincipal,
                                    bool aUseTrackingProtection,
+                                   const uint32_t& aCallbackId,
                                    bool* aSuccess)
 {
   *aSuccess = false;
   nsresult rv = NS_OK;
   // Note that in safe mode, the URL classifier service isn't available, so we
   // should handle the service not being present gracefully.
   nsCOMPtr<nsIURIClassifier> uriClassifier =
     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (NS_SUCCEEDED(rv)) {
+    RefPtr<MyCallback> cb(new MyCallback(aCallbackId, this));
     rv = uriClassifier->Classify(aPrincipal, aUseTrackingProtection,
-                                 this, aSuccess);
+                                 cb, aSuccess);
   }
   if (NS_FAILED(rv) || !*aSuccess) {
     // We treat the case where we fail to classify and the case where the
     // classifier returns successfully but doesn't perform a lookup as the
-    // classification not yielding any results, so we just kill the child actor
-    // without ever calling out callback in both cases.
+    // classification not yielding any results.
     // This means that code using this in the child process will only get a hit
     // on its callback if some classification actually happens.
     *aSuccess = false;
-    ClassificationFailed();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 URLClassifierParent::RecvClassifyLocal(const URIParams& aURI,
                                        const nsCString& aTables,
                                        nsresult *aRv,
@@ -59,65 +103,38 @@ URLClassifierParent::RecvClassifyLocal(c
     return IPC_FAIL_NO_REASON(this);
   }
   *aRv = uriClassifier->ClassifyLocalWithTables(uri, aTables, *aResults);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 URLClassifierParent::RecvAsyncClassifyLocal(const URIParams& aURI,
-                                            const nsCString& aTables)
+                                            const nsCString& aTables,
+                                            const uint32_t& aCallbackId)
 {
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
     do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
 
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
     if (!uri) {
       return IPC_FAIL_NO_REASON(this);
     }
-    rv = uriClassifier->AsyncClassifyLocalWithTables(uri, aTables, this);
+    RefPtr<MyCallback> cb(new MyCallback(aCallbackId, this));
+    rv = uriClassifier->AsyncClassifyLocalWithTables(uri, aTables, cb);
   }
 
   if (NS_FAILED(rv)) {
-    // We treat the case where we fail to classify and the case where the
-    // classifier returns successfully but doesn't perform a lookup as the
-    // classification not yielding any results, so we just kill the child actor
-    // without ever calling out callback in both cases.
-    // This means that code using this in the child process will only get a hit
-    // on its callback if some classification actually happens.
-    ClassificationFailed();
+    Unused << SendOnClassifyComplete(ClassifierInfo(),
+                                     NS_OK, // Implies not on the list.
+                                     aCallbackId);
   }
   return IPC_OK();
 }
 
-nsresult
-URLClassifierParent::OnClassifyComplete(nsresult aErrorCode,
-                                        const nsACString& aList,
-                                        const nsACString& aProvider,
-                                        const nsACString& aPrefix)
-{
-  if (mIPCOpen) {
-    ClassifierInfo info;
-    info.list() = aList;
-    info.prefix() = aPrefix;
-    info.provider() = aProvider;
-
-    Unused << Send__delete__(this, info, aErrorCode);
-  }
-  return NS_OK;
-}
-
-void
-URLClassifierParent::ClassificationFailed()
-{
-  if (mIPCOpen) {
-    Unused << Send__delete__(this, void_t(), NS_ERROR_FAILURE);
-  }
-}
-
 void
 URLClassifierParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mIPCOpen = false;
 }
--- a/dom/ipc/URLClassifierParent.h
+++ b/dom/ipc/URLClassifierParent.h
@@ -3,59 +3,61 @@
 /* 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/. */
 
 #ifndef mozilla_dom_URLClassifierParent_h
 #define mozilla_dom_URLClassifierParent_h
 
 #include "mozilla/dom/PURLClassifierParent.h"
-#include "nsIURIClassifier.h"
+#include "nsISupports.h"
 
 namespace mozilla {
 namespace dom {
 
-class URLClassifierParent : public nsIURIClassifierCallback,
+class URLClassifierParent : public nsISupports,
                             public PURLClassifierParent
 {
  public:
   URLClassifierParent() = default;
 
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIURICLASSIFIERCALLBACK
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  void ClassificationFailed();
-
   // PURLClassifierParent messages.
   virtual mozilla::ipc::IPCResult
   RecvClassify(const Principal& aPrincipal,
                const bool& aUseTrackingProtection,
+               const uint32_t& aCallbackId,
                bool* aSuccess) override
   {
     nsCOMPtr<nsIPrincipal> principal(aPrincipal);
-    return StartClassify(principal, aUseTrackingProtection, aSuccess);
+    return StartClassify(principal, aUseTrackingProtection, aCallbackId, aSuccess);
   }
 
   virtual mozilla::ipc::IPCResult
   RecvAsyncClassifyLocal(const URIParams& aURI,
-                         const nsCString& aTables) override;
+                         const nsCString& aTables,
+                         const uint32_t& aCallbackId) override;
 
   virtual mozilla::ipc::IPCResult
   RecvClassifyLocal(const URIParams& aURI,
                     const nsCString& aTables,
                     nsresult* aRv,
                     nsTArray<nsCString>* aResults) override;
 
+  bool IsIPCOpen() const { return mIPCOpen; }
+
  private:
   ~URLClassifierParent() = default;
 
   mozilla::ipc::IPCResult StartClassify(nsIPrincipal* aPrincipal,
                                         bool aUseTrackingProtection,
+                                        const uint32_t& aCallbackId,
                                         bool* aSuccess);
 
   bool mIPCOpen = true;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -1382,16 +1382,20 @@ nsUrlClassifierDBService::nsUrlClassifie
  , mCheckPhishing(CHECK_PHISHING_DEFAULT)
  , mCheckBlockedURIs(CHECK_BLOCKED_DEFAULT)
  , mInUpdate(false)
 {
 }
 
 nsUrlClassifierDBService::~nsUrlClassifierDBService()
 {
+  if (XRE_IsContentProcess()) {
+    // XXX: Is this a good point to delete actors?
+    mActor = nullptr;
+  }
   sUrlClassifierDBService = nullptr;
 }
 
 nsresult
 nsUrlClassifierDBService::ReadTablesFromPrefs()
 {
   nsCString allTables;
   nsCString tables;
@@ -1458,16 +1462,19 @@ nsUrlClassifierDBService::Init()
   case GeckoProcessType_Default:
     // The parent process is supported.
     break;
   case GeckoProcessType_Content:
     // In a content process, we simply forward all requests to the parent process,
     // so we can skip the initialization steps here.
     // Note that since we never register an observer, Shutdown() will also never
     // be called in the content process.
+    using namespace mozilla::dom;
+    mActor = static_cast<URLClassifierChild*>
+      (ContentChild::GetSingleton()->SendPURLClassifierConstructor());
     return NS_OK;
   default:
     // No other process type is supported!
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Retrieve all the preferences.
   mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
@@ -1608,25 +1615,23 @@ NS_IMETHODIMP
 nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
                                    bool aTrackingProtectionEnabled,
                                    nsIURIClassifierCallback* c,
                                    bool* result)
 {
   NS_ENSURE_ARG(aPrincipal);
 
   if (XRE_IsContentProcess()) {
-    using namespace mozilla::dom;
-    auto actor = static_cast<URLClassifierChild*>
-      (ContentChild::GetSingleton()->SendPURLClassifierConstructor());
-    if (actor) {
-      actor->SetCallback(c);
-      actor->SendClassify(IPC::Principal(aPrincipal),
-                          aTrackingProtectionEnabled,
-                          result);
+    if (!mActor) {
+      return NS_ERROR_FAILURE;
     }
+    mActor->Classify(IPC::Principal(aPrincipal),
+                     aTrackingProtectionEnabled,
+                     c,
+                     result);
     return NS_OK;
   }
 
   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
 
   if (!(mCheckMalware || mCheckPhishing || aTrackingProtectionEnabled ||
         mCheckBlockedURIs)) {
     *result = false;
@@ -1681,27 +1686,24 @@ nsUrlClassifierDBService::AsyncClassifyL
 {
   MOZ_ASSERT(NS_IsMainThread(), "AsyncClassifyLocalWithTables must be called "
                                 "on main thread");
 
   // Check and deal with content process case.
   if (XRE_IsContentProcess()) {
     using namespace mozilla::dom;
     using namespace mozilla::ipc;
-    auto actor = static_cast<URLClassifierChild*>
-      (ContentChild::GetSingleton()->SendPURLClassifierConstructor());
-    if (!actor) {
+    if (!mActor) {
       return NS_ERROR_FAILURE;
     }
 
     URIParams uri;
     SerializeURI(aURI, uri);
     nsAutoCString tables(aTables);
-    actor->SetCallback(aCallback);
-    actor->SendAsyncClassifyLocal(uri, tables);
+    mActor->AsyncClassifyLocal(uri, tables, aCallback);
     return NS_OK;
   }
 
   if (gShuttingDownThread) {
     return NS_ERROR_ABORT;
   }
 
   using namespace mozilla::Telemetry;
@@ -1770,26 +1772,24 @@ nsUrlClassifierDBService::ClassifyLocalW
   if (gShuttingDownThread) {
     return NS_ERROR_ABORT;
   }
 
   nsresult rv;
   if (XRE_IsContentProcess()) {
     using namespace mozilla::dom;
     using namespace mozilla::ipc;
-    auto actor = static_cast<URLClassifierChild*>
-      (ContentChild::GetSingleton()->SendPURLClassifierConstructor());
-    if (!actor) {
+    if (!mActor) {
       return NS_ERROR_FAILURE;
     }
 
     URIParams uri;
     SerializeURI(aURI, uri);
     nsAutoCString tables(aTables);
-    if (!actor->SendClassifyLocal(uri, tables, &rv, &aTableResults)) {
+    if (!mActor->SendClassifyLocal(uri, tables, &rv, &aTableResults)) {
       return NS_ERROR_FAILURE;
     }
     return rv;
   }
 
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CLASSIFYLOCAL_TIME> timer;
 
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -52,16 +52,21 @@ namespace safebrowsing {
 class Classifier;
 class ProtocolParser;
 class TableUpdate;
 
 nsresult
 TablesToResponse(const nsACString& tables);
 
 } // namespace safebrowsing
+
+namespace dom {
+  class URLClassifierChild;
+}
+
 } // namespace mozilla
 
 // This is a proxy class that just creates a background thread and delagates
 // calls to the background thread.
 class nsUrlClassifierDBService final : public nsIUrlClassifierDBService,
                                        public nsIURIClassifier,
                                        public nsIObserver
 {
@@ -140,16 +145,18 @@ private:
   // The list of tables that can use the default hash completer object.
   nsTArray<nsCString> mGethashTables;
 
   // The list of tables that should never be hash completed.
   nsTArray<nsCString> mDisallowCompletionsTables;
 
   // Thread that we do the updates on.
   static nsIThread* gDbBackgroundThread;
+
+  nsAutoPtr<mozilla::dom::URLClassifierChild> mActor;
 };
 
 class nsUrlClassifierDBServiceWorker final : public nsIUrlClassifierDBService
 {
 public:
   nsUrlClassifierDBServiceWorker();
 
   NS_DECL_THREADSAFE_ISUPPORTS