Bug 1356211 - Part 3: Supports custom callback thread for nsIURIClassifier.asyncClassifyLocalWithTables.
With the help of part 1 and 2, we are able to IPC this API off the main thread and do the callback
on the specified thread.
MozReview-Commit-ID: Atn2FPiQmtV
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -869,17 +869,17 @@ nsChannelClassifier::IsTrackerWhiteliste
nsCOMPtr<nsIURI> whitelistURI;
rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<IsTrackerWhitelistedCallback> cb =
new IsTrackerWhitelistedCallback(this, aList, aProvider, aPrefix,
whitelistEntry);
- return uriClassifier->AsyncClassifyLocalWithTables(whitelistURI, trackingWhitelist, cb);
+ return uriClassifier->AsyncClassifyLocalWithTables(whitelistURI, trackingWhitelist, cb, nullptr);
}
NS_IMETHODIMP
nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
const nsACString& aList,
const nsACString& aProvider,
const nsACString& aPrefix)
{
--- a/netwerk/base/nsIURIClassifier.idl
+++ b/netwerk/base/nsIURIClassifier.idl
@@ -9,16 +9,17 @@
class nsCString;
%}
[ref] native StringArrayRef(nsTArray<nsCString>);
interface nsIChannel;
interface nsIEventTarget;
interface nsIPrincipal;
interface nsIURI;
+interface nsIThread;
/**
* Callback function for nsIURIClassifier lookups.
*/
[scriptable, function, uuid(8face46e-0c96-470f-af40-0037dcd797bd)]
interface nsIURIClassifierCallback : nsISupports
{
/**
@@ -87,18 +88,22 @@ interface nsIURIClassifier : nsISupports
[noscript] StringArrayRef classifyLocalWithTables(in nsIURI aURI, in ACString aTables);
/**
* Asynchronously classify a URI with a comma-separated string
* containing the given tables. This does not make network requests.
* The callback does NOT totally follow nsIURIClassifierCallback's
* semantics described above. Only |aList| will be meaningful, which
* is a comma separated list of table names. (same as what classifyLocal
* returns.)
+ *
+ * Note that if the aCallbackEventTarget is not specified, the callback
+ * will occur on the main thread.
*/
void asyncClassifyLocalWithTables(in nsIURI aURI,
in ACString aTables,
- in nsIURIClassifierCallback aCallback);
+ in nsIURIClassifierCallback aCallback,
+ in nsIEventTarget aCallbackEventTarget);
/**
* Same as above, but returns a comma separated list of table names.
* This is an internal interface used only for testing purposes.
*/
ACString classifyLocal(in nsIURI aURI, in ACString aTables);
};
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6004,17 +6004,17 @@ nsHttpChannel::InitLocalBlockList(const
}
nsAutoCString tables;
Preferences::GetCString("urlclassifier.trackingTable", &tables);
nsTArray<nsCString> results;
RefPtr<InitLocalBlockListXpcCallback> xpcCallback
= new InitLocalBlockListXpcCallback(aCallback);
- rv = classifier->AsyncClassifyLocalWithTables(uri, tables, xpcCallback);
+ rv = classifier->AsyncClassifyLocalWithTables(uri, tables, xpcCallback, nullptr);
if (NS_FAILED(rv)) {
return false;
}
return true;
}
NS_IMETHODIMP
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -2161,16 +2161,17 @@ SpecialPowersAPI.prototype = {
} else {
callback.onClassifyComplete.call(undefined, ...args);
}
});
};
return classifierService.asyncClassifyLocalWithTables(unwrapIfWrapped(uri),
tables,
- wrapCallback);
+ wrapCallback,
+ null);
},
};
this.SpecialPowersAPI = SpecialPowersAPI;
this.bindDOMWindowUtils = bindDOMWindowUtils;
this.getRawComponents = getRawComponents;
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -47,16 +47,18 @@
#include "mozilla/Attributes.h"
#include "nsIPrincipal.h"
#include "Classifier.h"
#include "ProtocolParser.h"
#include "nsContentUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/URLClassifierChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsProxyRelease.h"
#include "SBTelemetryUtils.h"
namespace mozilla {
namespace safebrowsing {
nsresult
@@ -1646,16 +1648,19 @@ nsUrlClassifierDBService::Init()
if (appInfo) {
bool inSafeMode = false;
appInfo->GetInSafeMode(&inSafeMode);
if (inSafeMode) {
return NS_ERROR_NOT_AVAILABLE;
}
}
+ NS_NewNamedThread(NS_LITERAL_CSTRING("URL Classifier IPC"),
+ getter_AddRefs(mPBackgroundIPCThread));
+
switch (XRE_GetProcessType()) {
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
@@ -1834,46 +1839,124 @@ nsUrlClassifierDBService::ClassifyLocal(
} else {
aTableResults.AppendLiteral(",");
}
aTableResults.Append(result);
}
return NS_OK;
}
+mozilla::ipc::PBackgroundChild*
+nsUrlClassifierDBService::EnsurePBackgroundChild()
+{
+ using namespace mozilla::dom;
+ using namespace mozilla::ipc;
+ PBackgroundChild* existingBackgroundChild =
+ BackgroundChild::GetForCurrentThread();
+ // If it's not spun up yet, block until it is, and retry
+ if (!existingBackgroundChild) {
+ LOG(("No existingBackgroundChild"));
+ existingBackgroundChild =
+ BackgroundChild::SynchronouslyCreateForCurrentThread();
+ }
+ return existingBackgroundChild;
+}
+
+namespace {
+
+// This callback wrapper is required when we need to pass the
+// possibly-non-thread-safe callback around. The wrapped callback
+// will be released on the main thread.
+class ThreadSafeURIClassifierCallback : public nsIURIClassifierCallback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit ThreadSafeURIClassifierCallback(nsIURIClassifierCallback* aCallback)
+ : mCallback(aCallback)
+ {
+ }
+
+ NS_IMETHOD
+ OnClassifyComplete(nsresult /*aErrorCode*/,
+ const nsACString& aLists, // Only this matters.
+ const nsACString& /*aProvider*/,
+ const nsACString& /*aPrefix*/) override
+ {
+ return mCallback->OnClassifyComplete(NS_OK, aLists,
+ EmptyCString(), EmptyCString());
+ }
+
+private:
+ virtual ~ThreadSafeURIClassifierCallback()
+ {
+ if (!NS_IsMainThread()) {
+ NS_ReleaseOnMainThread(mCallback.forget());
+ }
+ }
+
+ nsCOMPtr<nsIURIClassifierCallback> mCallback;
+};
+
+NS_IMPL_ISUPPORTS(ThreadSafeURIClassifierCallback, nsIURIClassifierCallback)
+
+} // end of unnamed namespace.
+
NS_IMETHODIMP
nsUrlClassifierDBService::AsyncClassifyLocalWithTables(nsIURI *aURI,
const nsACString& aTables,
- nsIURIClassifierCallback* aCallback)
+ nsIURIClassifierCallback* aCallback,
+ nsIEventTarget* aCallbackEventTarget)
{
MOZ_ASSERT(NS_IsMainThread(), "AsyncClassifyLocalWithTables must be called "
"on main thread");
+ // Wrap around the raw input for convenience of use.
+ RefPtr<nsIEventTarget> nonNullCallbackEventTarget =
+ aCallbackEventTarget ? aCallbackEventTarget
+ : mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
+
+ RefPtr<nsIURIClassifierCallback> wrappedCallback =
+ new ThreadSafeURIClassifierCallback(aCallback);
+
if (XRE_IsContentProcess()) {
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);
-
+ RefPtr<nsUrlClassifierDBService> self = this;
URIParams uri;
SerializeURI(aURI, uri);
nsAutoCString tables(aTables);
- if (!content->SendPURLClassifierLocalConstructor(actor, uri, tables)) {
- return NS_ERROR_FAILURE;
- }
-
- actor->SetCallback(aCallback);
+
+ // We are gonna dispatch the following runnable to the IPC thread.
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
+ auto actor = new URLClassifierLocalChild();
+ auto pbackgroundChild = self->EnsurePBackgroundChild();
+ if (!pbackgroundChild) {
+ // We failed to have a PBackgroundChild for IPC. Just callback
+ // with null result on the callback thread.
+ nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction([=] {
+ wrappedCallback->OnClassifyComplete(NS_OK, // Not used.
+ EmptyCString(),
+ EmptyCString(), // provider. (Not used)
+ EmptyCString()); // prefix. (Not used)
+ });
+ nonNullCallbackEventTarget->Dispatch(cbRunnable, NS_DISPATCH_NORMAL);
+ return;
+ }
+ actor->SetCallback(wrappedCallback);
+ actor->SetCallbackEventTarget(nonNullCallbackEventTarget);
+ if (!pbackgroundChild->SendPURLClassifierLocalConstructor(actor, uri, tables)) {
+ NS_WARNING("Unable to SendPURLClassifierLocalConstructor()");
+ return;
+ }
+ });
+
+ mPBackgroundIPCThread->Dispatch(r, NS_DISPATCH_NORMAL);
+
return NS_OK;
}
if (gShuttingDownThread) {
return NS_ERROR_ABORT;
}
using namespace mozilla::Telemetry;
@@ -1887,54 +1970,48 @@ nsUrlClassifierDBService::AsyncClassifyL
nsCOMPtr<nsIUrlClassifierUtils> utilsService =
do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
nsresult rv = utilsService->GetKeyForURI(uri, key);
NS_ENSURE_SUCCESS(rv, rv);
auto worker = mWorker;
nsCString tables(aTables);
- // Since aCallback will be passed around threads...
- nsMainThreadPtrHandle<nsIURIClassifierCallback> callback(
- new nsMainThreadPtrHolder<nsIURIClassifierCallback>(aCallback));
-
- nsCOMPtr<nsIRunnable> r =
- NS_NewRunnableFunction([worker, key, tables, callback, startTime] () -> void {
-
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] {
+ // Perform the local lookup.
nsCString matchedLists;
nsAutoPtr<LookupResultArray> results(new LookupResultArray());
if (results) {
nsresult rv = worker->DoLocalLookup(key, tables, results);
if (NS_SUCCEEDED(rv)) {
for (uint32_t i = 0; i < results->Length(); i++) {
if (i > 0) {
matchedLists.AppendLiteral(",");
}
matchedLists.Append(results->ElementAt(i).mTableName);
}
}
}
- nsCOMPtr<nsIRunnable> cbRunnable =
- NS_NewRunnableFunction([callback, matchedLists, startTime] () -> void {
- // Measure the time diff between calling and callback.
- AccumulateDelta_impl<Millisecond>::compute(
- Telemetry::URLCLASSIFIER_ASYNC_CLASSIFYLOCAL_TIME, startTime);
-
- // |callback| is captured as const value so ...
- auto cb = const_cast<nsIURIClassifierCallback*>(callback.get());
- cb->OnClassifyComplete(NS_OK, // Not used.
- matchedLists,
- EmptyCString(), // provider. (Not used)
- EmptyCString()); // prefix. (Not used)
- });
-
- NS_DispatchToMainThread(cbRunnable);
+ // We've done the local lookup. Call back on the given callback thread.
+ nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction([=] {
+ // Measure the time diff between calling and callback.
+ AccumulateDelta_impl<Millisecond>::compute(
+ Telemetry::URLCLASSIFIER_ASYNC_CLASSIFYLOCAL_TIME, startTime);
+
+ wrappedCallback->OnClassifyComplete(NS_OK, // Not used.
+ matchedLists,
+ EmptyCString(), // provider. (Not used)
+ EmptyCString()); // prefix. (Not used)
+ });
+
+ nonNullCallbackEventTarget->Dispatch(cbRunnable, NS_DISPATCH_NORMAL);
});
+ // Dispatch the lookup task to the worker thread.
return gDbBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
nsUrlClassifierDBService::ClassifyLocalWithTables(nsIURI *aURI,
const nsACString& aTables,
nsTArray<nsCString>& aTableResults)
{
@@ -1978,16 +2055,17 @@ nsUrlClassifierDBService::ClassifyLocalW
}
// In unittests, we may not have been initalized, so don't crash.
rv = mWorkerProxy->DoLocalLookup(key, aTables, results);
if (NS_SUCCEEDED(rv)) {
rv = ProcessLookupResults(results, aTableResults);
NS_ENSURE_SUCCESS(rv, rv);
}
+
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
const nsACString& tables,
nsIUrlClassifierCallback* c)
{
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -72,16 +72,21 @@ namespace safebrowsing {
class Classifier;
class ProtocolParser;
class TableUpdate;
nsresult
TablesToResponse(const nsACString& tables);
} // namespace safebrowsing
+
+namespace ipc {
+ class PBackgroundChild;
+}
+
} // 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
{
@@ -100,16 +105,18 @@ public:
NS_DECL_NSIURICLASSIFIER
NS_DECL_NSIOBSERVER
bool GetCompleter(const nsACString& tableName,
nsIUrlClassifierHashCompleter** completer);
nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results);
nsresult CacheMisses(mozilla::safebrowsing::PrefixArray *results);
+ mozilla::ipc::PBackgroundChild* EnsurePBackgroundChild();
+
static nsIThread* BackgroundThread();
static bool ShutdownHasStarted();
private:
const nsTArray<nsCString> kObservedPrefs = {
NS_LITERAL_CSTRING(CHECK_MALWARE_PREF),
@@ -175,16 +182,18 @@ private:
nsTArray<nsCString> mDisallowCompletionsTables;
// Comma-separated list of tables to use in lookups.
nsCString mTrackingProtectionTables;
nsCString mBaseTables;
// Thread that we do the updates on.
static nsIThread* gDbBackgroundThread;
+
+ nsCOMPtr<nsIThread> mPBackgroundIPCThread;
};
class nsUrlClassifierDBServiceWorker final : public nsIUrlClassifierDBService
{
public:
nsUrlClassifierDBServiceWorker();
NS_DECL_THREADSAFE_ISUPPORTS