Bug 1901077 - Switch GIF decoder to yield new frames at terminating block. r=tnikkel
[gecko.git] / netwerk / url-classifier / AsyncUrlChannelClassifier.cpp
blob4dfb7224e7d989639f85dcdf3eb13db966fa6ff1
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"
17 #include "nsNetCID.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"
25 namespace mozilla {
26 namespace net {
28 namespace {
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
44 // object.
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 +
49 // URIData objects.
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.
58 // URIData
59 // -----------------------------------------------------------------------------
61 // In order to avoid multiple URI parsing, we have this class which contains
62 // nsIURI and its fragments.
63 class URIData {
64 public:
65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URIData);
67 static nsresult Create(nsIURI* aURI, nsIURI* aInnermostURI,
68 nsIUrlClassifierFeature::URIType aURIType,
69 URIData** aData);
71 bool IsEqual(nsIURI* aURI) const;
73 const nsTArray<nsCString>& Fragments();
75 nsIURI* URI() const;
77 private:
78 URIData();
79 ~URIData() = default;
81 nsCOMPtr<nsIURI> mURI;
82 nsCString mURISpec;
83 nsTArray<nsCString> mFragments;
84 nsIUrlClassifierFeature::URIType mURIType;
87 /* static */
88 nsresult URIData::Create(nsIURI* aURI, nsIURI* aInnermostURI,
89 nsIUrlClassifierFeature::URIType aURIType,
90 URIData** aData) {
91 MOZ_ASSERT(NS_IsMainThread());
92 MOZ_ASSERT(aURI);
93 MOZ_ASSERT(aInnermostURI);
95 RefPtr<URIData> data = new URIData();
96 data->mURI = aURI;
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))) {
106 return rv;
109 UC_LOG_LEAK(
110 ("AsyncChannelClassifier::URIData::Create new URIData created for spec "
111 "%s [this=%p]",
112 data->mURISpec.get(), data.get()));
114 data.forget(aData);
115 return NS_OK;
118 URIData::URIData() { MOZ_ASSERT(NS_IsMainThread()); }
120 bool URIData::IsEqual(nsIURI* aURI) const {
121 MOZ_ASSERT(NS_IsMainThread());
122 MOZ_ASSERT(aURI);
124 bool isEqual = false;
125 nsresult rv = mURI->Equals(aURI, &isEqual);
126 if (NS_WARN_IF(NS_FAILED(rv))) {
127 return false;
130 return isEqual;
133 const nsTArray<nsCString>& URIData::Fragments() {
134 MOZ_ASSERT(!NS_IsMainThread());
136 if (mFragments.IsEmpty()) {
137 nsresult rv;
139 if (mURIType == nsIUrlClassifierFeature::pairwiseEntitylistURI) {
140 rv = LookupCache::GetLookupEntitylistFragments(mURISpec, &mFragments);
141 } else {
142 rv = LookupCache::GetLookupFragments(mURISpec, &mFragments);
145 Unused << NS_WARN_IF(NS_FAILED(rv));
148 return mFragments;
151 nsIURI* URIData::URI() const {
152 MOZ_ASSERT(NS_IsMainThread());
153 return mURI;
156 // TableData
157 // ----------------------------------------------------------------------------
159 // In order to avoid multiple lookups on the same table + URI, we have this
160 // class.
161 class TableData {
162 public:
163 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableData);
165 enum State {
166 eUnclassified,
167 eNoMatch,
168 eMatch,
171 TableData(URIData* aURIData, const nsACString& aTable);
173 nsIURI* URI() const;
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);
187 private:
188 ~TableData();
190 RefPtr<URIData> mURIData;
191 State mState;
193 nsCString mTable;
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);
202 UC_LOG_LEAK(
203 ("AsyncChannelClassifier::TableData CTOR - new TableData created %s "
204 "[this=%p]",
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());
217 return mTable;
220 const LookupResultArray& TableData::Result() const {
221 MOZ_ASSERT(NS_IsMainThread());
222 return mResults;
225 TableData::State TableData::MatchState() const {
226 MOZ_ASSERT(NS_IsMainThread());
227 return mState;
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) {
240 UC_LOG_LEAK(
241 ("AsyncChannelClassifier::TableData::DoLookup - starting lookup "
242 "[this=%p]",
243 this));
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;
252 UC_LOG_LEAK(
253 ("AsyncChannelClassifier::TableData::DoLookup - lookup completed. "
254 "Matches: %d [this=%p]",
255 (int)mResults.Length(), this));
258 return !mResults.IsEmpty();
261 // FeatureData
262 // ----------------------------------------------------------------------------
264 class FeatureTask;
266 // This is class contains all the Feature data.
267 class FeatureData {
268 enum State {
269 eUnclassified,
270 eNoMatch,
271 eMatchBlocklist,
272 eMatchEntitylist,
275 public:
276 FeatureData() = default;
277 ~FeatureData();
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);
287 private:
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());
310 MOZ_ASSERT(aTask);
311 MOZ_ASSERT(aChannel);
312 MOZ_ASSERT(aFeature);
314 if (UC_LOG_ENABLED()) {
315 nsAutoCString name;
316 aFeature->GetName(name);
317 UC_LOG_LEAK(
318 ("AsyncChannelClassifier::FeatureData::Initialize - Feature %s "
319 "[this=%p, channel=%p]",
320 name.get(), this, aChannel));
323 mFeature = aFeature;
324 mChannel = aChannel;
326 nsresult rv = InitializeList(
327 aTask, aChannel, nsIUrlClassifierFeature::blocklist, mBlocklistTables);
328 if (NS_WARN_IF(NS_FAILED(rv))) {
329 return rv;
332 rv = InitializeList(aTask, aChannel, nsIUrlClassifierFeature::entitylist,
333 mEntitylistTables);
334 if (NS_FAILED(rv)) {
335 return rv;
338 return NS_OK;
341 void FeatureData::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) {
342 MOZ_ASSERT(!NS_IsMainThread());
343 MOZ_ASSERT(aWorkerClassifier);
344 MOZ_ASSERT(mState == eUnclassified);
346 UC_LOG_LEAK(
347 ("AsyncChannelClassifier::FeatureData::DoLookup - lookup starting "
348 "[this=%p]",
349 this));
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()) {
355 UC_LOG_LEAK(
356 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by pref "
357 "[this=%p]",
358 this));
359 mState = eMatchEntitylist;
360 return;
363 // Let's check if this feature blocklists the URI.
365 bool isBlocklisted =
366 !mHostInPrefTables[nsIUrlClassifierFeature::blocklist].IsEmpty();
368 UC_LOG_LEAK(
369 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted by pref: "
370 "%d [this=%p]",
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
376 // entitylisted).
377 for (TableData* tableData : mBlocklistTables) {
378 if (tableData->DoLookup(aWorkerClassifier)) {
379 isBlocklisted = true;
380 break;
385 UC_LOG_LEAK(
386 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted before "
387 "entitylisting: %d [this=%p]",
388 isBlocklisted, this));
390 if (!isBlocklisted) {
391 mState = eNoMatch;
392 return;
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)) {
401 UC_LOG_LEAK(
402 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by "
403 "table [this=%p]",
404 this));
405 mState = eMatchEntitylist;
406 return;
410 UC_LOG_LEAK(
411 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted [this=%p]",
412 this));
413 mState = eMatchBlocklist;
416 bool FeatureData::MaybeCompleteClassification(nsIChannel* aChannel) {
417 MOZ_ASSERT(NS_IsMainThread());
419 nsAutoCString name;
420 mFeature->GetName(name);
422 UC_LOG_LEAK(
423 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
424 "completing "
425 "classification [this=%p channel=%p]",
426 this, aChannel));
428 switch (mState) {
429 case eNoMatch:
430 UC_LOG(
431 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
432 "no match for feature %s. Let's "
433 "move on [this=%p channel=%p]",
434 name.get(), this, aChannel));
435 return true;
437 case eMatchEntitylist:
438 UC_LOG(
439 ("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification "
440 "- entitylisted by feature %s. Let's "
441 "move on [this=%p channel=%p]",
442 name.get(), this, aChannel));
443 return true;
445 case eMatchBlocklist:
446 UC_LOG(
447 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
448 "blocklisted by feature %s [this=%p channel=%p]",
449 name.get(), this, aChannel));
450 break;
452 case eUnclassified:
453 MOZ_CRASH("We should not be here!");
454 break;
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))) {
463 UC_LOG_WARN(
464 ("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification - "
465 "error. Let's move on [this=%p channel=%p]",
466 this, aChannel));
467 return true;
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));
474 UC_LOG(
475 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
476 "uri %s found in "
477 "exceptionlist of feature %s [this=%p channel=%p]",
478 spec.get(), name.get(), this, aChannel));
479 return true;
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
488 // entry
489 Completion complete;
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());
505 UC_LOG_LEAK(
506 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - "
507 "process channel [this=%p channel=%p]",
508 this, aChannel));
510 bool shouldContinue = false;
511 rv = mFeature->ProcessChannel(aChannel, list, hashes, &shouldContinue);
512 Unused << NS_WARN_IF(NS_FAILED(rv));
514 return shouldContinue;
517 // CallbackHolder
518 // ----------------------------------------------------------------------------
520 // This class keeps the callback alive and makes sure that we release it on the
521 // correct thread.
522 class CallbackHolder final {
523 public:
524 NS_INLINE_DECL_REFCOUNTING(CallbackHolder);
526 explicit CallbackHolder(std::function<void()>&& aCallback)
527 : mCallback(std::move(aCallback)) {}
529 void Exec() const { mCallback(); }
531 private:
532 ~CallbackHolder() = default;
534 std::function<void()> mCallback;
537 // FeatureTask
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.
544 class FeatureTask {
545 public:
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,
560 URIData** aData);
562 nsresult GetOrCreateTableData(URIData* aURIData, const nsACString& aTable,
563 TableData** aData);
565 private:
566 FeatureTask(nsIChannel* aChannel, std::function<void()>&& aCallback);
567 ~FeatureTask();
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.
581 /* static */
582 nsresult FeatureTask::Create(nsIChannel* aChannel,
583 std::function<void()>&& aCallback,
584 FeatureTask** aTask) {
585 MOZ_ASSERT(NS_IsMainThread());
586 MOZ_ASSERT(aChannel);
587 MOZ_ASSERT(aTask);
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()) {
594 UC_LOG(
595 ("AsyncChannelClassifier::FeatureTask::Create - no task is needed for "
596 "channel %p",
597 aChannel));
598 return NS_OK;
601 RefPtr<FeatureTask> task = new FeatureTask(aChannel, std::move(aCallback));
603 UC_LOG(
604 ("AsyncChannelClassifier::FeatureTask::Create - FeatureTask %p created "
605 "for channel %p",
606 task.get(), aChannel));
608 for (nsIUrlClassifierFeature* feature : features) {
609 FeatureData* featureData = task->mFeatures.AppendElement();
610 nsresult rv = featureData->Initialize(task, aChannel, feature);
611 if (NS_FAILED(rv)) {
612 return rv;
616 task.forget(aTask);
617 return NS_OK;
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());
640 MOZ_ASSERT(aURI);
641 MOZ_ASSERT(aInnermostURI);
642 MOZ_ASSERT(aData);
644 UC_LOG_LEAK(
645 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - checking if "
646 "a URIData must be "
647 "created [this=%p]",
648 this));
650 for (URIData* data : mURIs) {
651 if (data->IsEqual(aURI)) {
652 UC_LOG_LEAK(
653 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - reuse "
654 "existing URIData %p [this=%p]",
655 data, this));
657 RefPtr<URIData> uriData = data;
658 uriData.forget(aData);
659 return NS_OK;
663 RefPtr<URIData> data;
664 nsresult rv =
665 URIData::Create(aURI, aInnermostURI, aURIType, getter_AddRefs(data));
666 if (NS_WARN_IF(NS_FAILED(rv))) {
667 return rv;
670 mURIs.AppendElement(data);
672 UC_LOG_LEAK(
673 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - create new "
674 "URIData %p [this=%p]",
675 data.get(), this));
677 data.forget(aData);
678 return NS_OK;
681 nsresult FeatureTask::GetOrCreateTableData(URIData* aURIData,
682 const nsACString& aTable,
683 TableData** aData) {
684 MOZ_ASSERT(NS_IsMainThread());
685 MOZ_ASSERT(aURIData);
686 MOZ_ASSERT(aData);
688 UC_LOG_LEAK(
689 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - checking "
690 "if TableData must be "
691 "created [this=%p]",
692 this));
694 for (TableData* data : mTables) {
695 if (data->IsEqual(aURIData, aTable)) {
696 UC_LOG_LEAK(
697 ("FeatureTask::GetOrCreateTableData - reuse existing TableData %p "
698 "[this=%p]",
699 data, this));
701 RefPtr<TableData> tableData = data;
702 tableData.forget(aData);
703 return NS_OK;
707 RefPtr<TableData> data = new TableData(aURIData, aTable);
708 mTables.AppendElement(data);
710 UC_LOG_LEAK(
711 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - create new "
712 "TableData %p [this=%p]",
713 data.get(), this));
715 data.forget(aData);
716 return NS_OK;
719 void FeatureTask::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) {
720 MOZ_ASSERT(!NS_IsMainThread());
721 MOZ_ASSERT(aWorkerClassifier);
723 UC_LOG_LEAK(
724 ("AsyncChannelClassifier::FeatureTask::DoLookup - starting lookup "
725 "[this=%p]",
726 this));
728 for (FeatureData& feature : mFeatures) {
729 feature.DoLookup(aWorkerClassifier);
732 UC_LOG_LEAK(
733 ("AsyncChannelClassifier::FeatureTask::DoLookup - lookup completed "
734 "[this=%p]",
735 this));
738 void FeatureTask::CompleteClassification() {
739 MOZ_ASSERT(NS_IsMainThread());
741 for (FeatureData& feature : mFeatures) {
742 if (!feature.MaybeCompleteClassification(mChannel)) {
743 break;
747 UC_LOG(
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());
761 MOZ_ASSERT(aTask);
762 MOZ_ASSERT(aChannel);
764 UC_LOG_LEAK(
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));
773 if (NS_FAILED(rv)) {
774 if (UC_LOG_ENABLED()) {
775 nsAutoCString errorName;
776 GetErrorName(rv, errorName);
777 UC_LOG_LEAK(
778 ("AsyncChannelClassifier::FeatureData::InitializeList - Got an "
779 "unexpected error (rv=%s) [this=%p]",
780 errorName.get(), this));
782 return rv;
785 if (!uri) {
786 // Return success when the URI is empty to conitnue to do the lookup.
787 UC_LOG_LEAK(
788 ("AsyncChannelClassifier::FeatureData::InitializeList - got an empty "
789 "URL [this=%p]",
790 this));
791 return NS_OK;
794 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(uri);
795 if (NS_WARN_IF(!innermostURI)) {
796 return NS_ERROR_FAILURE;
799 nsAutoCString host;
800 rv = innermostURI->GetHost(host);
801 if (NS_WARN_IF(NS_FAILED(rv))) {
802 return rv;
805 bool found = false;
806 nsAutoCString tableName;
807 rv = mFeature->HasHostInPreferences(host, aListType, tableName, &found);
808 if (NS_WARN_IF(NS_FAILED(rv))) {
809 return rv;
812 if (found) {
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))) {
820 return rv;
823 MOZ_ASSERT(uriData);
825 nsTArray<nsCString> tables;
826 rv = mFeature->GetTables(aListType, tables);
827 if (NS_WARN_IF(NS_FAILED(rv))) {
828 return 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))) {
835 return rv;
838 MOZ_ASSERT(data);
839 aList.AppendElement(data);
842 return NS_OK;
845 } // namespace
847 /* static */
848 nsresult AsyncUrlChannelClassifier::CheckChannel(
849 nsIChannel* aChannel, std::function<void()>&& aCallback) {
850 MOZ_ASSERT(XRE_IsParentProcess());
851 MOZ_ASSERT(aChannel);
853 if (!aCallback) {
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();
861 chanSpec.Truncate(
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;
870 topWinSpec.Truncate(
871 std::min(topWinSpec.Length(), UrlClassifierCommon::sMaxSpecLength));
873 UC_LOG(
874 ("AsyncUrlChannelClassifier::CheckChannel - starting the "
875 "classification on channel %p",
876 aChannel));
877 UC_LOG((" uri is %s [channel=%p]", chanSpec.get(), aChannel));
878 UC_LOG(
879 (" top-level uri is %s [channel=%p]", topWinSpec.get(), aChannel));
883 RefPtr<FeatureTask> task;
884 nsresult rv =
885 FeatureTask::Create(aChannel, std::move(aCallback), getter_AddRefs(task));
886 if (NS_FAILED(rv)) {
887 return rv;
890 if (!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);
919 } // namespace net
920 } // namespace mozilla