fune/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp
Valentin Gosu 1a1f42da37 Bug 1714307 - Run modernize-use-default-member-init --fix check on netwerk r=necko-reviewers,kershaw
This changeset is the result of adding modernize-use-default-member-init to
tools/clang-tidy/config.yaml then proceeding to run
`./mach static-analysis check netwerk/ --fix`
I then went through the resulting fix and manually updated all of the member
variables which were missed due to them having a non-trivial constructor.

Note that the tool was only run on Linux, so code that only runs on some
platforms may have been missed.

The member variables that are still initialized in the contructor definition
are:
  - bitfields (not all currently supported compilers allow default-member-init
  - variables that are initialized via a parameter
  - variables that use code not visible in the header file

There are a few advantages to landing this change:
- fewer lines of code - now declaration is in the same place as initialization
  this also makes it easier to see when looking at the header.
- it makes it harder to miss initializing a member when adding a new contructor
- variables that depend on an include guard look much nicer now

Additionally I removed some unnecessary reinitialization of NetAddr members
(it has a constructor that does that now), and changed nsWifiScannerDBus to
use the thread-safe strtok_r instead of strtok.

Differential Revision: https://phabricator.services.mozilla.com/D116980
2021-06-11 07:10:41 +00:00

922 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set expandtab ts=4 sw=2 sts=2 cin: */
/* 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 "Classifier.h"
#include "mozilla/Components.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/net/AsyncUrlChannelClassifier.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "mozilla/net/UrlClassifierFeatureResult.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
#include "nsUrlClassifierDBService.h"
#include "nsUrlClassifierUtils.h"
namespace mozilla {
namespace net {
namespace {
// Big picture comment
// -----------------------------------------------------------------------------
// nsUrlClassifierDBService::channelClassify() classifies a channel using a set
// of URL-Classifier features. This method minimizes the number of lookups and
// URI parsing and this is done using the classes here described.
//
// The first class is 'FeatureTask' which is able to retrieve the list of
// features for this channel using the feature-factory. See
// UrlClassifierFeatureFactory.
// For each feature, it creates a FeatureData object, which contains the
// entitylist and blocklist prefs and tables. The reason why we create
// FeatureData is because:
// - features are not thread-safe.
// - we want to store the state of the classification in the FeatureData
// object.
//
// It can happen that multiple features share the same tables. In order to do
// the lookup just once, we have TableData class. When multiple features
// contain the same table, they have references to the same couple TableData +
// URIData objects.
//
// During the classification, the channel's URIs are fragmented. In order to
// create these fragments just once, we use the URIData class, which is pointed
// by TableData classes.
//
// The creation of these classes happens on the main-thread. The classification
// happens on the worker thread.
// URIData
// -----------------------------------------------------------------------------
// In order to avoid multiple URI parsing, we have this class which contains
// nsIURI and its fragments.
class URIData {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URIData);
static nsresult Create(nsIURI* aURI, nsIURI* aInnermostURI,
nsIUrlClassifierFeature::URIType aURIType,
URIData** aData);
bool IsEqual(nsIURI* aURI) const;
const nsTArray<nsCString>& Fragments();
nsIURI* URI() const;
private:
URIData();
~URIData();
nsCOMPtr<nsIURI> mURI;
nsCString mURISpec;
nsTArray<nsCString> mFragments;
nsIUrlClassifierFeature::URIType mURIType;
};
/* static */
nsresult URIData::Create(nsIURI* aURI, nsIURI* aInnermostURI,
nsIUrlClassifierFeature::URIType aURIType,
URIData** aData) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aURI);
MOZ_ASSERT(aInnermostURI);
RefPtr<URIData> data = new URIData();
data->mURI = aURI;
data->mURIType = aURIType;
nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
if (NS_WARN_IF(!utilsService)) {
return NS_ERROR_FAILURE;
}
nsresult rv = utilsService->GetKeyForURI(aInnermostURI, data->mURISpec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
UC_LOG_LEAK(
("AsyncChannelClassifier::URIData::Create new URIData created for spec "
"%s [this=%p]",
data->mURISpec.get(), data.get()));
data.forget(aData);
return NS_OK;
}
URIData::URIData() { MOZ_ASSERT(NS_IsMainThread()); }
URIData::~URIData() { NS_ReleaseOnMainThread("URIData:mURI", mURI.forget()); }
bool URIData::IsEqual(nsIURI* aURI) const {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aURI);
bool isEqual = false;
nsresult rv = mURI->Equals(aURI, &isEqual);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return isEqual;
}
const nsTArray<nsCString>& URIData::Fragments() {
MOZ_ASSERT(!NS_IsMainThread());
if (mFragments.IsEmpty()) {
nsresult rv;
if (mURIType == nsIUrlClassifierFeature::pairwiseEntitylistURI) {
rv = LookupCache::GetLookupEntitylistFragments(mURISpec, &mFragments);
} else {
rv = LookupCache::GetLookupFragments(mURISpec, &mFragments);
}
Unused << NS_WARN_IF(NS_FAILED(rv));
}
return mFragments;
}
nsIURI* URIData::URI() const {
MOZ_ASSERT(NS_IsMainThread());
return mURI;
}
// TableData
// ----------------------------------------------------------------------------
// In order to avoid multiple lookups on the same table + URI, we have this
// class.
class TableData {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableData);
enum State {
eUnclassified,
eNoMatch,
eMatch,
};
TableData(URIData* aURIData, const nsACString& aTable);
nsIURI* URI() const;
const nsACString& Table() const;
const LookupResultArray& Result() const;
State MatchState() const;
bool IsEqual(URIData* aURIData, const nsACString& aTable) const;
// Returns true if the table classifies the URI. This method must be called
// on hte classifier worker thread.
bool DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier);
private:
~TableData();
RefPtr<URIData> mURIData;
State mState;
nsCString mTable;
LookupResultArray mResults;
};
TableData::TableData(URIData* aURIData, const nsACString& aTable)
: mURIData(aURIData), mState(eUnclassified), mTable(aTable) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aURIData);
UC_LOG_LEAK(
("AsyncChannelClassifier::TableData CTOR - new TableData created %s "
"[this=%p]",
aTable.BeginReading(), this));
}
TableData::~TableData() = default;
nsIURI* TableData::URI() const {
MOZ_ASSERT(NS_IsMainThread());
return mURIData->URI();
}
const nsACString& TableData::Table() const {
MOZ_ASSERT(NS_IsMainThread());
return mTable;
}
const LookupResultArray& TableData::Result() const {
MOZ_ASSERT(NS_IsMainThread());
return mResults;
}
TableData::State TableData::MatchState() const {
MOZ_ASSERT(NS_IsMainThread());
return mState;
}
bool TableData::IsEqual(URIData* aURIData, const nsACString& aTable) const {
MOZ_ASSERT(NS_IsMainThread());
return mURIData == aURIData && mTable == aTable;
}
bool TableData::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aWorkerClassifier);
if (mState == TableData::eUnclassified) {
UC_LOG_LEAK(
("AsyncChannelClassifier::TableData::DoLookup - starting lookup "
"[this=%p]",
this));
const nsTArray<nsCString>& fragments = mURIData->Fragments();
nsresult rv = aWorkerClassifier->DoSingleLocalLookupWithURIFragments(
fragments, mTable, mResults);
Unused << NS_WARN_IF(NS_FAILED(rv));
mState = mResults.IsEmpty() ? TableData::eNoMatch : TableData::eMatch;
UC_LOG_LEAK(
("AsyncChannelClassifier::TableData::DoLookup - lookup completed. "
"Matches: %d [this=%p]",
(int)mResults.Length(), this));
}
return !mResults.IsEmpty();
}
// FeatureData
// ----------------------------------------------------------------------------
class FeatureTask;
// This is class contains all the Feature data.
class FeatureData {
enum State {
eUnclassified,
eNoMatch,
eMatchBlocklist,
eMatchEntitylist,
};
public:
FeatureData() = default;
~FeatureData();
nsresult Initialize(FeatureTask* aTask, nsIChannel* aChannel,
nsIUrlClassifierFeature* aFeature);
void DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier);
// Returns true if the next feature should be processed.
bool MaybeCompleteClassification(nsIChannel* aChannel);
private:
nsresult InitializeList(FeatureTask* aTask, nsIChannel* aChannel,
nsIUrlClassifierFeature::listType aListType,
nsTArray<RefPtr<TableData>>& aList);
State mState{eUnclassified};
nsCOMPtr<nsIUrlClassifierFeature> mFeature;
nsCOMPtr<nsIChannel> mChannel;
nsTArray<RefPtr<TableData>> mBlocklistTables;
nsTArray<RefPtr<TableData>> mEntitylistTables;
// blocklist + entitylist.
nsCString mHostInPrefTables[2];
};
FeatureData::~FeatureData() {
NS_ReleaseOnMainThread("FeatureData:mFeature", mFeature.forget());
}
nsresult FeatureData::Initialize(FeatureTask* aTask, nsIChannel* aChannel,
nsIUrlClassifierFeature* aFeature) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTask);
MOZ_ASSERT(aChannel);
MOZ_ASSERT(aFeature);
if (UC_LOG_ENABLED()) {
nsAutoCString name;
aFeature->GetName(name);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::Initialize - Feature %s "
"[this=%p, channel=%p]",
name.get(), this, aChannel));
}
mFeature = aFeature;
mChannel = aChannel;
nsresult rv = InitializeList(
aTask, aChannel, nsIUrlClassifierFeature::blocklist, mBlocklistTables);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = InitializeList(aTask, aChannel, nsIUrlClassifierFeature::entitylist,
mEntitylistTables);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void FeatureData::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aWorkerClassifier);
MOZ_ASSERT(mState == eUnclassified);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - lookup starting "
"[this=%p]",
this));
// This is wrong, but it's fast: we don't want to check if the host is in the
// blocklist table if we know that it's going to be entitylisted by pref.
// So, also if maybe it's not blocklisted, let's consider it 'entitylisted'.
if (!mHostInPrefTables[nsIUrlClassifierFeature::entitylist].IsEmpty()) {
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by pref "
"[this=%p]",
this));
mState = eMatchEntitylist;
return;
}
// Let's check if this feature blocklists the URI.
bool isBlocklisted =
!mHostInPrefTables[nsIUrlClassifierFeature::blocklist].IsEmpty();
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted by pref: "
"%d [this=%p]",
isBlocklisted, this));
if (!isBlocklisted) {
// If one of the blocklist table matches the URI, we don't need to continue
// with the others: the feature is blocklisted (but maybe also
// entitylisted).
for (TableData* tableData : mBlocklistTables) {
if (tableData->DoLookup(aWorkerClassifier)) {
isBlocklisted = true;
break;
}
}
}
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted before "
"entitylisting: %d [this=%p]",
isBlocklisted, this));
if (!isBlocklisted) {
mState = eNoMatch;
return;
}
// Now, let's check if we need to entitylist the same URI.
for (TableData* tableData : mEntitylistTables) {
// If one of the entitylist table matches the URI, we don't need to continue
// with the others: the feature is entitylisted.
if (tableData->DoLookup(aWorkerClassifier)) {
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by "
"table [this=%p]",
this));
mState = eMatchEntitylist;
return;
}
}
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted [this=%p]",
this));
mState = eMatchBlocklist;
}
bool FeatureData::MaybeCompleteClassification(nsIChannel* aChannel) {
MOZ_ASSERT(NS_IsMainThread());
nsAutoCString name;
mFeature->GetName(name);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
"completing "
"classification [this=%p channel=%p]",
this, aChannel));
switch (mState) {
case eNoMatch:
UC_LOG(
("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
"no match for feature %s. Let's "
"move on [this=%p channel=%p]",
name.get(), this, aChannel));
return true;
case eMatchEntitylist:
UC_LOG(
("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification "
"- entitylisted by feature %s. Let's "
"move on [this=%p channel=%p]",
name.get(), this, aChannel));
return true;
case eMatchBlocklist:
UC_LOG(
("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
"blocklisted by feature %s [this=%p channel=%p]",
name.get(), this, aChannel));
break;
case eUnclassified:
MOZ_CRASH("We should not be here!");
break;
}
MOZ_ASSERT(mState == eMatchBlocklist);
// Maybe we have to ignore this host
nsAutoCString exceptionList;
nsresult rv = mFeature->GetExceptionHostList(exceptionList);
if (NS_WARN_IF(NS_FAILED(rv))) {
UC_LOG_WARN(
("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification - "
"error. Let's move on [this=%p channel=%p]",
this, aChannel));
return true;
}
if (!mBlocklistTables.IsEmpty() &&
nsContentUtils::IsURIInList(mBlocklistTables[0]->URI(), exceptionList)) {
nsCString spec = mBlocklistTables[0]->URI()->GetSpecOrDefault();
spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
UC_LOG(
("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
"uri %s found in "
"exceptionlist of feature %s [this=%p channel=%p]",
spec.get(), name.get(), this, aChannel));
return true;
}
nsTArray<nsCString> list;
nsTArray<nsCString> hashes;
if (!mHostInPrefTables[nsIUrlClassifierFeature::blocklist].IsEmpty()) {
list.AppendElement(mHostInPrefTables[nsIUrlClassifierFeature::blocklist]);
// Telemetry expects every tracking channel has hash, create it for test
// entry
Completion complete;
complete.FromPlaintext(
mHostInPrefTables[nsIUrlClassifierFeature::blocklist]);
hashes.AppendElement(complete.ToString());
}
for (TableData* tableData : mBlocklistTables) {
if (tableData->MatchState() == TableData::eMatch) {
list.AppendElement(tableData->Table());
for (const auto& r : tableData->Result()) {
hashes.AppendElement(r->hash.complete.ToString());
}
}
}
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
"process channel [this=%p channel=%p]",
this, aChannel));
bool shouldContinue = false;
rv = mFeature->ProcessChannel(aChannel, list, hashes, &shouldContinue);
Unused << NS_WARN_IF(NS_FAILED(rv));
return shouldContinue;
}
// CallbackHolder
// ----------------------------------------------------------------------------
// This class keeps the callback alive and makes sure that we release it on the
// correct thread.
class CallbackHolder final {
public:
NS_INLINE_DECL_REFCOUNTING(CallbackHolder);
explicit CallbackHolder(std::function<void()>&& aCallback)
: mCallback(std::move(aCallback)) {}
void Exec() const { mCallback(); }
private:
~CallbackHolder() = default;
std::function<void()> mCallback;
};
// FeatureTask
// ----------------------------------------------------------------------------
// A FeatureTask is a class that is able to classify a channel using a set of
// features. The features are grouped by:
// - URIs - to avoid extra URI parsing.
// - Tables - to avoid multiple lookup on the same table.
class FeatureTask {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureTask);
static nsresult Create(nsIChannel* aChannel,
std::function<void()>&& aCallback,
FeatureTask** aTask);
// Called on the classifier thread.
void DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier);
// Called on the main-thread to process the channel.
void CompleteClassification();
nsresult GetOrCreateURIData(nsIURI* aURI, nsIURI* aInnermostURI,
nsIUrlClassifierFeature::URIType aURIType,
URIData** aData);
nsresult GetOrCreateTableData(URIData* aURIData, const nsACString& aTable,
TableData** aData);
private:
FeatureTask(nsIChannel* aChannel, std::function<void()>&& aCallback);
~FeatureTask();
nsCOMPtr<nsIChannel> mChannel;
RefPtr<CallbackHolder> mCallbackHolder;
nsTArray<FeatureData> mFeatures;
nsTArray<RefPtr<URIData>> mURIs;
nsTArray<RefPtr<TableData>> mTables;
};
// Features are able to classify particular URIs from a channel. For instance,
// tracking-annotation feature uses the top-level URI to entitylist the current
// channel's URI; flash feature always uses the channel's URI. Because of
// this, this function aggregates feature per URI and tables.
/* static */
nsresult FeatureTask::Create(nsIChannel* aChannel,
std::function<void()>&& aCallback,
FeatureTask** aTask) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aChannel);
MOZ_ASSERT(aTask);
// We need to obtain the list of nsIUrlClassifierFeature objects able to
// classify this channel. If the list is empty, we do an early return.
nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features;
UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features);
if (features.IsEmpty()) {
UC_LOG(
("AsyncChannelClassifier::FeatureTask::Create - no task is needed for "
"channel %p",
aChannel));
return NS_OK;
}
RefPtr<FeatureTask> task = new FeatureTask(aChannel, std::move(aCallback));
UC_LOG(
("AsyncChannelClassifier::FeatureTask::Create - FeatureTask %p created "
"for channel %p",
task.get(), aChannel));
for (nsIUrlClassifierFeature* feature : features) {
FeatureData* featureData = task->mFeatures.AppendElement();
nsresult rv = featureData->Initialize(task, aChannel, feature);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
task.forget(aTask);
return NS_OK;
}
FeatureTask::FeatureTask(nsIChannel* aChannel,
std::function<void()>&& aCallback)
: mChannel(aChannel) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mChannel);
std::function<void()> callback = std::move(aCallback);
mCallbackHolder = new CallbackHolder(std::move(callback));
}
FeatureTask::~FeatureTask() {
NS_ReleaseOnMainThread("FeatureTask::mChannel", mChannel.forget());
NS_ReleaseOnMainThread("FeatureTask::mCallbackHolder",
mCallbackHolder.forget());
}
nsresult FeatureTask::GetOrCreateURIData(
nsIURI* aURI, nsIURI* aInnermostURI,
nsIUrlClassifierFeature::URIType aURIType, URIData** aData) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aURI);
MOZ_ASSERT(aInnermostURI);
MOZ_ASSERT(aData);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - checking if "
"a URIData must be "
"created [this=%p]",
this));
for (URIData* data : mURIs) {
if (data->IsEqual(aURI)) {
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - reuse "
"existing URIData %p [this=%p]",
data, this));
RefPtr<URIData> uriData = data;
uriData.forget(aData);
return NS_OK;
}
}
RefPtr<URIData> data;
nsresult rv =
URIData::Create(aURI, aInnermostURI, aURIType, getter_AddRefs(data));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mURIs.AppendElement(data);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - create new "
"URIData %p [this=%p]",
data.get(), this));
data.forget(aData);
return NS_OK;
}
nsresult FeatureTask::GetOrCreateTableData(URIData* aURIData,
const nsACString& aTable,
TableData** aData) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aURIData);
MOZ_ASSERT(aData);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - checking "
"if TableData must be "
"created [this=%p]",
this));
for (TableData* data : mTables) {
if (data->IsEqual(aURIData, aTable)) {
UC_LOG_LEAK(
("FeatureTask::GetOrCreateTableData - reuse existing TableData %p "
"[this=%p]",
data, this));
RefPtr<TableData> tableData = data;
tableData.forget(aData);
return NS_OK;
}
}
RefPtr<TableData> data = new TableData(aURIData, aTable);
mTables.AppendElement(data);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - create new "
"TableData %p [this=%p]",
data.get(), this));
data.forget(aData);
return NS_OK;
}
void FeatureTask::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aWorkerClassifier);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::DoLookup - starting lookup "
"[this=%p]",
this));
for (FeatureData& feature : mFeatures) {
feature.DoLookup(aWorkerClassifier);
}
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureTask::DoLookup - lookup completed "
"[this=%p]",
this));
}
void FeatureTask::CompleteClassification() {
MOZ_ASSERT(NS_IsMainThread());
for (FeatureData& feature : mFeatures) {
if (!feature.MaybeCompleteClassification(mChannel)) {
break;
}
}
UC_LOG(
("AsyncChannelClassifier::FeatureTask::CompleteClassification - complete "
"classification for "
"channel %p [this=%p]",
mChannel.get(), this));
mCallbackHolder->Exec();
}
nsresult FeatureData::InitializeList(
FeatureTask* aTask, nsIChannel* aChannel,
nsIUrlClassifierFeature::listType aListType,
nsTArray<RefPtr<TableData>>& aList) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTask);
MOZ_ASSERT(aChannel);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::InitializeList - initialize list "
"%d for channel %p [this=%p]",
aListType, aChannel, this));
nsCOMPtr<nsIURI> uri;
nsIUrlClassifierFeature::URIType URIType;
nsresult rv = mFeature->GetURIByListType(aChannel, aListType, &URIType,
getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (UC_LOG_ENABLED()) {
nsAutoCString errorName;
GetErrorName(rv, errorName);
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::InitializeList - Got an "
"unexpected error (rv=%s) [this=%p]",
errorName.get(), this));
}
return rv;
}
if (!uri) {
// Return success when the URI is empty to conitnue to do the lookup.
UC_LOG_LEAK(
("AsyncChannelClassifier::FeatureData::InitializeList - got an empty "
"URL [this=%p]",
this));
return NS_OK;
}
nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(uri);
if (NS_WARN_IF(!innermostURI)) {
return NS_ERROR_FAILURE;
}
nsAutoCString host;
rv = innermostURI->GetHost(host);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool found = false;
nsAutoCString tableName;
rv = mFeature->HasHostInPreferences(host, aListType, tableName, &found);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (found) {
mHostInPrefTables[aListType] = tableName;
}
RefPtr<URIData> uriData;
rv = aTask->GetOrCreateURIData(uri, innermostURI, URIType,
getter_AddRefs(uriData));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(uriData);
nsTArray<nsCString> tables;
rv = mFeature->GetTables(aListType, tables);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (const nsCString& table : tables) {
RefPtr<TableData> data;
rv = aTask->GetOrCreateTableData(uriData, table, getter_AddRefs(data));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(data);
aList.AppendElement(data);
}
return NS_OK;
}
} // namespace
/* static */
nsresult AsyncUrlChannelClassifier::CheckChannel(
nsIChannel* aChannel, std::function<void()>&& aCallback) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(aChannel);
if (!aCallback) {
return NS_ERROR_INVALID_ARG;
}
if (UC_LOG_ENABLED()) {
nsCOMPtr<nsIURI> chanURI;
if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(chanURI)))) {
nsCString chanSpec = chanURI->GetSpecOrDefault();
chanSpec.Truncate(
std::min(chanSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
nsCOMPtr<nsIURI> topWinURI;
Unused << UrlClassifierCommon::GetTopWindowURI(aChannel,
getter_AddRefs(topWinURI));
nsCString topWinSpec =
topWinURI ? topWinURI->GetSpecOrDefault() : "(null)"_ns;
topWinSpec.Truncate(
std::min(topWinSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
UC_LOG(
("AsyncUrlChannelClassifier::CheckChannel - starting the "
"classification on channel %p",
aChannel));
UC_LOG((" uri is %s [channel=%p]", chanSpec.get(), aChannel));
UC_LOG(
(" top-level uri is %s [channel=%p]", topWinSpec.get(), aChannel));
}
}
RefPtr<FeatureTask> task;
nsresult rv =
FeatureTask::Create(aChannel, std::move(aCallback), getter_AddRefs(task));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!task) {
// No task is needed for this channel, return an error so the caller won't
// wait for a callback.
return NS_ERROR_FAILURE;
}
RefPtr<nsUrlClassifierDBServiceWorker> workerClassifier =
nsUrlClassifierDBService::GetWorker();
if (NS_WARN_IF(!workerClassifier)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"AsyncUrlChannelClassifier::CheckChannel",
[task, workerClassifier]() -> void {
MOZ_ASSERT(!NS_IsMainThread());
task->DoLookup(workerClassifier);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"AsyncUrlChannelClassifier::CheckChannel - return",
[task]() -> void { task->CompleteClassification(); });
NS_DispatchToMainThread(r);
});
return nsUrlClassifierDBService::BackgroundThread()->Dispatch(
r, NS_DISPATCH_NORMAL);
}
} // namespace net
} // namespace mozilla