Bug 1343425 - Supports nsIURIClassifier.asyncClassifyLocalWithTables. draft
authorHenry Chang <hchang@mozilla.com>
Fri, 07 Apr 2017 14:15:16 +0800
changeset 557697 1cae533452487570fc09d0cb20416b0f966f498d
parent 557602 fe88209821169b2b7524f36e6a2fe76b6d08fdef
child 623120 ede04882ceade396a26f0769a496abe79bbaed34
push id52788
push userhchang@mozilla.com
push dateFri, 07 Apr 2017 06:16:12 +0000
bugs1343425, 1353701
milestone55.0a1
Bug 1343425 - Supports nsIURIClassifier.asyncClassifyLocalWithTables. We add a new "on-off" protocol PURLClassifierLocal which calls nsIURIClassifier.asyncClassifyLocalWithTables on construction and calls back on destruction. Pretty much the same design as PURLClassifier. In order to avoid code duplication, the actor implementation is templatized and |MaybeInfo| in PURLClassifier.ipdl is moved around. Test case is included and the custom event target is not in place for labelling. The custom event target will be done in Bug 1353701. MozReview-Commit-ID: IdHYgdnBV7S
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/PURLClassifier.ipdl
dom/ipc/PURLClassifierInfo.ipdlh
dom/ipc/PURLClassifierLocal.ipdl
dom/ipc/URLClassifierChild.cpp
dom/ipc/URLClassifierChild.h
dom/ipc/URLClassifierParent.cpp
dom/ipc/URLClassifierParent.h
dom/ipc/moz.build
testing/specialpowers/content/specialpowersAPI.js
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/tests/mochitest/test_classifier.html
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3138,16 +3138,31 @@ ContentChild::AllocPURLClassifierChild(c
 bool
 ContentChild::DeallocPURLClassifierChild(PURLClassifierChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete aActor;
   return true;
 }
 
+PURLClassifierLocalChild*
+ContentChild::AllocPURLClassifierLocalChild(const URIParams& aUri,
+                                            const nsCString& aTables)
+{
+  return new URLClassifierLocalChild();
+}
+
+bool
+ContentChild::DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  delete aActor;
+  return true;
+}
+
 // The IPC code will call this method asking us to assign an event target to new
 // actors created by the ContentParent.
 already_AddRefed<nsIEventTarget>
 ContentChild::GetConstructedEventTarget(const Message& aMsg)
 {
   // Currently we only set targets for PBrowser.
   if (aMsg.type() != PContent::Msg_PBrowserConstructor__ID) {
     return nullptr;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -605,23 +605,31 @@ public:
 
   // Get a reference to the font family list passed from the chrome process,
   // for use during gfx initialization.
   InfallibleTArray<mozilla::dom::FontFamilyListEntry>&
   SystemFontFamilyList() {
     return mFontFamilies;
   }
 
+  // PURLClassifierChild
   virtual PURLClassifierChild*
   AllocPURLClassifierChild(const Principal& aPrincipal,
                            const bool& aUseTrackingProtection,
                            bool* aSuccess) override;
   virtual bool
   DeallocPURLClassifierChild(PURLClassifierChild* aActor) override;
 
+  // PURLClassifierLocalChild
+  virtual PURLClassifierLocalChild*
+  AllocPURLClassifierLocalChild(const URIParams& aUri,
+                                const nsCString& aTables) override;
+  virtual bool
+  DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor) override;
+
   nsTArray<LookAndFeelInt>&
   LookAndFeelCache() {
     return mLookAndFeelCache;
   }
 
   /**
    * Helper function for protocols that use the GPU process when available.
    * Overrides FatalError to just be a warning when communicating with the
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5153,16 +5153,19 @@ ContentParent::RecvUpdateChildKeyedScala
 
 mozilla::ipc::IPCResult
 ContentParent::RecvRecordChildEvents(nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents)
 {
   TelemetryIPC::RecordChildEvents(GeckoProcessType_Content, aEvents);
   return IPC_OK();
 }
 
+//////////////////////////////////////////////////////////////////
+// PURLClassifierParent
+
 PURLClassifierParent*
 ContentParent::AllocPURLClassifierParent(const Principal& aPrincipal,
                                          const bool& aUseTrackingProtection,
                                          bool* aSuccess)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   *aSuccess = true;
@@ -5195,16 +5198,58 @@ ContentParent::DeallocPURLClassifierPare
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aActor);
 
   RefPtr<URLClassifierParent> actor =
     dont_AddRef(static_cast<URLClassifierParent*>(aActor));
   return true;
 }
 
+//////////////////////////////////////////////////////////////////
+// PURLClassifierLocalParent
+
+PURLClassifierLocalParent*
+ContentParent::AllocPURLClassifierLocalParent(const URIParams& aURI,
+                                              const nsCString& aTables)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<URLClassifierLocalParent> actor = new URLClassifierLocalParent();
+  return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvPURLClassifierLocalConstructor(PURLClassifierLocalParent* aActor,
+                                                  const URIParams& aURI,
+                                                  const nsCString& aTables)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+
+  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+  if (!uri) {
+    NS_WARNING("Failed to DeserializeURI");
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
+  return actor->StartClassify(uri, aTables);
+}
+
+bool
+ContentParent::DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+
+  RefPtr<URLClassifierLocalParent> actor =
+    dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
+  return true;
+}
+
 mozilla::ipc::IPCResult
 ContentParent::RecvClassifyLocal(const URIParams& aURI, const nsCString& aTables,
                                  nsresult *aRv, nsTArray<nsCString>* aResults)
 {
   MOZ_ASSERT(aResults);
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     return IPC_FAIL_NO_REASON(this);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -600,26 +600,36 @@ public:
   RecvGetA11yContentId(uint32_t* aContentId) override;
 
   virtual mozilla::ipc::IPCResult
   RecvA11yHandlerControl(const uint32_t& aPid,
                          const IHandlerControlHolder& aHandlerControl) override;
 
   virtual int32_t Pid() const override;
 
+  // PURLClassifierParent.
   virtual PURLClassifierParent*
   AllocPURLClassifierParent(const Principal& aPrincipal,
                             const bool& aUseTrackingProtection,
                             bool* aSuccess) override;
   virtual mozilla::ipc::IPCResult
   RecvPURLClassifierConstructor(PURLClassifierParent* aActor,
                                 const Principal& aPrincipal,
                                 const bool& aUseTrackingProtection,
                                 bool* aSuccess) override;
 
+  // PURLClassifierLocalParent.
+  virtual PURLClassifierLocalParent*
+  AllocPURLClassifierLocalParent(const URIParams& aURI,
+                                 const nsCString& aTables) override;
+  virtual mozilla::ipc::IPCResult
+  RecvPURLClassifierLocalConstructor(PURLClassifierLocalParent* aActor,
+                                     const URIParams& aURI,
+                                     const nsCString& aTables) override;
+
   virtual bool SendActivate(PBrowserParent* aTab) override
   {
     return PContentParent::SendActivate(aTab);
   }
 
   virtual bool SendDeactivate(PBrowserParent* aTab) override
   {
     return PContentParent::SendDeactivate(aTab);
@@ -627,16 +637,19 @@ public:
 
   virtual bool SendParentActivated(PBrowserParent* aTab,
                                    const bool& aActivated) override
   {
     return PContentParent::SendParentActivated(aTab, aActivated);
   }
 
   virtual bool
+  DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor) override;
+
+  virtual bool
   DeallocPURLClassifierParent(PURLClassifierParent* aActor) override;
 
   virtual mozilla::ipc::IPCResult
   RecvClassifyLocal(const URIParams& aURI,
                     const nsCString& aTables,
                     nsresult* aRv,
                     nsTArray<nsCString>* aResults) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -35,16 +35,17 @@ include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTestShell;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
 include protocol PWebBrowserPersistDocument;
 include protocol PWebrtcGlobal;
 include protocol PPresentation;
 include protocol PURLClassifier;
+include protocol PURLClassifierLocal;
 include protocol PVRManager;
 include protocol PVideoDecoderManager;
 include protocol PFlyWebPublishedServer;
 include DOMTypes;
 include JavaScriptTypes;
 include IPCStream;
 include PTabContext;
 include URIParams;
@@ -296,16 +297,17 @@ nested(upto inside_cpow) sync protocol P
     manages PTestShell;
     manages PJavaScript;
     manages PRemoteSpellcheckEngine;
     manages PWebBrowserPersistDocument;
     manages PWebrtcGlobal;
     manages PPresentation;
     manages PFlyWebPublishedServer;
     manages PURLClassifier;
+    manages PURLClassifierLocal;
 
 both:
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
     // TabChild::BrowserFrameProvideWindow (which happens when the child's
     // content calls window.open()), and the parent creates the PBrowser as part
@@ -727,16 +729,18 @@ parent:
     async PPresentation();
 
     async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params);
 
     sync PURLClassifier(Principal principal, bool useTrackingProtection)
         returns (bool success);
     sync ClassifyLocal(URIParams uri, nsCString tables)
         returns (nsresult rv, nsCString[] results);
+    // The async version of ClassifyLocal.
+    async PURLClassifierLocal(URIParams uri, nsCString tables);
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
     async SetURITitle(URIParams uri, nsString title);
 
     async LoadURIExternal(URIParams uri, PBrowser windowContext);
--- a/dom/ipc/PURLClassifier.ipdl
+++ b/dom/ipc/PURLClassifier.ipdl
@@ -1,28 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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/. */
 
-using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
-
 include protocol PContent;
 include PURLClassifierInfo;
 
 namespace mozilla {
 namespace dom {
 
-union MaybeInfo {
-  ClassifierInfo;
-  void_t;
-};
-
 protocol PURLClassifier
 {
   manager PContent;
 
 child:
   async __delete__(MaybeInfo info, nsresult errorCode);
 };
 
--- a/dom/ipc/PURLClassifierInfo.ipdlh
+++ b/dom/ipc/PURLClassifierInfo.ipdlh
@@ -1,17 +1,24 @@
 /* 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/. */
 
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
 namespace mozilla {
 namespace dom {
 
 struct ClassifierInfo {
   nsCString list;
   nsCString provider;
   nsCString prefix;
 };
 
+union MaybeInfo {
+  ClassifierInfo;
+  void_t;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PURLClassifierLocal.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 protocol PContent;
+include PURLClassifierInfo;
+
+namespace mozilla {
+namespace dom {
+
+protocol PURLClassifierLocal
+{
+  manager PContent;
+
+child:
+  async __delete__(MaybeInfo info, nsresult errorCode);
+};
+
+} // namespace dom
+} // namespace mozilla
deleted file mode 100644
--- a/dom/ipc/URLClassifierChild.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- 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 "URLClassifierChild.h"
-#include "nsComponentManagerUtils.h"
-#include "nsIURI.h"
-
-using namespace mozilla::dom;
-
-mozilla::ipc::IPCResult
-URLClassifierChild::Recv__delete__(const MaybeInfo& aInfo,
-                                   const nsresult& aResult)
-{
-  MOZ_ASSERT(mCallback);
-  if (aInfo.type() == MaybeInfo::TClassifierInfo) {
-    mCallback->OnClassifyComplete(aResult, aInfo.get_ClassifierInfo().list(),
-                                  aInfo.get_ClassifierInfo().provider(),
-                                  aInfo.get_ClassifierInfo().prefix());
-  }
-  return IPC_OK();
-}
--- a/dom/ipc/URLClassifierChild.h
+++ b/dom/ipc/URLClassifierChild.h
@@ -3,35 +3,50 @@
 /* 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_URLClassifierChild_h
 #define mozilla_dom_URLClassifierChild_h
 
 #include "mozilla/dom/PURLClassifierChild.h"
+#include "mozilla/dom/PURLClassifierLocalChild.h"
 #include "nsIURIClassifier.h"
 
 namespace mozilla {
 namespace dom {
 
-class URLClassifierChild : public PURLClassifierChild
+template<typename BaseProtocol>
+class URLClassifierChildBase : public BaseProtocol
 {
- public:
-  URLClassifierChild() = default;
+public:
+  URLClassifierChildBase() = default;
 
   void SetCallback(nsIURIClassifierCallback* aCallback)
   {
     mCallback = aCallback;
   }
-  mozilla::ipc::IPCResult Recv__delete__(const MaybeInfo& aInfo,
-                                         const nsresult& aResult) override;
 
- private:
-  ~URLClassifierChild() = default;
+  mozilla::ipc::IPCResult Recv__delete__(const MaybeInfo& aInfo,
+                                         const nsresult& aResult) override
+  {
+    MOZ_ASSERT(mCallback);
+    if (aInfo.type() == MaybeInfo::TClassifierInfo) {
+      mCallback->OnClassifyComplete(aResult, aInfo.get_ClassifierInfo().list(),
+                                    aInfo.get_ClassifierInfo().provider(),
+                                    aInfo.get_ClassifierInfo().prefix());
+    }
+    return IPC_OK();
+  }
+
+private:
+  ~URLClassifierChildBase() = default;
 
   nsCOMPtr<nsIURIClassifierCallback> mCallback;
 };
 
+using URLClassifierChild = URLClassifierChildBase<PURLClassifierChild>;
+using URLClassifierLocalChild = URLClassifierChildBase<PURLClassifierLocalChild>;
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_URLClassifierChild_h
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -6,16 +6,19 @@
 
 #include "URLClassifierParent.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/Unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+/////////////////////////////////////////////////////////////////////
+//URLClassifierParent.
+
 NS_IMPL_ISUPPORTS(URLClassifierParent, nsIURIClassifierCallback)
 
 mozilla::ipc::IPCResult
 URLClassifierParent::StartClassify(nsIPrincipal* aPrincipal,
                                    bool aUseTrackingProtection,
                                    bool* aSuccess)
 {
   *aSuccess = false;
@@ -36,38 +39,46 @@ URLClassifierParent::StartClassify(nsIPr
     // 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();
 }
 
-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;
 }
+
+/////////////////////////////////////////////////////////////////////
+//URLClassifierLocalParent.
+
+NS_IMPL_ISUPPORTS(URLClassifierLocalParent, nsIURIClassifierCallback)
+
+mozilla::ipc::IPCResult
+URLClassifierLocalParent::StartClassify(nsIURI* aURI, const nsACString& aTables)
+{
+  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)) {
+    MOZ_ASSERT(aURI);
+    rv = uriClassifier->AsyncClassifyLocalWithTables(aURI, aTables, this);
+  }
+  if (NS_FAILED(rv)) {
+    // Cannot do ClassificationFailed() because the child side
+    // is expecting a callback. Only the second parameter will
+    // be used, which is the "matched list". We treat "unable
+    // to classify" as "not on any list".
+    OnClassifyComplete(NS_OK, EmptyCString(), EmptyCString(), EmptyCString());
+  }
+  return IPC_OK();
+}
+
+void
+URLClassifierLocalParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mIPCOpen = false;
+}
--- a/dom/ipc/URLClassifierParent.h
+++ b/dom/ipc/URLClassifierParent.h
@@ -3,39 +3,87 @@
 /* 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 "mozilla/dom/PURLClassifierLocalParent.h"
 #include "nsIURIClassifier.h"
 
 namespace mozilla {
 namespace dom {
 
-class URLClassifierParent : public nsIURIClassifierCallback,
-                            public PURLClassifierParent
+template<typename BaseProtocol>
+class URLClassifierParentBase : public nsIURIClassifierCallback,
+                                public BaseProtocol
 {
- public:
-  URLClassifierParent() = default;
+public:
+  // nsIURIClassifierCallback.
+  NS_IMETHOD OnClassifyComplete(nsresult aErrorCode,
+                                const nsACString& aList,
+                                const nsACString& aProvider,
+                                const nsACString& aPrefix)
+  {
+    if (mIPCOpen) {
+      ClassifierInfo info = ClassifierInfo(nsCString(aList),
+                                           nsCString(aProvider),
+                                           nsCString(aPrefix));
+      Unused << BaseProtocol::Send__delete__(this, info, aErrorCode);
+    }
+    return NS_OK;
+  }
 
+  // Custom.
+  void ClassificationFailed()
+  {
+    if (mIPCOpen) {
+      Unused << BaseProtocol::Send__delete__(this, void_t(), NS_ERROR_FAILURE);
+    }
+  }
+
+protected:
+  ~URLClassifierParentBase() = default;
+  bool mIPCOpen = true;
+};
+
+//////////////////////////////////////////////////////////////
+// URLClassifierParent
+
+class URLClassifierParent : public URLClassifierParentBase<PURLClassifierParent>
+{
+public:
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIURICLASSIFIERCALLBACK
 
   mozilla::ipc::IPCResult StartClassify(nsIPrincipal* aPrincipal,
                                         bool aUseTrackingProtection,
                                         bool* aSuccess);
-  void ActorDestroy(ActorDestroyReason aWhy) override;
-
-  void ClassificationFailed();
-
- private:
+private:
   ~URLClassifierParent() = default;
 
-  bool mIPCOpen = true;
+  // Override PURLClassifierParent::ActorDestroy. We seem to unable to
+  // override from the base template class.
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+//////////////////////////////////////////////////////////////
+// URLClassifierLocalParent
+
+class URLClassifierLocalParent : public URLClassifierParentBase<PURLClassifierLocalParent>
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  mozilla::ipc::IPCResult StartClassify(nsIURI* aURI, const nsACString& aTables);
+
+private:
+  ~URLClassifierLocalParent() = default;
+
+  // Override PURLClassifierParent::ActorDestroy.
+  void ActorDestroy(ActorDestroyReason aWhy) override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_URLClassifierParent_h
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -66,17 +66,16 @@ UNIFIED_SOURCES += [
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'StructuredCloneData.cpp',
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
     'TabParent.cpp',
     'TelemetryScrollProbe.cpp',
-    'URLClassifierChild.cpp',
     'URLClassifierParent.cpp',
 ]
 
 # ContentChild.cpp cannot be compiled in unified mode on  linux due to Time conflict
 SOURCES += [
     'ContentChild.cpp',
     'ProcessHangMonitor.cpp',
 ]
@@ -94,16 +93,17 @@ IPDL_SOURCES += [
     'PCycleCollectWithLogs.ipdl',
     'PDocumentRenderer.ipdl',
     'PFilePicker.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
     'PTabContext.ipdlh',
     'PURLClassifier.ipdl',
     'PURLClassifierInfo.ipdlh',
+    'PURLClassifierLocal.ipdl',
     'ServiceWorkerConfiguration.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -720,17 +720,17 @@ SpecialPowersAPI.prototype = {
      }
      return delayedCallback;
   },
 
   /* apply permissions to the system and when the test case is finished (SimpleTest.finish())
      we will revert the permission back to the original.
 
      inPermissions is an array of objects where each object has a type, action, context, ex:
-     [{'type': 'SystemXHR', 'allow': 1, 'context': document}, 
+     [{'type': 'SystemXHR', 'allow': 1, 'context': document},
       {'type': 'SystemXHR', 'allow': Ci.nsIPermissionManager.PROMPT_ACTION, 'context': document}]
 
      Allow can be a boolean value of true/false or ALLOW_ACTION/DENY_ACTION/PROMPT_ACTION/UNKNOWN_ACTION
   */
   pushPermissions: function(inPermissions, callback) {
     inPermissions = Cu.waiveXrays(inPermissions);
     var pendingPermissions = [];
     var cleanupPermissions = [];
@@ -2139,13 +2139,33 @@ SpecialPowersAPI.prototype = {
         }
       }, Ci.nsIThread.DISPATCH_NORMAL);
     };
 
     return classifierService.classify(unwrapIfWrapped(principal), eventTarget,
                                       tpEnabled, wrapCallback);
   },
 
+  // TODO: Bug 1353701 - Supports custom event target for labelling.
+  doUrlClassifyLocal(uri, tables, callback) {
+    let classifierService =
+      Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIURIClassifier);
+
+    let wrapCallback = (...args) => {
+      Services.tm.mainThread.dispatch(() => {
+        if (typeof callback == 'function') {
+          callback.call(undefined, ...args);
+        } else {
+          callback.onClassifyComplete.call(undefined, ...args);
+        }
+      }, Ci.nsIThread.DISPATCH_NORMAL);
+    };
+
+    return classifierService.asyncClassifyLocalWithTables(unwrapIfWrapped(uri),
+                                                          tables,
+                                                          wrapCallback);
+  },
+
 };
 
 this.SpecialPowersAPI = SpecialPowersAPI;
 this.bindDOMWindowUtils = bindDOMWindowUtils;
 this.getRawComponents = getRawComponents;
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -1752,18 +1752,38 @@ NS_IMETHODIMP
 nsUrlClassifierDBService::AsyncClassifyLocalWithTables(nsIURI *aURI,
                                                        const nsACString& aTables,
                                                        nsIURIClassifierCallback* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread(), "AsyncClassifyLocalWithTables must be called "
                                 "on main thread");
 
   if (XRE_IsContentProcess()) {
-    // TODO: e10s support. Bug 1343425.
-    return NS_ERROR_NOT_IMPLEMENTED;
+    using namespace mozilla::dom;
+    using namespace mozilla::ipc;
+
+    ContentChild* content = ContentChild::GetSingleton();
+    MOZ_ASSERT(content);
+
+    auto actor = new URLClassifierLocalChild();
+
+    // TODO: Bug 1353701 - Supports custom event target for labelling.
+    nsCOMPtr<nsIEventTarget> systemGroupEventTarget
+      = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
+    content->SetEventTargetForActor(actor, systemGroupEventTarget);
+
+    URIParams uri;
+    SerializeURI(aURI, uri);
+    nsAutoCString tables(aTables);
+    if (!content->SendPURLClassifierLocalConstructor(actor, uri, tables)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    actor->SetCallback(aCallback);
+    return NS_OK;
   }
 
   if (gShuttingDownThread) {
     return NS_ERROR_ABORT;
   }
 
   using namespace mozilla::Telemetry;
   auto startTime = TimeStamp::Now(); // For telemetry.
--- a/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
@@ -139,31 +139,23 @@ function testService() {
       let uri = ios.newURI(test.url);
       let prin = ssm.createCodebasePrincipal(uri, {});
       is(service.classifyLocal(uri, tables), test.table,
          `Successful synchronous classification of ${test.url} with TP=${test.trackingProtection}`);
       let result = SpecialPowers.doUrlClassify(prin, null, test.trackingProtection, function(errorCode) {
         is(errorCode, test.result,
            `Successful asynchronous classification of ${test.url} with TP=${test.trackingProtection}`);
 
-        try {
-          // Same as classifyLocal except for the 'async' call.
-          service.asyncClassifyLocalWithTables(uri, tables, function(errorCode, tables) {
-            is(tables, test.table,
-               `Successful asynchronous local classification of ${test.url} with TP=${test.trackingProtection}`);
-            runNextTest();
-          });
-        } catch (e) {
-          // TODO: asyncClassifyLocalWithTables e10s support. See Bug 1343425.
-          let processType = Cc["@mozilla.org/xre/app-info;1"]
-                              .getService(Ci.nsIXULRuntime).processType;
-          isnot(processType, Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT,
-                "Local asynchronous classification is not supported in content process");
+        // Same as classifyLocal except for the 'async' call.
+        SpecialPowers.doUrlClassifyLocal(uri, tables, function(errorCode, tables) {
+          is(tables, test.table,
+             `Successful asynchronous local classification of ${test.url} with TP=${test.trackingProtection}`);
           runNextTest();
-        }
+        });
+
       });
     }
     runNextTest(resolve);
   });
 }
 
 SpecialPowers.pushPrefEnv(
   {"set" : [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],