Bug 1513300 - Implement Cryptomining URL-Classifier, r=dimi
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 09 Jan 2019 12:16:04 +0100
changeset 453039 d503dc3fd033
parent 453038 a11f752f828c
child 453040 99db2eabbf07
push id35341
push usercsabou@mozilla.com
push dateWed, 09 Jan 2019 16:06:01 +0000
treeherdermozilla-central@29b2c2f57879 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdimi
bugs1513300
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1513300 - Implement Cryptomining URL-Classifier, r=dimi
modules/libpref/init/StaticPrefList.h
netwerk/url-classifier/UrlClassifierCommon.cpp
netwerk/url-classifier/UrlClassifierFeatureCryptomining.cpp
netwerk/url-classifier/UrlClassifierFeatureCryptomining.h
netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
netwerk/url-classifier/moz.build
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
toolkit/components/url-classifier/tests/mochitest/mochitest.ini
toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1786,16 +1786,23 @@ VARCACHE_PREF(
 
 // Block 3rd party fingerprinting resources.
 VARCACHE_PREF(
   "privacy.trackingprotection.fingerprinting.enabled",
    privacy_trackingprotection_fingerprinting_enabled,
   bool, true
 )
 
+// Block 3rd party cryptomining resources.
+VARCACHE_PREF(
+  "privacy.trackingprotection.cryptomining.enabled",
+   privacy_trackingprotection_cryptomining_enabled,
+  bool, true
+)
+
 // Lower the priority of network loads for resources on the tracking protection
 // list.  Note that this requires the
 // privacy.trackingprotection.annotate_channels pref to be on in order to have
 // any effect.
 #ifdef NIGHTLY_BUILD
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
--- a/netwerk/url-classifier/UrlClassifierCommon.cpp
+++ b/netwerk/url-classifier/UrlClassifierCommon.cpp
@@ -88,17 +88,18 @@ LazyLogModule UrlClassifierCommon::sLog(
 }
 
 /* static */ bool UrlClassifierCommon::ShouldEnableClassifier(
     nsIChannel* aChannel,
     AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose) {
   MOZ_ASSERT(aChannel);
   MOZ_ASSERT(aBlockingPurpose == AntiTrackingCommon::eTrackingProtection ||
              aBlockingPurpose == AntiTrackingCommon::eTrackingAnnotations ||
-             aBlockingPurpose == AntiTrackingCommon::eFingerprinting);
+             aBlockingPurpose == AntiTrackingCommon::eFingerprinting ||
+             aBlockingPurpose == AntiTrackingCommon::eCryptomining);
 
   nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(aChannel);
   if (!channel) {
     UC_LOG(("nsChannelClassifier: Not an HTTP channel"));
     return false;
   }
 
   nsCOMPtr<nsIURI> chanURI;
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptomining.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "UrlClassifierFeatureCryptomining.h"
+
+#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/net/UrlClassifierCommon.h"
+#include "mozilla/StaticPrefs.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+#define CRYPTOMINING_FEATURE_NAME "cryptomining"
+
+#define URLCLASSIFIER_CRYPTOMINING_BLACKLIST \
+  "urlclassifier.features.cryptomining.blacklistTables"
+#define URLCLASSIFIER_CRYPTOMINING_BLACKLIST_TEST_ENTRIES \
+  "urlclassifier.features.cryptomining.blacklistHosts"
+#define URLCLASSIFIER_CRYPTOMINING_WHITELIST \
+  "urlclassifier.features.cryptomining.whitelistTables"
+#define URLCLASSIFIER_CRYPTOMINING_WHITELIST_TEST_ENTRIES \
+  "urlclassifier.features.cryptomining.whitelistHosts"
+#define TABLE_CRYPTOMINING_BLACKLIST_PREF "cryptomining-blacklist-pref"
+#define TABLE_CRYPTOMINING_WHITELIST_PREF "cryptomining-whitelist-pref"
+
+StaticRefPtr<UrlClassifierFeatureCryptomining> gFeatureCryptomining;
+
+}  // namespace
+
+UrlClassifierFeatureCryptomining::UrlClassifierFeatureCryptomining()
+    : UrlClassifierFeatureBase(
+          NS_LITERAL_CSTRING(CRYPTOMINING_FEATURE_NAME),
+          NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_BLACKLIST),
+          NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_WHITELIST),
+          NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_BLACKLIST_TEST_ENTRIES),
+          NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_WHITELIST_TEST_ENTRIES),
+          NS_LITERAL_CSTRING(TABLE_CRYPTOMINING_BLACKLIST_PREF),
+          NS_LITERAL_CSTRING(TABLE_CRYPTOMINING_WHITELIST_PREF),
+          EmptyCString()) {}
+
+/* static */ const char* UrlClassifierFeatureCryptomining::Name() {
+  return CRYPTOMINING_FEATURE_NAME;
+}
+
+/* static */ void UrlClassifierFeatureCryptomining::MaybeInitialize() {
+  UC_LOG(("UrlClassifierFeatureCryptomining: MaybeInitialize"));
+
+  if (!gFeatureCryptomining) {
+    gFeatureCryptomining = new UrlClassifierFeatureCryptomining();
+    gFeatureCryptomining->InitializePreferences();
+  }
+}
+
+/* static */ void UrlClassifierFeatureCryptomining::MaybeShutdown() {
+  UC_LOG(("UrlClassifierFeatureCryptomining: MaybeShutdown"));
+
+  if (gFeatureCryptomining) {
+    gFeatureCryptomining->ShutdownPreferences();
+    gFeatureCryptomining = nullptr;
+  }
+}
+
+/* static */ already_AddRefed<UrlClassifierFeatureCryptomining>
+UrlClassifierFeatureCryptomining::MaybeCreate(nsIChannel* aChannel) {
+  MOZ_ASSERT(aChannel);
+
+  UC_LOG(("UrlClassifierFeatureCryptomining: MaybeCreate for channel %p",
+          aChannel));
+
+  if (!StaticPrefs::privacy_trackingprotection_cryptomining_enabled()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> chanURI;
+  nsresult rv = aChannel->GetURI(getter_AddRefs(chanURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  bool isThirdParty =
+      nsContentUtils::IsThirdPartyWindowOrChannel(nullptr, aChannel, chanURI);
+  if (!isThirdParty) {
+    if (UC_LOG_ENABLED()) {
+      nsCString spec = chanURI->GetSpecOrDefault();
+      spec.Truncate(
+          std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
+      UC_LOG(
+          ("UrlClassifierFeatureCryptomining: Skipping cryptomining checks "
+           "for first party or top-level load channel[%p] "
+           "with uri %s",
+           aChannel, spec.get()));
+    }
+
+    return nullptr;
+  }
+
+  if (!UrlClassifierCommon::ShouldEnableClassifier(
+          aChannel, AntiTrackingCommon::eCryptomining)) {
+    return nullptr;
+  }
+
+  MaybeInitialize();
+  MOZ_ASSERT(gFeatureCryptomining);
+
+  RefPtr<UrlClassifierFeatureCryptomining> self = gFeatureCryptomining;
+  return self.forget();
+}
+
+/* static */ already_AddRefed<nsIUrlClassifierFeature>
+UrlClassifierFeatureCryptomining::GetIfNameMatches(const nsACString& aName) {
+  if (!aName.EqualsLiteral(CRYPTOMINING_FEATURE_NAME)) {
+    return nullptr;
+  }
+
+  MaybeInitialize();
+  MOZ_ASSERT(gFeatureCryptomining);
+
+  RefPtr<UrlClassifierFeatureCryptomining> self = gFeatureCryptomining;
+  return self.forget();
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureCryptomining::ProcessChannel(nsIChannel* aChannel,
+                                                 const nsACString& aList,
+                                                 bool* aShouldContinue) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aShouldContinue);
+
+  // This is a blocking feature.
+  *aShouldContinue = false;
+
+  UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_TRACKING_URI, aList,
+                                         EmptyCString(), EmptyCString());
+
+  UC_LOG(
+      ("UrlClassifierFeatureCryptomining::ProcessChannel, cancelling "
+       "channel[%p]",
+       aChannel));
+  nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
+
+  // FIXME: the way we cancel the channel depends on what the UI wants to show.
+  // This needs to change, at some point.
+  if (httpChannel) {
+    Unused << httpChannel->CancelForTrackingProtection();
+  } else {
+    Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierFeatureCryptomining::GetURIByListType(
+    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
+    nsIURI** aURI) {
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  if (aListType == nsIUrlClassifierFeature::blacklist) {
+    return aChannel->GetURI(aURI);
+  }
+
+  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
+  return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/url-classifier/UrlClassifierFeatureCryptomining.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_net_UrlClassifierFeatureCryptomining_h
+#define mozilla_net_UrlClassifierFeatureCryptomining_h
+
+#include "UrlClassifierFeatureBase.h"
+
+class nsIChannel;
+
+namespace mozilla {
+namespace net {
+
+class UrlClassifierFeatureCryptomining final : public UrlClassifierFeatureBase {
+ public:
+  static const char* Name();
+
+  static void MaybeShutdown();
+
+  static already_AddRefed<UrlClassifierFeatureCryptomining> MaybeCreate(
+      nsIChannel* aChannel);
+
+  static already_AddRefed<nsIUrlClassifierFeature> GetIfNameMatches(
+      const nsACString& aName);
+
+  NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
+                            bool* aShouldContinue) override;
+
+  NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
+                              nsIUrlClassifierFeature::listType aListType,
+                              nsIURI** aURI) override;
+
+ private:
+  UrlClassifierFeatureCryptomining();
+
+  static void MaybeInitialize();
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_UrlClassifierFeatureCryptomining_h
--- a/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
+++ b/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/net/UrlClassifierFeatureFactory.h"
 
 // List of Features
+#include "UrlClassifierFeatureCryptomining.h"
 #include "UrlClassifierFeatureFingerprinting.h"
 #include "UrlClassifierFeatureFlash.h"
 #include "UrlClassifierFeatureLoginReputation.h"
 #include "UrlClassifierFeatureTrackingProtection.h"
 #include "UrlClassifierFeatureTrackingAnnotation.h"
 #include "UrlClassifierFeatureCustomTables.h"
 
 #include "nsAppRunner.h"
@@ -20,16 +21,17 @@ namespace mozilla {
 namespace net {
 
 /* static */ void UrlClassifierFeatureFactory::Shutdown() {
   // We want to expose Features only in the parent process.
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  UrlClassifierFeatureCryptomining::MaybeShutdown();
   UrlClassifierFeatureFingerprinting::MaybeShutdown();
   UrlClassifierFeatureFlash::MaybeShutdown();
   UrlClassifierFeatureLoginReputation::MaybeShutdown();
   UrlClassifierFeatureTrackingAnnotation::MaybeShutdown();
   UrlClassifierFeatureTrackingProtection::MaybeShutdown();
 }
 
 /* static */ void UrlClassifierFeatureFactory::GetFeaturesFromChannel(
@@ -40,16 +42,22 @@ namespace net {
 
   nsCOMPtr<nsIUrlClassifierFeature> feature;
 
   // Note that the order of the features is extremely important! When more than
   // 1 feature classifies the channel, we call ::ProcessChannel() following this
   // feature order, and this could produce different results with a different
   // feature ordering.
 
+  // Cryptomining
+  feature = UrlClassifierFeatureCryptomining::MaybeCreate(aChannel);
+  if (feature) {
+    aFeatures.AppendElement(feature);
+  }
+
   // Fingerprinting
   feature = UrlClassifierFeatureFingerprinting::MaybeCreate(aChannel);
   if (feature) {
     aFeatures.AppendElement(feature);
   }
 
   // Tracking Protection
   feature = UrlClassifierFeatureTrackingProtection::MaybeCreate(aChannel);
@@ -78,16 +86,22 @@ UrlClassifierFeatureFactory::GetFeatureL
 /* static */ already_AddRefed<nsIUrlClassifierFeature>
 UrlClassifierFeatureFactory::GetFeatureByName(const nsACString& aName) {
   if (!XRE_IsParentProcess()) {
     return nullptr;
   }
 
   nsCOMPtr<nsIUrlClassifierFeature> feature;
 
+  // Cryptomining
+  feature = UrlClassifierFeatureCryptomining::GetIfNameMatches(aName);
+  if (feature) {
+    return feature.forget();
+  }
+
   // Fingerprinting
   feature = UrlClassifierFeatureFingerprinting::GetIfNameMatches(aName);
   if (feature) {
     return feature.forget();
   }
 
   // Tracking Protection
   feature = UrlClassifierFeatureTrackingProtection::GetIfNameMatches(aName);
@@ -117,18 +131,24 @@ UrlClassifierFeatureFactory::GetFeatureB
 }
 
 /* static */ void UrlClassifierFeatureFactory::GetFeatureNames(
     nsTArray<nsCString>& aArray) {
   if (!XRE_IsParentProcess()) {
     return;
   }
 
+  // Cryptomining
+  nsAutoCString name;
+  name.Assign(UrlClassifierFeatureCryptomining::Name());
+  if (!name.IsEmpty()) {
+    aArray.AppendElement(name);
+  }
+
   // Fingerprinting
-  nsAutoCString name;
   name.Assign(UrlClassifierFeatureFingerprinting::Name());
   if (!name.IsEmpty()) {
     aArray.AppendElement(name);
   }
 
   // Tracking Protection
   name.Assign(UrlClassifierFeatureTrackingProtection::Name());
   if (!name.IsEmpty()) {
--- a/netwerk/url-classifier/moz.build
+++ b/netwerk/url-classifier/moz.build
@@ -17,16 +17,17 @@ XPIDL_MODULE = 'url-classifier'
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
 DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
 
 UNIFIED_SOURCES += [
     'AsyncUrlChannelClassifier.cpp',
     'nsChannelClassifier.cpp',
     'UrlClassifierCommon.cpp',
     'UrlClassifierFeatureBase.cpp',
+    'UrlClassifierFeatureCryptomining.cpp',
     'UrlClassifierFeatureCustomTables.cpp',
     'UrlClassifierFeatureFactory.cpp',
     'UrlClassifierFeatureFingerprinting.cpp',
     'UrlClassifierFeatureFlash.cpp',
     'UrlClassifierFeatureLoginReputation.cpp',
     'UrlClassifierFeatureResult.cpp',
     'UrlClassifierFeatureTrackingAnnotation.cpp',
     'UrlClassifierFeatureTrackingProtection.cpp',
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -1369,17 +1369,19 @@ nsresult AntiTrackingCommon::IsOnContent
   // For storage checks, check the storage pref, and for annotations checks,
   // check the corresponding pref as well.  This allows each set of checks to
   // be disabled individually if needed.
   if ((aPurpose == eStorageChecks &&
        !StaticPrefs::browser_contentblocking_allowlist_storage_enabled()) ||
       (aPurpose == eTrackingAnnotations &&
        !StaticPrefs::browser_contentblocking_allowlist_annotations_enabled()) ||
       (aPurpose == eFingerprinting &&
-       !StaticPrefs::privacy_trackingprotection_fingerprinting_enabled())) {
+       !StaticPrefs::privacy_trackingprotection_fingerprinting_enabled()) ||
+      (aPurpose == eCryptomining &&
+       !StaticPrefs::privacy_trackingprotection_cryptomining_enabled())) {
     LOG(
         ("Attempting to check the content blocking allow list aborted because "
          "the third-party cookies UI has been disabled."));
     return NS_OK;
   }
 
   LOG_SPEC(("Deciding whether the user has overridden content blocking for %s",
             _spec),
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -116,16 +116,17 @@ class AntiTrackingCommon final {
       const nsCString& aParentOrigin, const nsCString& aGrantedOrigin,
       int aAllowMode);
 
   enum ContentBlockingAllowListPurpose {
     eStorageChecks,
     eTrackingProtection,
     eTrackingAnnotations,
     eFingerprinting,
+    eCryptomining,
   };
 
   // Check whether a top window URI is on the content blocking allow list.
   static nsresult IsOnContentBlockingAllowList(
       nsIURI* aTopWinURI, bool aIsPrivateBrowsing,
       ContentBlockingAllowListPurpose aPurpose, bool& aIsAllowListed);
 
   enum class BlockingDecision {
--- a/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
+++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
@@ -41,8 +41,9 @@ skip-if = (os == 'linux' && debug) #Bug 
 skip-if = (verify && debug && (os == 'win' || os == 'mac'))
 [test_classify_track.html]
 [test_gethash.html]
 [test_bug1254766.html]
 [test_cachemiss.html]
 skip-if = verify
 [test_annotation_vs_TP.html]
 [test_fingerprinting.html]
+[test_cryptomining.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test the cryptomining classifier</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+/* eslint-env mozilla/frame-script */
+
+var tests = [
+  // All disabled.
+  { config: [ false, false ], loadExpected: true },
+
+  // Just whitelisted.
+  { config: [ false, true ], loadExpected: true },
+
+  // Just blacklisted.
+  { config: [ true, false ], loadExpected: false },
+
+  // whitelist + blacklist: whitelist wins
+  { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+  return value ? what : "";
+}
+
+async function runTest(test) {
+  await SpecialPowers.pushPrefEnv({set: [
+    [ "urlclassifier.features.cryptomining.blacklistHosts", prefValue(test.config[0], "example.com") ],
+    [ "urlclassifier.features.cryptomining.whitelistHosts", prefValue(test.config[1], "mochi.test") ],
+    [ "urlclassifier.features.cryptomining.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
+    [ "urlclassifier.features.cryptomining.whitelistTables", "" ],
+    [ "privacy.trackingprotection.enabled", false ],
+    [ "privacy.trackingprotection.annotate_channels", false ],
+    [ "privacy.trackingprotection.cryptomining.enabled", true ],
+    [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+  ]});
+
+  info("Testing: " + test.config.toSource() + "\n");
+
+  // Let's load an image with a random query string, just to avoid network cache.
+  let result = await new Promise(resolve => {
+    let image = new Image();
+    image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+    image.onload = _ => resolve(true);
+    image.onerror = _ => resolve(false);
+  });
+
+  is(result, test.loadExpected, "The loading happened correctly");
+
+  // Let's load an image with a random query string, just to avoid network cache.
+  result = await new Promise(resolve => {
+    let image = new Image();
+    image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+    image.onload = _ => resolve(true);
+    image.onerror = _ => resolve(false);
+  });
+
+  is(result, test.loadExpected, "The loading happened correctly (by table)");
+}
+
+async function runTests() {
+  let chromeScript = SpecialPowers.loadChromeScript(_ => {
+    ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+
+    addMessageListener("loadTrackers", __ => {
+      UrlClassifierTestUtils.addTestTrackers().then(___ => {
+        sendAsyncMessage("trackersLoaded");
+      });
+    });
+
+    addMessageListener("unloadTrackers", __ => {
+      UrlClassifierTestUtils.cleanupTestTrackers();
+      sendAsyncMessage("trackersUnloaded");
+    });
+  });
+
+  await new Promise(resolve => {
+    chromeScript.addMessageListener("trackersLoaded", resolve);
+    chromeScript.sendAsyncMessage("loadTrackers");
+  });
+
+  for (let test in tests) {
+    await runTest(tests[test]);
+  }
+
+  await new Promise(resolve => {
+    chromeScript.addMessageListener("trackersUnloaded", resolve);
+    chromeScript.sendSyncMessage("unloadTrackers");
+  });
+
+  chromeScript.destroy();
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
--- a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
@@ -32,16 +32,17 @@ function prefValue(value, what) {
 async function runTest(test) {
   await SpecialPowers.pushPrefEnv({set: [
     [ "urlclassifier.features.fingerprinting.blacklistHosts", prefValue(test.config[0], "example.com") ],
     [ "urlclassifier.features.fingerprinting.whitelistHosts", prefValue(test.config[1], "mochi.test") ],
     [ "urlclassifier.features.fingerprinting.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
     [ "urlclassifier.features.fingerprinting.whitelistTables", "" ],
     [ "privacy.trackingprotection.enabled", false ],
     [ "privacy.trackingprotection.annotate_channels", false ],
+    [ "privacy.trackingprotection.cryptomining.enabled", false ],
     [ "privacy.trackingprotection.fingerprinting.enabled", true ],
   ]});
 
   info("Testing: " + test.config.toSource() + "\n");
 
   // Let's load an image with a random query string, just to avoid network cache.
   let result = await new Promise(resolve => {
     let image = new Image();