AsyncUrlChannelClassifier.cpp (28863B)
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/. */ 6 7 #include "Classifier.h" 8 #include "HttpBaseChannel.h" 9 #include "mozilla/Components.h" 10 #include "mozilla/ErrorNames.h" 11 #include "mozilla/net/AsyncUrlChannelClassifier.h" 12 #include "mozilla/dom/BrowsingContext.h" 13 #include "mozilla/dom/CanonicalBrowsingContext.h" 14 #include "mozilla/net/UrlClassifierCommon.h" 15 #include "mozilla/net/UrlClassifierFeatureFactory.h" 16 #include "mozilla/net/UrlClassifierFeatureResult.h" 17 #include "nsContentUtils.h" 18 #include "nsIChannel.h" 19 #include "nsIHttpChannel.h" 20 #include "nsIUrlClassifierExceptionList.h" 21 #include "nsNetCID.h" 22 #include "nsNetUtil.h" 23 #include "nsPrintfCString.h" 24 #include "nsProxyRelease.h" 25 #include "nsServiceManagerUtils.h" 26 #include "nsUrlClassifierDBService.h" 27 #include "nsUrlClassifierUtils.h" 28 #include "mozilla/net/UrlClassifierCommon.h" 29 30 namespace mozilla { 31 namespace net { 32 33 namespace { 34 35 // Big picture comment 36 // ----------------------------------------------------------------------------- 37 // nsUrlClassifierDBService::channelClassify() classifies a channel using a set 38 // of URL-Classifier features. This method minimizes the number of lookups and 39 // URI parsing and this is done using the classes here described. 40 // 41 // The first class is 'FeatureTask' which is able to retrieve the list of 42 // features for this channel using the feature-factory. See 43 // UrlClassifierFeatureFactory. 44 // For each feature, it creates a FeatureData object, which contains the 45 // entitylist and blocklist prefs and tables. The reason why we create 46 // FeatureData is because: 47 // - features are not thread-safe. 48 // - we want to store the state of the classification in the FeatureData 49 // object. 50 // 51 // It can happen that multiple features share the same tables. In order to do 52 // the lookup just once, we have TableData class. When multiple features 53 // contain the same table, they have references to the same couple TableData + 54 // URIData objects. 55 // 56 // During the classification, the channel's URIs are fragmented. In order to 57 // create these fragments just once, we use the URIData class, which is pointed 58 // by TableData classes. 59 // 60 // The creation of these classes happens on the main-thread. The classification 61 // happens on the worker thread. 62 63 // URIData 64 // ----------------------------------------------------------------------------- 65 66 // In order to avoid multiple URI parsing, we have this class which contains 67 // nsIURI and its fragments. 68 class URIData { 69 public: 70 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URIData); 71 72 static nsresult Create(nsIURI* aURI, nsIURI* aInnermostURI, 73 nsIUrlClassifierFeature::URIType aURIType, 74 URIData** aData); 75 76 bool IsEqual(nsIURI* aURI) const; 77 78 const nsTArray<nsCString>& Fragments(); 79 80 nsIURI* URI() const; 81 82 private: 83 URIData(); 84 ~URIData() = default; 85 86 nsCOMPtr<nsIURI> mURI; 87 nsCString mURISpec; 88 nsTArray<nsCString> mFragments; 89 nsIUrlClassifierFeature::URIType mURIType; 90 }; 91 92 /* static */ 93 nsresult URIData::Create(nsIURI* aURI, nsIURI* aInnermostURI, 94 nsIUrlClassifierFeature::URIType aURIType, 95 URIData** aData) { 96 MOZ_ASSERT(NS_IsMainThread()); 97 MOZ_ASSERT(aURI); 98 MOZ_ASSERT(aInnermostURI); 99 100 RefPtr<URIData> data = new URIData(); 101 data->mURI = aURI; 102 data->mURIType = aURIType; 103 104 nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance(); 105 if (NS_WARN_IF(!utilsService)) { 106 return NS_ERROR_FAILURE; 107 } 108 109 nsresult rv = utilsService->GetKeyForURI(aInnermostURI, data->mURISpec); 110 if (NS_WARN_IF(NS_FAILED(rv))) { 111 return rv; 112 } 113 114 UC_LOG_LEAK( 115 ("AsyncChannelClassifier::URIData::Create new URIData created for spec " 116 "%s [this=%p]", 117 data->mURISpec.get(), data.get())); 118 119 data.forget(aData); 120 return NS_OK; 121 } 122 123 URIData::URIData() { MOZ_ASSERT(NS_IsMainThread()); } 124 125 bool URIData::IsEqual(nsIURI* aURI) const { 126 MOZ_ASSERT(NS_IsMainThread()); 127 MOZ_ASSERT(aURI); 128 129 bool isEqual = false; 130 nsresult rv = mURI->Equals(aURI, &isEqual); 131 if (NS_WARN_IF(NS_FAILED(rv))) { 132 return false; 133 } 134 135 return isEqual; 136 } 137 138 const nsTArray<nsCString>& URIData::Fragments() { 139 MOZ_ASSERT(!NS_IsMainThread()); 140 141 if (mFragments.IsEmpty()) { 142 if (mURIType == nsIUrlClassifierFeature::pairwiseEntitylistURI) { 143 LookupCache::GetLookupEntitylistFragments(mURISpec, &mFragments); 144 } else { 145 LookupCache::GetLookupFragments(mURISpec, &mFragments); 146 } 147 } 148 149 return mFragments; 150 } 151 152 nsIURI* URIData::URI() const { 153 MOZ_ASSERT(NS_IsMainThread()); 154 return mURI; 155 } 156 157 // TableData 158 // ---------------------------------------------------------------------------- 159 160 // In order to avoid multiple lookups on the same table + URI, we have this 161 // class. 162 class TableData { 163 public: 164 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableData); 165 166 enum State { 167 eUnclassified, 168 eNoMatch, 169 eMatch, 170 }; 171 172 TableData(URIData* aURIData, const nsACString& aTable); 173 174 nsIURI* URI() const; 175 176 const nsACString& Table() const; 177 178 const LookupResultArray& Result() const; 179 180 State MatchState() const; 181 182 bool IsEqual(URIData* aURIData, const nsACString& aTable) const; 183 184 // Returns true if the table classifies the URI. This method must be called 185 // on hte classifier worker thread. 186 bool DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier); 187 188 private: 189 ~TableData(); 190 191 RefPtr<URIData> mURIData; 192 State mState; 193 194 nsCString mTable; 195 LookupResultArray mResults; 196 }; 197 198 TableData::TableData(URIData* aURIData, const nsACString& aTable) 199 : mURIData(aURIData), mState(eUnclassified), mTable(aTable) { 200 MOZ_ASSERT(NS_IsMainThread()); 201 MOZ_ASSERT(aURIData); 202 203 UC_LOG_LEAK( 204 ("AsyncChannelClassifier::TableData CTOR - new TableData created %s " 205 "[this=%p]", 206 aTable.BeginReading(), this)); 207 } 208 209 TableData::~TableData() = default; 210 211 nsIURI* TableData::URI() const { 212 MOZ_ASSERT(NS_IsMainThread()); 213 return mURIData->URI(); 214 } 215 216 const nsACString& TableData::Table() const { 217 MOZ_ASSERT(NS_IsMainThread()); 218 return mTable; 219 } 220 221 const LookupResultArray& TableData::Result() const { 222 MOZ_ASSERT(NS_IsMainThread()); 223 return mResults; 224 } 225 226 TableData::State TableData::MatchState() const { 227 MOZ_ASSERT(NS_IsMainThread()); 228 return mState; 229 } 230 231 bool TableData::IsEqual(URIData* aURIData, const nsACString& aTable) const { 232 MOZ_ASSERT(NS_IsMainThread()); 233 return mURIData == aURIData && mTable == aTable; 234 } 235 236 bool TableData::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) { 237 MOZ_ASSERT(!NS_IsMainThread()); 238 MOZ_ASSERT(aWorkerClassifier); 239 240 if (mState == TableData::eUnclassified) { 241 UC_LOG_LEAK( 242 ("AsyncChannelClassifier::TableData::DoLookup - starting lookup " 243 "[this=%p]", 244 this)); 245 246 const nsTArray<nsCString>& fragments = mURIData->Fragments(); 247 nsresult rv = aWorkerClassifier->DoSingleLocalLookupWithURIFragments( 248 fragments, mTable, mResults); 249 (void)NS_WARN_IF(NS_FAILED(rv)); 250 251 mState = mResults.IsEmpty() ? TableData::eNoMatch : TableData::eMatch; 252 253 UC_LOG_LEAK( 254 ("AsyncChannelClassifier::TableData::DoLookup - lookup completed. " 255 "Matches: %d [this=%p]", 256 (int)mResults.Length(), this)); 257 } 258 259 return !mResults.IsEmpty(); 260 } 261 262 // FeatureData 263 // ---------------------------------------------------------------------------- 264 265 class FeatureTask; 266 267 // This is class contains all the Feature data. 268 class FeatureData { 269 enum State { 270 eUnclassified, 271 eNoMatch, 272 eMatchBlocklist, 273 eMatchEntitylist, 274 }; 275 276 public: 277 FeatureData() = default; 278 ~FeatureData(); 279 280 nsresult Initialize(FeatureTask* aTask, nsIChannel* aChannel, 281 nsIUrlClassifierFeature* aFeature); 282 283 void DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier); 284 285 // Returns true if the next feature should be processed. 286 bool MaybeCompleteClassification(nsIChannel* aChannel); 287 288 private: 289 nsresult InitializeList(FeatureTask* aTask, nsIChannel* aChannel, 290 nsIUrlClassifierFeature::listType aListType, 291 nsTArray<RefPtr<TableData>>& aList); 292 293 State mState{eUnclassified}; 294 nsCOMPtr<nsIUrlClassifierFeature> mFeature; 295 296 nsTArray<RefPtr<TableData>> mBlocklistTables; 297 nsTArray<RefPtr<TableData>> mEntitylistTables; 298 299 // blocklist + entitylist. 300 nsCString mHostInPrefTables[2]; 301 }; 302 303 FeatureData::~FeatureData() { 304 NS_ReleaseOnMainThread("FeatureData:mFeature", mFeature.forget()); 305 } 306 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); 313 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)); 321 } 322 323 mFeature = aFeature; 324 325 nsresult rv = InitializeList( 326 aTask, aChannel, nsIUrlClassifierFeature::blocklist, mBlocklistTables); 327 if (NS_WARN_IF(NS_FAILED(rv))) { 328 return rv; 329 } 330 331 rv = InitializeList(aTask, aChannel, nsIUrlClassifierFeature::entitylist, 332 mEntitylistTables); 333 if (NS_FAILED(rv)) { 334 return rv; 335 } 336 337 return NS_OK; 338 } 339 340 void FeatureData::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) { 341 MOZ_ASSERT(!NS_IsMainThread()); 342 MOZ_ASSERT(aWorkerClassifier); 343 MOZ_ASSERT(mState == eUnclassified); 344 345 UC_LOG_LEAK( 346 ("AsyncChannelClassifier::FeatureData::DoLookup - lookup starting " 347 "[this=%p]", 348 this)); 349 350 // This is wrong, but it's fast: we don't want to check if the host is in the 351 // blocklist table if we know that it's going to be entitylisted by pref. 352 // So, also if maybe it's not blocklisted, let's consider it 'entitylisted'. 353 if (!mHostInPrefTables[nsIUrlClassifierFeature::entitylist].IsEmpty()) { 354 UC_LOG_LEAK( 355 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by pref " 356 "[this=%p]", 357 this)); 358 mState = eMatchEntitylist; 359 return; 360 } 361 362 // Let's check if this feature blocklists the URI. 363 364 bool isBlocklisted = 365 !mHostInPrefTables[nsIUrlClassifierFeature::blocklist].IsEmpty(); 366 367 UC_LOG_LEAK( 368 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted by pref: " 369 "%d [this=%p]", 370 isBlocklisted, this)); 371 372 if (!isBlocklisted) { 373 // If one of the blocklist table matches the URI, we don't need to continue 374 // with the others: the feature is blocklisted (but maybe also 375 // entitylisted). 376 for (TableData* tableData : mBlocklistTables) { 377 if (tableData->DoLookup(aWorkerClassifier)) { 378 isBlocklisted = true; 379 break; 380 } 381 } 382 } 383 384 UC_LOG_LEAK( 385 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted before " 386 "entitylisting: %d [this=%p]", 387 isBlocklisted, this)); 388 389 if (!isBlocklisted) { 390 mState = eNoMatch; 391 return; 392 } 393 394 // Now, let's check if we need to entitylist the same URI. 395 396 for (TableData* tableData : mEntitylistTables) { 397 // If one of the entitylist table matches the URI, we don't need to continue 398 // with the others: the feature is entitylisted. 399 if (tableData->DoLookup(aWorkerClassifier)) { 400 UC_LOG_LEAK( 401 ("AsyncChannelClassifier::FeatureData::DoLookup - entitylisted by " 402 "table [this=%p]", 403 this)); 404 mState = eMatchEntitylist; 405 return; 406 } 407 } 408 409 UC_LOG_LEAK( 410 ("AsyncChannelClassifier::FeatureData::DoLookup - blocklisted [this=%p]", 411 this)); 412 mState = eMatchBlocklist; 413 } 414 415 bool FeatureData::MaybeCompleteClassification(nsIChannel* aChannel) { 416 MOZ_ASSERT(NS_IsMainThread()); 417 418 nsAutoCString name; 419 mFeature->GetName(name); 420 421 UC_LOG_LEAK( 422 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 423 "completing " 424 "classification [this=%p channel=%p]", 425 this, aChannel)); 426 427 switch (mState) { 428 case eNoMatch: 429 UC_LOG( 430 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 431 "no match for feature %s. Let's " 432 "move on [this=%p channel=%p]", 433 name.get(), this, aChannel)); 434 return true; 435 436 case eMatchEntitylist: 437 UC_LOG( 438 ("AsyncChannelClassifier::FeatureData::MayebeCompleteClassification " 439 "- entitylisted by feature %s. Let's " 440 "move on [this=%p channel=%p]", 441 name.get(), this, aChannel)); 442 return true; 443 444 case eMatchBlocklist: 445 UC_LOG( 446 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 447 "blocklisted by feature %s [this=%p channel=%p]", 448 name.get(), this, aChannel)); 449 break; 450 451 case eUnclassified: 452 MOZ_CRASH("We should not be here!"); 453 break; 454 } 455 456 MOZ_ASSERT(mState == eMatchBlocklist); 457 458 // Maybe we have to ignore this host 459 nsCOMPtr<nsIUrlClassifierExceptionList> exceptionList; 460 nsresult rv = mFeature->GetExceptionList(getter_AddRefs(exceptionList)); 461 if (NS_WARN_IF(NS_FAILED(rv))) { 462 UC_LOG_WARN( 463 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 464 "error while getting exception list. Let's move on [exceptionList=%p " 465 "this=%p channel=%p]", 466 exceptionList.get(), this, aChannel)); 467 return true; 468 } 469 470 // Check if current load is allow-listed by the exception list. 471 if (!mBlocklistTables.IsEmpty() && exceptionList) { 472 // Get top level URI from channel. 473 nsCOMPtr<nsIURI> topLevelURI; 474 rv = UrlClassifierCommon::GetTopWindowURI(aChannel, 475 getter_AddRefs(topLevelURI)); 476 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get top level URI"); 477 478 bool isPrivateBrowsing = NS_UsePrivateBrowsing(aChannel); 479 bool isAllowListed = false; 480 rv = exceptionList->Matches(mBlocklistTables[0]->URI(), topLevelURI, 481 isPrivateBrowsing, &isAllowListed); 482 if (NS_SUCCEEDED(rv) && isAllowListed) { 483 nsCString spec = mBlocklistTables[0]->URI()->GetSpecOrDefault(); 484 spec.Truncate( 485 std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength)); 486 UC_LOG( 487 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 488 "uri %s found in " 489 "exceptionlist of feature %s [this=%p channel=%p]", 490 spec.get(), name.get(), this, aChannel)); 491 return true; 492 } 493 } 494 495 nsTArray<nsCString> list; 496 nsTArray<nsCString> hashes; 497 if (!mHostInPrefTables[nsIUrlClassifierFeature::blocklist].IsEmpty()) { 498 list.AppendElement(mHostInPrefTables[nsIUrlClassifierFeature::blocklist]); 499 500 // Telemetry expects every tracking channel has hash, create it for test 501 // entry 502 Completion complete; 503 complete.FromPlaintext( 504 mHostInPrefTables[nsIUrlClassifierFeature::blocklist]); 505 hashes.AppendElement(complete.ToString()); 506 } 507 508 for (TableData* tableData : mBlocklistTables) { 509 if (tableData->MatchState() == TableData::eMatch) { 510 list.AppendElement(tableData->Table()); 511 512 for (const auto& r : tableData->Result()) { 513 hashes.AppendElement(r->hash.complete.ToString()); 514 } 515 } 516 } 517 518 UC_LOG_LEAK( 519 ("AsyncChannelClassifier::FeatureData::MaybeCompleteClassification - " 520 "process channel [this=%p channel=%p]", 521 this, aChannel)); 522 523 bool shouldContinue = false; 524 rv = mFeature->ProcessChannel(aChannel, list, hashes, &shouldContinue); 525 (void)NS_WARN_IF(NS_FAILED(rv)); 526 527 return shouldContinue; 528 } 529 530 // CallbackHolder 531 // ---------------------------------------------------------------------------- 532 533 // This class keeps the callback alive and makes sure that we release it on the 534 // correct thread. 535 class CallbackHolder final { 536 public: 537 NS_INLINE_DECL_REFCOUNTING(CallbackHolder); 538 539 explicit CallbackHolder(std::function<void()>&& aCallback) 540 : mCallback(std::move(aCallback)) {} 541 542 void Exec() const { mCallback(); } 543 544 private: 545 ~CallbackHolder() = default; 546 547 std::function<void()> mCallback; 548 }; 549 550 // FeatureTask 551 // ---------------------------------------------------------------------------- 552 553 // A FeatureTask is a class that is able to classify a channel using a set of 554 // features. The features are grouped by: 555 // - URIs - to avoid extra URI parsing. 556 // - Tables - to avoid multiple lookup on the same table. 557 class FeatureTask { 558 public: 559 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureTask); 560 561 static nsresult Create(nsIChannel* aChannel, 562 std::function<void()>&& aCallback, 563 FeatureTask** aTask); 564 565 // Called on the classifier thread. 566 void DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier); 567 568 // Called on the main-thread to process the channel. 569 void CompleteClassification(); 570 571 nsresult GetOrCreateURIData(nsIURI* aURI, nsIURI* aInnermostURI, 572 nsIUrlClassifierFeature::URIType aURIType, 573 URIData** aData); 574 575 nsresult GetOrCreateTableData(URIData* aURIData, const nsACString& aTable, 576 TableData** aData); 577 578 private: 579 FeatureTask(nsIChannel* aChannel, std::function<void()>&& aCallback); 580 ~FeatureTask(); 581 582 nsCOMPtr<nsIChannel> mChannel; 583 RefPtr<CallbackHolder> mCallbackHolder; 584 585 nsTArray<FeatureData> mFeatures; 586 nsTArray<RefPtr<URIData>> mURIs; 587 nsTArray<RefPtr<TableData>> mTables; 588 }; 589 590 // Features are able to classify particular URIs from a channel. For instance, 591 // tracking-annotation feature uses the top-level URI to entitylist the current 592 // channel's URI. Because of 593 // this, this function aggregates feature per URI and tables. 594 /* static */ 595 nsresult FeatureTask::Create(nsIChannel* aChannel, 596 std::function<void()>&& aCallback, 597 FeatureTask** aTask) { 598 MOZ_ASSERT(NS_IsMainThread()); 599 MOZ_ASSERT(aChannel); 600 MOZ_ASSERT(aTask); 601 602 // We need to obtain the list of nsIUrlClassifierFeature objects able to 603 // classify this channel. If the list is empty, we do an early return. 604 nsTArray<nsCOMPtr<nsIUrlClassifierFeature>> features; 605 UrlClassifierFeatureFactory::GetFeaturesFromChannel(aChannel, features); 606 if (features.IsEmpty()) { 607 UC_LOG( 608 ("AsyncChannelClassifier::FeatureTask::Create - no task is needed for " 609 "channel %p", 610 aChannel)); 611 return NS_OK; 612 } 613 614 RefPtr<FeatureTask> task = new FeatureTask(aChannel, std::move(aCallback)); 615 616 UC_LOG( 617 ("AsyncChannelClassifier::FeatureTask::Create - FeatureTask %p created " 618 "for channel %p", 619 task.get(), aChannel)); 620 621 for (nsIUrlClassifierFeature* feature : features) { 622 FeatureData* featureData = task->mFeatures.AppendElement(); 623 nsresult rv = featureData->Initialize(task, aChannel, feature); 624 if (NS_FAILED(rv)) { 625 return rv; 626 } 627 } 628 629 task.forget(aTask); 630 return NS_OK; 631 } 632 633 FeatureTask::FeatureTask(nsIChannel* aChannel, 634 std::function<void()>&& aCallback) 635 : mChannel(aChannel) { 636 MOZ_ASSERT(NS_IsMainThread()); 637 MOZ_ASSERT(mChannel); 638 639 std::function<void()> callback = std::move(aCallback); 640 mCallbackHolder = new CallbackHolder(std::move(callback)); 641 } 642 643 FeatureTask::~FeatureTask() { 644 NS_ReleaseOnMainThread("FeatureTask::mChannel", mChannel.forget()); 645 NS_ReleaseOnMainThread("FeatureTask::mCallbackHolder", 646 mCallbackHolder.forget()); 647 } 648 649 nsresult FeatureTask::GetOrCreateURIData( 650 nsIURI* aURI, nsIURI* aInnermostURI, 651 nsIUrlClassifierFeature::URIType aURIType, URIData** aData) { 652 MOZ_ASSERT(NS_IsMainThread()); 653 MOZ_ASSERT(aURI); 654 MOZ_ASSERT(aInnermostURI); 655 MOZ_ASSERT(aData); 656 657 UC_LOG_LEAK( 658 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - checking if " 659 "a URIData must be " 660 "created [this=%p]", 661 this)); 662 663 for (URIData* data : mURIs) { 664 if (data->IsEqual(aURI)) { 665 UC_LOG_LEAK( 666 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - reuse " 667 "existing URIData %p [this=%p]", 668 data, this)); 669 670 RefPtr<URIData> uriData = data; 671 uriData.forget(aData); 672 return NS_OK; 673 } 674 } 675 676 RefPtr<URIData> data; 677 nsresult rv = 678 URIData::Create(aURI, aInnermostURI, aURIType, getter_AddRefs(data)); 679 if (NS_WARN_IF(NS_FAILED(rv))) { 680 return rv; 681 } 682 683 mURIs.AppendElement(data); 684 685 UC_LOG_LEAK( 686 ("AsyncChannelClassifier::FeatureTask::GetOrCreateURIData - create new " 687 "URIData %p [this=%p]", 688 data.get(), this)); 689 690 data.forget(aData); 691 return NS_OK; 692 } 693 694 nsresult FeatureTask::GetOrCreateTableData(URIData* aURIData, 695 const nsACString& aTable, 696 TableData** aData) { 697 MOZ_ASSERT(NS_IsMainThread()); 698 MOZ_ASSERT(aURIData); 699 MOZ_ASSERT(aData); 700 701 UC_LOG_LEAK( 702 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - checking " 703 "if TableData must be " 704 "created [this=%p]", 705 this)); 706 707 for (TableData* data : mTables) { 708 if (data->IsEqual(aURIData, aTable)) { 709 UC_LOG_LEAK( 710 ("FeatureTask::GetOrCreateTableData - reuse existing TableData %p " 711 "[this=%p]", 712 data, this)); 713 714 RefPtr<TableData> tableData = data; 715 tableData.forget(aData); 716 return NS_OK; 717 } 718 } 719 720 RefPtr<TableData> data = new TableData(aURIData, aTable); 721 mTables.AppendElement(data); 722 723 UC_LOG_LEAK( 724 ("AsyncChannelClassifier::FeatureTask::GetOrCreateTableData - create new " 725 "TableData %p [this=%p]", 726 data.get(), this)); 727 728 data.forget(aData); 729 return NS_OK; 730 } 731 732 void FeatureTask::DoLookup(nsUrlClassifierDBServiceWorker* aWorkerClassifier) { 733 MOZ_ASSERT(!NS_IsMainThread()); 734 MOZ_ASSERT(aWorkerClassifier); 735 736 UC_LOG_LEAK( 737 ("AsyncChannelClassifier::FeatureTask::DoLookup - starting lookup " 738 "[this=%p]", 739 this)); 740 741 for (FeatureData& feature : mFeatures) { 742 feature.DoLookup(aWorkerClassifier); 743 } 744 745 UC_LOG_LEAK( 746 ("AsyncChannelClassifier::FeatureTask::DoLookup - lookup completed " 747 "[this=%p]", 748 this)); 749 } 750 751 void FeatureTask::CompleteClassification() { 752 MOZ_ASSERT(NS_IsMainThread()); 753 754 for (FeatureData& feature : mFeatures) { 755 if (!feature.MaybeCompleteClassification(mChannel)) { 756 break; 757 } 758 } 759 760 UC_LOG( 761 ("AsyncChannelClassifier::FeatureTask::CompleteClassification - complete " 762 "classification for " 763 "channel %p [this=%p]", 764 mChannel.get(), this)); 765 766 mCallbackHolder->Exec(); 767 } 768 769 nsresult FeatureData::InitializeList( 770 FeatureTask* aTask, nsIChannel* aChannel, 771 nsIUrlClassifierFeature::listType aListType, 772 nsTArray<RefPtr<TableData>>& aList) { 773 MOZ_ASSERT(NS_IsMainThread()); 774 MOZ_ASSERT(aTask); 775 MOZ_ASSERT(aChannel); 776 777 UC_LOG_LEAK( 778 ("AsyncChannelClassifier::FeatureData::InitializeList - initialize list " 779 "%d for channel %p [this=%p]", 780 aListType, aChannel, this)); 781 782 nsCOMPtr<nsIURI> uri; 783 nsIUrlClassifierFeature::URIType URIType; 784 nsresult rv = mFeature->GetURIByListType(aChannel, aListType, &URIType, 785 getter_AddRefs(uri)); 786 if (NS_FAILED(rv)) { 787 if (UC_LOG_ENABLED()) { 788 nsAutoCString errorName; 789 GetErrorName(rv, errorName); 790 UC_LOG_LEAK( 791 ("AsyncChannelClassifier::FeatureData::InitializeList - Got an " 792 "unexpected error (rv=%s) [this=%p]", 793 errorName.get(), this)); 794 } 795 return rv; 796 } 797 798 if (!uri) { 799 // Return success when the URI is empty to conitnue to do the lookup. 800 UC_LOG_LEAK( 801 ("AsyncChannelClassifier::FeatureData::InitializeList - got an empty " 802 "URL [this=%p]", 803 this)); 804 return NS_OK; 805 } 806 807 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(uri); 808 if (NS_WARN_IF(!innermostURI)) { 809 return NS_ERROR_FAILURE; 810 } 811 812 nsAutoCString host; 813 rv = innermostURI->GetHost(host); 814 if (NS_WARN_IF(NS_FAILED(rv))) { 815 return rv; 816 } 817 818 bool found = false; 819 nsAutoCString tableName; 820 rv = mFeature->HasHostInPreferences(host, aListType, tableName, &found); 821 if (NS_WARN_IF(NS_FAILED(rv))) { 822 return rv; 823 } 824 825 if (found) { 826 mHostInPrefTables[aListType] = tableName; 827 } 828 829 RefPtr<URIData> uriData; 830 rv = aTask->GetOrCreateURIData(uri, innermostURI, URIType, 831 getter_AddRefs(uriData)); 832 if (NS_WARN_IF(NS_FAILED(rv))) { 833 return rv; 834 } 835 836 MOZ_ASSERT(uriData); 837 838 nsTArray<nsCString> tables; 839 rv = mFeature->GetTables(aListType, tables); 840 if (NS_WARN_IF(NS_FAILED(rv))) { 841 return rv; 842 } 843 844 for (const nsCString& table : tables) { 845 RefPtr<TableData> data; 846 rv = aTask->GetOrCreateTableData(uriData, table, getter_AddRefs(data)); 847 if (NS_WARN_IF(NS_FAILED(rv))) { 848 return rv; 849 } 850 851 MOZ_ASSERT(data); 852 aList.AppendElement(data); 853 } 854 855 return NS_OK; 856 } 857 858 } // namespace 859 860 /* static */ 861 void AsyncUrlChannelClassifier::WarmUp() { 862 // Trigger the construction of the singleton instance. 863 nsresult rv; 864 RefPtr<nsUrlClassifierDBService> service = 865 nsUrlClassifierDBService::GetInstance(&rv); 866 } 867 868 /* static */ 869 nsresult AsyncUrlChannelClassifier::CheckChannel( 870 nsIChannel* aChannel, std::function<void()>&& aCallback) { 871 MOZ_ASSERT(XRE_IsParentProcess()); 872 MOZ_ASSERT(aChannel); 873 874 if (!aCallback) { 875 return NS_ERROR_INVALID_ARG; 876 } 877 878 if (UC_LOG_ENABLED()) { 879 nsCOMPtr<nsIURI> chanURI; 880 if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(chanURI)))) { 881 nsCString chanSpec = chanURI->GetSpecOrDefault(); 882 chanSpec.Truncate( 883 std::min(chanSpec.Length(), UrlClassifierCommon::sMaxSpecLength)); 884 885 nsCOMPtr<nsIURI> topWinURI; 886 (void)UrlClassifierCommon::GetTopWindowURI(aChannel, 887 getter_AddRefs(topWinURI)); 888 nsCString topWinSpec = 889 topWinURI ? topWinURI->GetSpecOrDefault() : "(null)"_ns; 890 891 topWinSpec.Truncate( 892 std::min(topWinSpec.Length(), UrlClassifierCommon::sMaxSpecLength)); 893 894 UC_LOG( 895 ("AsyncUrlChannelClassifier::CheckChannel - starting the " 896 "classification on channel %p", 897 aChannel)); 898 UC_LOG((" uri is %s [channel=%p]", chanSpec.get(), aChannel)); 899 UC_LOG( 900 (" top-level uri is %s [channel=%p]", topWinSpec.get(), aChannel)); 901 } 902 } 903 904 RefPtr<FeatureTask> task; 905 nsresult rv = 906 FeatureTask::Create(aChannel, std::move(aCallback), getter_AddRefs(task)); 907 if (NS_FAILED(rv)) { 908 return rv; 909 } 910 911 if (!task) { 912 // No task is needed for this channel, return an error so the caller won't 913 // wait for a callback. 914 return NS_ERROR_FAILURE; 915 } 916 917 RefPtr<nsUrlClassifierDBServiceWorker> workerClassifier = 918 nsUrlClassifierDBService::GetWorker(); 919 if (NS_WARN_IF(!workerClassifier)) { 920 return NS_ERROR_FAILURE; 921 } 922 923 // raise the priority of URLClassifier's return dispatch to the MainThread if 924 // the channel is considered important 925 EventQueuePriority eventPriority = EventQueuePriority::Normal; 926 if (nsCOMPtr<HttpBaseChannel> baseChannel = do_QueryInterface(aChannel)) { 927 uint32_t classOfServiceFlags = 0; 928 baseChannel->GetClassFlags(&classOfServiceFlags); 929 if (classOfServiceFlags & 930 (nsIClassOfService::Leader | nsIClassOfService::UrgentStart | 931 nsIClassOfService::Unblocked)) { 932 eventPriority = EventQueuePriority::MediumHigh; 933 } 934 } 935 if (nsCOMPtr<nsISupportsPriority> supportsPriority = 936 do_QueryInterface(aChannel)) { 937 int32_t priority = nsISupportsPriority::PRIORITY_NORMAL; 938 supportsPriority->GetPriority(&priority); 939 // note that higher priorities have lower numeric values 940 if (priority <= nsISupportsPriority::PRIORITY_HIGH) { 941 eventPriority = EventQueuePriority::MediumHigh; 942 } 943 } 944 945 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 946 "AsyncUrlChannelClassifier::CheckChannel", 947 [task, workerClassifier, eventPriority]() -> void { 948 MOZ_ASSERT(!NS_IsMainThread()); 949 task->DoLookup(workerClassifier); 950 951 NS_DispatchToMainThreadQueue( 952 NS_NewRunnableFunction( 953 "AsyncUrlChannelClassifier::CheckChannel - return", 954 [task]() -> void { task->CompleteClassification(); }), 955 eventPriority); 956 }); 957 958 // no need to prioritize the dispatch to the URLClassifier thread 959 // since overriding prioritization is ignored if we aren't on the MainThread 960 return nsUrlClassifierDBService::BackgroundThread()->Dispatch( 961 r, NS_DISPATCH_NORMAL); 962 } 963 964 } // namespace net 965 } // namespace mozilla