1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Classifier.h"
8 #include "mozilla/Components.h"
9 #include "mozilla/ErrorNames.h"
10 #include "mozilla/net/AsyncUrlChannelClassifier.h"
11 #include "mozilla/net/UrlClassifierCommon.h"
12 #include "mozilla/net/UrlClassifierFeatureFactory.h"
13 #include "mozilla/net/UrlClassifierFeatureResult.h"
14 #include "nsContentUtils.h"
15 #include "nsIChannel.h"
16 #include "nsIHttpChannel.h"
18 #include "nsNetUtil.h"
19 #include "nsPrintfCString.h"
20 #include "nsProxyRelease.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsUrlClassifierDBService.h"
23 #include "nsUrlClassifierUtils.h"
30 // Big picture comment
31 // -----------------------------------------------------------------------------
32 // nsUrlClassifierDBService::channelClassify() classifies a channel using a set
33 // of URL-Classifier features. This method minimizes the number of lookups and
34 // URI parsing and this is done using the classes here described.
36 // The first class is 'FeatureTask' which is able to retrieve the list of
37 // features for this channel using the feature-factory. See
38 // UrlClassifierFeatureFactory.
39 // For each feature, it creates a FeatureData object, which contains the
40 // entitylist and blocklist prefs and tables. The reason why we create
41 // FeatureData is because:
42 // - features are not thread-safe.
43 // - we want to store the state of the classification in the FeatureData
46 // It can happen that multiple features share the same tables. In order to do
47 // the lookup just once, we have TableData class. When multiple features
48 // contain the same table, they have references to the same couple TableData +
51 // During the classification, the channel's URIs are fragmented. In order to
52 // create these fragments just once, we use the URIData class, which is pointed
53 // by TableData classes.
55 // The creation of these classes happens on the main-thread. The classification
56 // happens on the worker thread.
59 // -----------------------------------------------------------------------------
61 // In order to avoid multiple URI parsing, we have this class which contains
62 // nsIURI and its fragments.
65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URIData
);
67 static nsresult
Create(nsIURI
* aURI
, nsIURI
* aInnermostURI
,
68 nsIUrlClassifierFeature::URIType aURIType
,
71 bool IsEqual(nsIURI
* aURI
) const;
73 const nsTArray
<nsCString
>& Fragments();
81 nsCOMPtr
<nsIURI
> mURI
;
83 nsTArray
<nsCString
> mFragments
;
84 nsIUrlClassifierFeature::URIType mURIType
;
88 nsresult
URIData::Create(nsIURI
* aURI
, nsIURI
* aInnermostURI
,
89 nsIUrlClassifierFeature::URIType aURIType
,
91 MOZ_ASSERT(NS_IsMainThread());
93 MOZ_ASSERT(aInnermostURI
);
95 RefPtr
<URIData
> data
= new URIData();
97 data
->mURIType
= aURIType
;
99 nsUrlClassifierUtils
* utilsService
= nsUrlClassifierUtils::GetInstance();
100 if (NS_WARN_IF(!utilsService
)) {
101 return NS_ERROR_FAILURE
;
104 nsresult rv
= utilsService
->GetKeyForURI(aInnermostURI
, data
->mURISpec
);
105 if (NS_WARN_IF(NS_FAILED(rv
))) {
110 ("AsyncChannelClassifier::URIData::Create new URIData created for spec "
112 data
->mURISpec
.get(), data
.get()));
118 URIData::URIData() { MOZ_ASSERT(NS_IsMainThread()); }
120 bool URIData::IsEqual(nsIURI
* aURI
) const {
121 MOZ_ASSERT(NS_IsMainThread());
124 bool isEqual
= false;
125 nsresult rv
= mURI
->Equals(aURI
, &isEqual
);
126 if (NS_WARN_IF(NS_FAILED(rv
))) {
133 const nsTArray
<nsCString
>& URIData::Fragments() {
134 MOZ_ASSERT(!NS_IsMainThread());
136 if (mFragments
.IsEmpty()) {
139 if (mURIType
== nsIUrlClassifierFeature::pairwiseEntitylistURI
) {
140 rv
= LookupCache::GetLookupEntitylistFragments(mURISpec
, &mFragments
);
142 rv
= LookupCache::GetLookupFragments(mURISpec
, &mFragments
);
145 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
151 nsIURI
* URIData::URI() const {
152 MOZ_ASSERT(NS_IsMainThread());
157 // ----------------------------------------------------------------------------
159 // In order to avoid multiple lookups on the same table + URI, we have this
163 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableData
);
171 TableData(URIData
* aURIData
, const nsACString
& aTable
);
175 const nsACString
& Table() const;
177 const LookupResultArray
& Result() const;
179 State
MatchState() const;
181 bool IsEqual(URIData
* aURIData
, const nsACString
& aTable
) const;
183 // Returns true if the table classifies the URI. This method must be called
184 // on hte classifier worker thread.
185 bool DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
);
190 RefPtr
<URIData
> mURIData
;
194 LookupResultArray mResults
;
197 TableData::TableData(URIData
* aURIData
, const nsACString
& aTable
)
198 : mURIData(aURIData
), mState(eUnclassified
), mTable(aTable
) {
199 MOZ_ASSERT(NS_IsMainThread());
200 MOZ_ASSERT(aURIData
);
203 ("AsyncChannelClassifier::TableData CTOR - new TableData created %s "
205 aTable
.BeginReading(), this));
208 TableData::~TableData() = default;
210 nsIURI
* TableData::URI() const {
211 MOZ_ASSERT(NS_IsMainThread());
212 return mURIData
->URI();
215 const nsACString
& TableData::Table() const {
216 MOZ_ASSERT(NS_IsMainThread());
220 const LookupResultArray
& TableData::Result() const {
221 MOZ_ASSERT(NS_IsMainThread());
225 TableData::State
TableData::MatchState() const {
226 MOZ_ASSERT(NS_IsMainThread());
230 bool TableData::IsEqual(URIData
* aURIData
, const nsACString
& aTable
) const {
231 MOZ_ASSERT(NS_IsMainThread());
232 return mURIData
== aURIData
&& mTable
== aTable
;
235 bool TableData::DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
) {
236 MOZ_ASSERT(!NS_IsMainThread());
237 MOZ_ASSERT(aWorkerClassifier
);
239 if (mState
== TableData::eUnclassified
) {
241 ("AsyncChannelClassifier::TableData::DoLookup - starting lookup "
245 const nsTArray
<nsCString
>& fragments
= mURIData
->Fragments();
246 nsresult rv
= aWorkerClassifier
->DoSingleLocalLookupWithURIFragments(
247 fragments
, mTable
, mResults
);
248 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
250 mState
= mResults
.IsEmpty() ? TableData::eNoMatch
: TableData::eMatch
;
253 ("AsyncChannelClassifier::TableData::DoLookup - lookup completed. "
254 "Matches: %d [this=%p]",
255 (int)mResults
.Length(), this));
258 return !mResults
.IsEmpty();
262 // ----------------------------------------------------------------------------
266 // This is class contains all the Feature data.
276 FeatureData() = default;
279 nsresult
Initialize(FeatureTask
* aTask
, nsIChannel
* aChannel
,
280 nsIUrlClassifierFeature
* aFeature
);
282 void DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
);
284 // Returns true if the next feature should be processed.
285 bool MaybeCompleteClassification(nsIChannel
* aChannel
);
288 nsresult
InitializeList(FeatureTask
* aTask
, nsIChannel
* aChannel
,
289 nsIUrlClassifierFeature::listType aListType
,
290 nsTArray
<RefPtr
<TableData
>>& aList
);
292 State mState
{eUnclassified
};
293 nsCOMPtr
<nsIUrlClassifierFeature
> mFeature
;
294 nsCOMPtr
<nsIChannel
> mChannel
;
296 nsTArray
<RefPtr
<TableData
>> mBlocklistTables
;
297 nsTArray
<RefPtr
<TableData
>> mEntitylistTables
;
299 // blocklist + entitylist.
300 nsCString mHostInPrefTables
[2];
303 FeatureData::~FeatureData() {
304 NS_ReleaseOnMainThread("FeatureData:mFeature", mFeature
.forget());
307 nsresult
FeatureData::Initialize(FeatureTask
* aTask
, nsIChannel
* aChannel
,
308 nsIUrlClassifierFeature
* aFeature
) {
309 MOZ_ASSERT(NS_IsMainThread());
311 MOZ_ASSERT(aChannel
);
312 MOZ_ASSERT(aFeature
);
314 if (UC_LOG_ENABLED()) {
316 aFeature
->GetName(name
);
318 ("AsyncChannelClassifier::FeatureData::Initialize - Feature %s "
319 "[this=%p, channel=%p]",
320 name
.get(), this, aChannel
));
326 nsresult rv
= InitializeList(
327 aTask
, aChannel
, nsIUrlClassifierFeature::blocklist
, mBlocklistTables
);
328 if (NS_WARN_IF(NS_FAILED(rv
))) {
332 rv
= InitializeList(aTask
, aChannel
, nsIUrlClassifierFeature::entitylist
,
341 void FeatureData::DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
) {
342 MOZ_ASSERT(!NS_IsMainThread());
343 MOZ_ASSERT(aWorkerClassifier
);
344 MOZ_ASSERT(mState
== eUnclassified
);
347 ("AsyncChannelClassifier::FeatureData::DoLookup - lookup starting "
351 // This is wrong, but it's fast: we don't want to check if the host is in the
352 // blocklist table if we know that it's going to be entitylisted by pref.
353 // So, also if maybe it's not blocklisted, let's consider it 'entitylisted'.
354 if (!mHostInPrefTables
[nsIUrlClassifierFeature::entitylist
].IsEmpty()) {
356 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by pref "
359 mState
= eMatchEntitylist
;
363 // Let's check if this feature blocklists the URI.
366 !mHostInPrefTables
[nsIUrlClassifierFeature::blocklist
].IsEmpty();
369 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted by pref: "
371 isBlocklisted
, this));
373 if (!isBlocklisted
) {
374 // If one of the blocklist table matches the URI, we don't need to continue
375 // with the others: the feature is blocklisted (but maybe also
377 for (TableData
* tableData
: mBlocklistTables
) {
378 if (tableData
->DoLookup(aWorkerClassifier
)) {
379 isBlocklisted
= true;
386 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted before "
387 "entitylisting: %d [this=%p]",
388 isBlocklisted
, this));
390 if (!isBlocklisted
) {
395 // Now, let's check if we need to entitylist the same URI.
397 for (TableData
* tableData
: mEntitylistTables
) {
398 // If one of the entitylist table matches the URI, we don't need to continue
399 // with the others: the feature is entitylisted.
400 if (tableData
->DoLookup(aWorkerClassifier
)) {
402 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by "
405 mState
= eMatchEntitylist
;
411 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted [this=%p]",
413 mState
= eMatchBlocklist
;
416 bool FeatureData::MaybeCompleteClassification(nsIChannel
* aChannel
) {
417 MOZ_ASSERT(NS_IsMainThread());
420 mFeature
->GetName(name
);
423 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
425 "classification [this=%p channel=%p]",
431 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
432 "no match for feature %s. Let's "
433 "move on [this=%p channel=%p]",
434 name
.get(), this, aChannel
));
437 case eMatchEntitylist
:
439 ("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification "
440 "- entitylisted by feature %s. Let's "
441 "move on [this=%p channel=%p]",
442 name
.get(), this, aChannel
));
445 case eMatchBlocklist
:
447 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
448 "blocklisted by feature %s [this=%p channel=%p]",
449 name
.get(), this, aChannel
));
453 MOZ_CRASH("We should not be here!");
457 MOZ_ASSERT(mState
== eMatchBlocklist
);
459 // Maybe we have to ignore this host
460 nsAutoCString exceptionList
;
461 nsresult rv
= mFeature
->GetExceptionHostList(exceptionList
);
462 if (NS_WARN_IF(NS_FAILED(rv
))) {
464 ("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification - "
465 "error. Let's move on [this=%p channel=%p]",
470 if (!mBlocklistTables
.IsEmpty() &&
471 nsContentUtils::IsURIInList(mBlocklistTables
[0]->URI(), exceptionList
)) {
472 nsCString spec
= mBlocklistTables
[0]->URI()->GetSpecOrDefault();
473 spec
.Truncate(std::min(spec
.Length(), UrlClassifierCommon::sMaxSpecLength
));
475 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
477 "exceptionlist of feature %s [this=%p channel=%p]",
478 spec
.get(), name
.get(), this, aChannel
));
482 nsTArray
<nsCString
> list
;
483 nsTArray
<nsCString
> hashes
;
484 if (!mHostInPrefTables
[nsIUrlClassifierFeature::blocklist
].IsEmpty()) {
485 list
.AppendElement(mHostInPrefTables
[nsIUrlClassifierFeature::blocklist
]);
487 // Telemetry expects every tracking channel has hash, create it for test
490 complete
.FromPlaintext(
491 mHostInPrefTables
[nsIUrlClassifierFeature::blocklist
]);
492 hashes
.AppendElement(complete
.ToString());
495 for (TableData
* tableData
: mBlocklistTables
) {
496 if (tableData
->MatchState() == TableData::eMatch
) {
497 list
.AppendElement(tableData
->Table());
499 for (const auto& r
: tableData
->Result()) {
500 hashes
.AppendElement(r
->hash
.complete
.ToString());
506 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
507 "process channel [this=%p channel=%p]",
510 bool shouldContinue
= false;
511 rv
= mFeature
->ProcessChannel(aChannel
, list
, hashes
, &shouldContinue
);
512 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
514 return shouldContinue
;
518 // ----------------------------------------------------------------------------
520 // This class keeps the callback alive and makes sure that we release it on the
522 class CallbackHolder final
{
524 NS_INLINE_DECL_REFCOUNTING(CallbackHolder
);
526 explicit CallbackHolder(std::function
<void()>&& aCallback
)
527 : mCallback(std::move(aCallback
)) {}
529 void Exec() const { mCallback(); }
532 ~CallbackHolder() = default;
534 std::function
<void()> mCallback
;
538 // ----------------------------------------------------------------------------
540 // A FeatureTask is a class that is able to classify a channel using a set of
541 // features. The features are grouped by:
542 // - URIs - to avoid extra URI parsing.
543 // - Tables - to avoid multiple lookup on the same table.
546 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureTask
);
548 static nsresult
Create(nsIChannel
* aChannel
,
549 std::function
<void()>&& aCallback
,
550 FeatureTask
** aTask
);
552 // Called on the classifier thread.
553 void DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
);
555 // Called on the main-thread to process the channel.
556 void CompleteClassification();
558 nsresult
GetOrCreateURIData(nsIURI
* aURI
, nsIURI
* aInnermostURI
,
559 nsIUrlClassifierFeature::URIType aURIType
,
562 nsresult
GetOrCreateTableData(URIData
* aURIData
, const nsACString
& aTable
,
566 FeatureTask(nsIChannel
* aChannel
, std::function
<void()>&& aCallback
);
569 nsCOMPtr
<nsIChannel
> mChannel
;
570 RefPtr
<CallbackHolder
> mCallbackHolder
;
572 nsTArray
<FeatureData
> mFeatures
;
573 nsTArray
<RefPtr
<URIData
>> mURIs
;
574 nsTArray
<RefPtr
<TableData
>> mTables
;
577 // Features are able to classify particular URIs from a channel. For instance,
578 // tracking-annotation feature uses the top-level URI to entitylist the current
579 // channel's URI. Because of
580 // this, this function aggregates feature per URI and tables.
582 nsresult
FeatureTask::Create(nsIChannel
* aChannel
,
583 std::function
<void()>&& aCallback
,
584 FeatureTask
** aTask
) {
585 MOZ_ASSERT(NS_IsMainThread());
586 MOZ_ASSERT(aChannel
);
589 // We need to obtain the list of nsIUrlClassifierFeature objects able to
590 // classify this channel. If the list is empty, we do an early return.
591 nsTArray
<nsCOMPtr
<nsIUrlClassifierFeature
>> features
;
592 UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel
, features
);
593 if (features
.IsEmpty()) {
595 ("AsyncChannelClassifier::FeatureTask::Create - no task is needed for "
601 RefPtr
<FeatureTask
> task
= new FeatureTask(aChannel
, std::move(aCallback
));
604 ("AsyncChannelClassifier::FeatureTask::Create - FeatureTask %p created "
606 task
.get(), aChannel
));
608 for (nsIUrlClassifierFeature
* feature
: features
) {
609 FeatureData
* featureData
= task
->mFeatures
.AppendElement();
610 nsresult rv
= featureData
->Initialize(task
, aChannel
, feature
);
620 FeatureTask::FeatureTask(nsIChannel
* aChannel
,
621 std::function
<void()>&& aCallback
)
622 : mChannel(aChannel
) {
623 MOZ_ASSERT(NS_IsMainThread());
624 MOZ_ASSERT(mChannel
);
626 std::function
<void()> callback
= std::move(aCallback
);
627 mCallbackHolder
= new CallbackHolder(std::move(callback
));
630 FeatureTask::~FeatureTask() {
631 NS_ReleaseOnMainThread("FeatureTask::mChannel", mChannel
.forget());
632 NS_ReleaseOnMainThread("FeatureTask::mCallbackHolder",
633 mCallbackHolder
.forget());
636 nsresult
FeatureTask::GetOrCreateURIData(
637 nsIURI
* aURI
, nsIURI
* aInnermostURI
,
638 nsIUrlClassifierFeature::URIType aURIType
, URIData
** aData
) {
639 MOZ_ASSERT(NS_IsMainThread());
641 MOZ_ASSERT(aInnermostURI
);
645 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - checking if "
650 for (URIData
* data
: mURIs
) {
651 if (data
->IsEqual(aURI
)) {
653 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - reuse "
654 "existing URIData %p [this=%p]",
657 RefPtr
<URIData
> uriData
= data
;
658 uriData
.forget(aData
);
663 RefPtr
<URIData
> data
;
665 URIData::Create(aURI
, aInnermostURI
, aURIType
, getter_AddRefs(data
));
666 if (NS_WARN_IF(NS_FAILED(rv
))) {
670 mURIs
.AppendElement(data
);
673 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - create new "
674 "URIData %p [this=%p]",
681 nsresult
FeatureTask::GetOrCreateTableData(URIData
* aURIData
,
682 const nsACString
& aTable
,
684 MOZ_ASSERT(NS_IsMainThread());
685 MOZ_ASSERT(aURIData
);
689 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - checking "
690 "if TableData must be "
694 for (TableData
* data
: mTables
) {
695 if (data
->IsEqual(aURIData
, aTable
)) {
697 ("FeatureTask::GetOrCreateTableData - reuse existing TableData %p "
701 RefPtr
<TableData
> tableData
= data
;
702 tableData
.forget(aData
);
707 RefPtr
<TableData
> data
= new TableData(aURIData
, aTable
);
708 mTables
.AppendElement(data
);
711 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - create new "
712 "TableData %p [this=%p]",
719 void FeatureTask::DoLookup(nsUrlClassifierDBServiceWorker
* aWorkerClassifier
) {
720 MOZ_ASSERT(!NS_IsMainThread());
721 MOZ_ASSERT(aWorkerClassifier
);
724 ("AsyncChannelClassifier::FeatureTask::DoLookup - starting lookup "
728 for (FeatureData
& feature
: mFeatures
) {
729 feature
.DoLookup(aWorkerClassifier
);
733 ("AsyncChannelClassifier::FeatureTask::DoLookup - lookup completed "
738 void FeatureTask::CompleteClassification() {
739 MOZ_ASSERT(NS_IsMainThread());
741 for (FeatureData
& feature
: mFeatures
) {
742 if (!feature
.MaybeCompleteClassification(mChannel
)) {
748 ("AsyncChannelClassifier::FeatureTask::CompleteClassification - complete "
749 "classification for "
750 "channel %p [this=%p]",
751 mChannel
.get(), this));
753 mCallbackHolder
->Exec();
756 nsresult
FeatureData::InitializeList(
757 FeatureTask
* aTask
, nsIChannel
* aChannel
,
758 nsIUrlClassifierFeature::listType aListType
,
759 nsTArray
<RefPtr
<TableData
>>& aList
) {
760 MOZ_ASSERT(NS_IsMainThread());
762 MOZ_ASSERT(aChannel
);
765 ("AsyncChannelClassifier::FeatureData::InitializeList - initialize list "
766 "%d for channel %p [this=%p]",
767 aListType
, aChannel
, this));
769 nsCOMPtr
<nsIURI
> uri
;
770 nsIUrlClassifierFeature::URIType URIType
;
771 nsresult rv
= mFeature
->GetURIByListType(aChannel
, aListType
, &URIType
,
772 getter_AddRefs(uri
));
774 if (UC_LOG_ENABLED()) {
775 nsAutoCString errorName
;
776 GetErrorName(rv
, errorName
);
778 ("AsyncChannelClassifier::FeatureData::InitializeList - Got an "
779 "unexpected error (rv=%s) [this=%p]",
780 errorName
.get(), this));
786 // Return success when the URI is empty to conitnue to do the lookup.
788 ("AsyncChannelClassifier::FeatureData::InitializeList - got an empty "
794 nsCOMPtr
<nsIURI
> innermostURI
= NS_GetInnermostURI(uri
);
795 if (NS_WARN_IF(!innermostURI
)) {
796 return NS_ERROR_FAILURE
;
800 rv
= innermostURI
->GetHost(host
);
801 if (NS_WARN_IF(NS_FAILED(rv
))) {
806 nsAutoCString tableName
;
807 rv
= mFeature
->HasHostInPreferences(host
, aListType
, tableName
, &found
);
808 if (NS_WARN_IF(NS_FAILED(rv
))) {
813 mHostInPrefTables
[aListType
] = tableName
;
816 RefPtr
<URIData
> uriData
;
817 rv
= aTask
->GetOrCreateURIData(uri
, innermostURI
, URIType
,
818 getter_AddRefs(uriData
));
819 if (NS_WARN_IF(NS_FAILED(rv
))) {
825 nsTArray
<nsCString
> tables
;
826 rv
= mFeature
->GetTables(aListType
, tables
);
827 if (NS_WARN_IF(NS_FAILED(rv
))) {
831 for (const nsCString
& table
: tables
) {
832 RefPtr
<TableData
> data
;
833 rv
= aTask
->GetOrCreateTableData(uriData
, table
, getter_AddRefs(data
));
834 if (NS_WARN_IF(NS_FAILED(rv
))) {
839 aList
.AppendElement(data
);
848 nsresult
AsyncUrlChannelClassifier::CheckChannel(
849 nsIChannel
* aChannel
, std::function
<void()>&& aCallback
) {
850 MOZ_ASSERT(XRE_IsParentProcess());
851 MOZ_ASSERT(aChannel
);
854 return NS_ERROR_INVALID_ARG
;
857 if (UC_LOG_ENABLED()) {
858 nsCOMPtr
<nsIURI
> chanURI
;
859 if (NS_SUCCEEDED(aChannel
->GetURI(getter_AddRefs(chanURI
)))) {
860 nsCString chanSpec
= chanURI
->GetSpecOrDefault();
862 std::min(chanSpec
.Length(), UrlClassifierCommon::sMaxSpecLength
));
864 nsCOMPtr
<nsIURI
> topWinURI
;
865 Unused
<< UrlClassifierCommon::GetTopWindowURI(aChannel
,
866 getter_AddRefs(topWinURI
));
867 nsCString topWinSpec
=
868 topWinURI
? topWinURI
->GetSpecOrDefault() : "(null)"_ns
;
871 std::min(topWinSpec
.Length(), UrlClassifierCommon::sMaxSpecLength
));
874 ("AsyncUrlChannelClassifier::CheckChannel - starting the "
875 "classification on channel %p",
877 UC_LOG((" uri is %s [channel=%p]", chanSpec
.get(), aChannel
));
879 (" top-level uri is %s [channel=%p]", topWinSpec
.get(), aChannel
));
883 RefPtr
<FeatureTask
> task
;
885 FeatureTask::Create(aChannel
, std::move(aCallback
), getter_AddRefs(task
));
891 // No task is needed for this channel, return an error so the caller won't
892 // wait for a callback.
893 return NS_ERROR_FAILURE
;
896 RefPtr
<nsUrlClassifierDBServiceWorker
> workerClassifier
=
897 nsUrlClassifierDBService::GetWorker();
898 if (NS_WARN_IF(!workerClassifier
)) {
899 return NS_ERROR_FAILURE
;
902 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
903 "AsyncUrlChannelClassifier::CheckChannel",
904 [task
, workerClassifier
]() -> void {
905 MOZ_ASSERT(!NS_IsMainThread());
906 task
->DoLookup(workerClassifier
);
908 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
909 "AsyncUrlChannelClassifier::CheckChannel - return",
910 [task
]() -> void { task
->CompleteClassification(); });
912 NS_DispatchToMainThread(r
);
915 return nsUrlClassifierDBService::BackgroundThread()->Dispatch(
916 r
, NS_DISPATCH_NORMAL
);
920 } // namespace mozilla