summaryrefslogtreecommitdiffstats
path: root/toolkit/components/url-classifier/tests/mochitest
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/url-classifier/tests/mochitest')
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html143
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bad.css1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bad.css^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/basic.vtt27
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bug_1281083.html11
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bug_1580416.html13
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/cache.sjs90
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/chrome.ini69
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html154
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html26
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierCommon.js90
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierFrame.html57
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierHelper.js187
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/cleanWorker.js12
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/dnt.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/dnt.sjs9
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.css1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.css^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.js3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.js^headers^2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evilWorker.js5
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/features.js291
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/gethash.sjs86
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/gethashFrame.html61
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/good.js3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/head.js48
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/import.css3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/import2.css3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/mochitest.ini101
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/ping.sjs15
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/raptor.jpgbin0 -> 49629 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs7
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/report.sjs73
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sandboxed.html8
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/seek.webmbin0 -> 215529 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_register.html33
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_unregister.html12
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_worker.js10
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html134
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html58
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html259
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html167
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html167
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier.html141
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html155
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html72
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html187
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html73
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html156
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html131
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html74
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_track.html164
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html98
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_donottrack.html139
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html128
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html128
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_gethash.html118
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html149
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_reporturl.html213
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html91
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html107
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html32
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html241
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html96
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html156
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html98
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html159
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/threathit.sjs48
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/track.html7
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackerFrame.html91
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs82
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.html21
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.js9
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js5
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/update.sjs71
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/vp9.webmbin0 -> 97465 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html15
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/workerFrame.html65
84 files changed, 6089 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
new file mode 100644
index 0000000000..1096274260
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
@@ -0,0 +1,143 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+// Modified by evil.js
+var scriptItem;
+
+var scriptItem1 = "untouched";
+var imageItem1 = "untouched";
+var frameItem1 = "untouched";
+var scriptItem2 = "untouched";
+var imageItem2 = "untouched";
+var frameItem2 = "untouched";
+var xhrItem = "untouched";
+var fetchItem = "untouched";
+var mediaItem1 = "untouched";
+
+async function checkLoads() {
+ window.parent.is(scriptItem1, "spoiled", "Should not block tracking js 1");
+ window.parent.is(scriptItem2, "spoiled", "Should not block tracking js 2");
+ window.parent.is(imageItem1, "spoiled", "Should not block tracking img 1");
+ window.parent.is(imageItem2, "spoiled", "Should not block tracking img 2");
+ window.parent.is(frameItem1, "spoiled", "Should not block tracking iframe 1");
+ window.parent.is(frameItem2, "spoiled", "Should not block tracking iframe 2");
+ window.parent.is(mediaItem1, "loaded", "Should not block tracking video");
+ window.parent.is(xhrItem, "loaded", "Should not block tracking XHR");
+ window.parent.is(fetchItem, "loaded", "Should not block fetches from tracking domains");
+ window.parent.is(window.document.blockedNodeByClassifierCount, 0,
+ "No elements should be blocked");
+
+ // End (parent) test.
+ await window.parent.clearPermissions();
+ window.parent.SimpleTest.finish();
+}
+
+var onloadCalled = false;
+var xhrFinished = false;
+var fetchFinished = false;
+var videoLoaded = false;
+function loaded(type) {
+ if (type === "onload") {
+ onloadCalled = true;
+ } else if (type === "xhr") {
+ xhrFinished = true;
+ } else if (type === "fetch") {
+ fetchFinished = true;
+ } else if (type === "video") {
+ videoLoaded = true;
+ }
+
+ if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) {
+ checkLoads();
+ }
+}
+</script>
+
+</head>
+
+<body onload="loaded('onload')">
+
+<!-- Try loading from a tracking script URI (1) -->
+<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = 'spoiled';"></script>
+
+<!-- Try loading from a tracking image URI (1) -->
+<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg" onload="imageItem1 = 'spoiled';"/>
+
+<!-- Try loading from a tracking frame URI (1) -->
+<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
+
+<!-- Try loading from a tracking video URI -->
+<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm"></video>
+
+<script>
+var v = document.getElementById("badmedia1");
+v.addEventListener("loadedmetadata", function() {
+ mediaItem1 = "loaded";
+ loaded("video");
+}, true);
+v.addEventListener("error", function() {
+ mediaItem1 = "error";
+ loaded("video");
+}, true);
+
+// Try loading from a tracking script URI (2) - The loader may follow a
+// different path depending on whether the resource is loaded from JS or HTML.
+var newScript = document.createElement("script");
+newScript.id = "badscript2";
+newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+newScript.addEventListener("load", function onload() { scriptItem2 = "spoiled"; });
+document.body.appendChild(newScript);
+
+// / Try loading from a tracking image URI (2)
+var newImage = document.createElement("img");
+newImage.id = "badimage2";
+newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg";
+newImage.addEventListener("load", function onload() { imageItem2 = "spoiled"; });
+document.body.appendChild(newImage);
+
+// Try loading from a tracking iframe URI (2)
+var newFrame = document.createElement("iframe");
+newFrame.id = "badframe2";
+newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html";
+newFrame.addEventListener("load", function onload() { frameItem2 = "spoiled"; });
+document.body.appendChild(newFrame);
+
+// Try doing an XHR against a tracking domain (bug 1216793)
+function reqListener() {
+ xhrItem = "loaded";
+ loaded("xhr");
+}
+function transferFailed() {
+ xhrItem = "failed";
+ loaded("xhr");
+}
+function transferCanceled() {
+ xhrItem = "canceled";
+ loaded("xhr");
+}
+var oReq = new XMLHttpRequest();
+oReq.addEventListener("load", reqListener);
+oReq.addEventListener("error", transferFailed);
+oReq.addEventListener("abort", transferCanceled);
+oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js");
+oReq.send();
+
+// Fetch from a tracking domain
+fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) {
+ if (response.ok) {
+ fetchItem = "loaded";
+ loaded("fetch");
+ } else {
+ fetchItem = "badresponse";
+ loaded("fetch");
+ }
+ }).catch(function(error) {
+ fetchItem = "error";
+ loaded("fetch");
+});
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css b/toolkit/components/url-classifier/tests/mochitest/bad.css
new file mode 100644
index 0000000000..f57b36a778
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bad.css
@@ -0,0 +1 @@
+#styleBad { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt b/toolkit/components/url-classifier/tests/mochitest/basic.vtt
new file mode 100644
index 0000000000..7781790d04
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt
@@ -0,0 +1,27 @@
+WEBVTT
+Region: id=testOne lines=2 width=30%
+Region: id=testTwo lines=4 width=20%
+
+1
+00:00.500 --> 00:00.700 region:testOne
+This
+
+2
+00:01.200 --> 00:02.400 region:testTwo
+Is
+
+2.5
+00:02.000 --> 00:03.500 region:testOne
+(Over here?!)
+
+3
+00:02.710 --> 00:02.910
+A
+
+4
+00:03.217 --> 00:03.989
+Test
+
+5
+00:03.217 --> 00:03.989
+And more!
diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^
new file mode 100644
index 0000000000..23de552c1a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: * \ No newline at end of file
diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html
new file mode 100644
index 0000000000..80124da3cc
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<!-- Try loading from a malware javascript URI -->
+<script id="badscript" data-touched="not sure" src="http://bug1281083.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html
new file mode 100644
index 0000000000..ae305bbb47
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<script id="goodscript" data-touched="not sure" src="http://mochitest.apps.fbsbx.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/cache.sjs b/toolkit/components/url-classifier/tests/mochitest/cache.sjs
new file mode 100644
index 0000000000..84fb0e9089
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/cache.sjs
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ var responseBody;
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ // gethash count return how many gethash request received.
+ // This is used by client to know if a gethash request is triggered by gecko
+ } else if ("gethashcount" == request.queryString) {
+ let counter = getState("counter");
+ responseBody = counter == "" ? "0" : counter;
+ } else {
+ let body = new BinaryInputStream(request.bodyInputStream);
+ let avail;
+ let bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ let counter = getState("counter");
+ counter = counter == "" ? "1" : (parseInt(counter) + 1).toString();
+ setState("counter", counter);
+
+ responseBody = parseV2Request(bytes);
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var request = String.fromCharCode.apply(this, bytes);
+ var [HEADER, PREFIXES] = request.split("\n");
+ var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => {
+ return parseInt(val);
+ });
+
+ var ret = "";
+ for (var start = 0; start < LENGTH; start += PREFIXSIZE) {
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ var completions = getState(list).split("\n");
+
+ for (var completion of completions) {
+ if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) {
+ ret += list + ":1:32\n";
+ ret += completion;
+ }
+ }
+ });
+ }
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/chrome.ini b/toolkit/components/url-classifier/tests/mochitest/chrome.ini
new file mode 100644
index 0000000000..71087f55ab
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/chrome.ini
@@ -0,0 +1,69 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ allowlistAnnotatedFrame.html
+ classifiedAnnotatedFrame.html
+ classifiedAnnotatedPBFrame.html
+ trackingRequest.html
+ bug_1281083.html
+ bug_1580416.html
+ report.sjs
+ gethash.sjs
+ classifierCommon.js
+ classifierHelper.js
+ head.js
+ threathit.sjs
+ redirect_tracker.sjs
+ !/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
+ !/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
+ !/toolkit/components/url-classifier/tests/mochitest/good.js
+ !/toolkit/components/url-classifier/tests/mochitest/evil.css
+ !/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^
+ !/toolkit/components/url-classifier/tests/mochitest/evil.js
+ !/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^
+ !/toolkit/components/url-classifier/tests/mochitest/evilWorker.js
+ !/toolkit/components/url-classifier/tests/mochitest/import.css
+ !/toolkit/components/url-classifier/tests/mochitest/raptor.jpg
+ !/toolkit/components/url-classifier/tests/mochitest/track.html
+ !/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js
+ !/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^
+ !/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js
+ !/toolkit/components/url-classifier/tests/mochitest/vp9.webm
+ !/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html
+ !/toolkit/components/url-classifier/tests/mochitest/workerFrame.html
+ !/toolkit/components/url-classifier/tests/mochitest/ping.sjs
+ !/toolkit/components/url-classifier/tests/mochitest/basic.vtt
+ !/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^
+ !/toolkit/components/url-classifier/tests/mochitest/dnt.html
+ !/toolkit/components/url-classifier/tests/mochitest/dnt.sjs
+ !/toolkit/components/url-classifier/tests/mochitest/update.sjs
+ !/toolkit/components/url-classifier/tests/mochitest/bad.css
+ !/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
+ !/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
+ !/toolkit/components/url-classifier/tests/mochitest/seek.webm
+ !/toolkit/components/url-classifier/tests/mochitest/cache.sjs
+
+[test_classified_annotations.html]
+tags = trackingprotection
+skip-if = os == 'linux' && asan # Bug 1202548
+[test_allowlisted_annotations.html]
+tags = trackingprotection
+[test_privatebrowsing_trackingprotection.html]
+tags = trackingprotection
+[test_trackingprotection_bug1157081.html]
+tags = trackingprotection
+[test_trackingprotection_bug1580416.html]
+tags = trackingprotection
+[test_trackingprotection_whitelist.html]
+tags = trackingprotection
+[test_safebrowsing_bug1272239.html]
+[test_donottrack.html]
+[test_classifier_changetablepref.html]
+skip-if = verify
+[test_classifier_changetablepref_bug1395411.html]
+[test_reporturl.html]
+skip-if = verify
+[test_trackingprotection_bug1312515.html]
+[test_advisory_link.html]
+[test_threathit_report.html]
+skip-if = verify
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
new file mode 100644
index 0000000000..9b12f529cb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
@@ -0,0 +1,154 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+"use strict";
+
+var scriptItem = "untouched";
+var scriptItem1 = "untouched";
+var scriptItem2 = "untouched";
+var imageItem1 = "untouched";
+var imageItem2 = "untouched";
+var frameItem1 = "untouched";
+var frameItem2 = "untouched";
+var xhrItem = "untouched";
+var fetchItem = "untouched";
+var mediaItem1 = "untouched";
+
+var badids = [
+ "badscript1",
+ "badscript2",
+ "badimage1",
+ "badimage2",
+ "badframe1",
+ "badframe2",
+ "badmedia1",
+ "badcss",
+];
+
+var onloadCalled = false;
+var xhrFinished = false;
+var fetchFinished = false;
+var videoLoaded = false;
+function loaded(type) {
+ if (type === "onload") {
+ onloadCalled = true;
+ } else if (type === "xhr") {
+ xhrFinished = true;
+ } else if (type === "fetch") {
+ fetchFinished = true;
+ } else if (type === "video") {
+ videoLoaded = true;
+ }
+ if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) {
+ var msg = new window.CustomEvent("OnLoadComplete", {
+ detail: JSON.stringify({
+ scriptItem,
+ scriptItem1,
+ scriptItem2,
+ imageItem1,
+ imageItem2,
+ frameItem1,
+ frameItem2,
+ xhrItem,
+ fetchItem,
+ mediaItem1,
+ }),
+ });
+ window.dispatchEvent(msg);
+ }
+}
+</script>
+
+<!-- Try loading from a tracking CSS URI -->
+<link id="badcss" rel="stylesheet" type="text/css" href="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+</head>
+
+<body onload="loaded('onload')">
+
+<!-- Try loading from a tracking script URI (1): evil.js onload will have updated the scriptItem variable -->
+<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = scriptItem;"></script>
+
+<!-- Try loading from a tracking image URI (1) -->
+<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true" onload="imageItem1 = 'spoiled';"/>
+
+<!-- Try loading from a tracking frame URI (1) -->
+<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
+
+<!-- Try loading from a tracking video URI -->
+<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm?reload=true"></video>
+
+<script>
+var v = document.getElementById("badmedia1");
+v.addEventListener("loadedmetadata", function() {
+ mediaItem1 = "loaded";
+ loaded("video");
+}, true);
+v.addEventListener("error", function() {
+ mediaItem1 = "error";
+ loaded("video");
+}, true);
+
+// Try loading from a tracking script URI (2) - The loader may follow a different path depending on whether the resource is loaded from JS or HTML.
+var newScript = document.createElement("script");
+newScript.id = "badscript2";
+newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+newScript.addEventListener("load", function() { scriptItem2 = scriptItem; });
+document.body.appendChild(newScript);
+
+// Try loading from a tracking image URI (2)
+var newImage = document.createElement("img");
+newImage.id = "badimage2";
+newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true";
+newImage.addEventListener("load", function() { imageItem2 = "spoiled"; });
+document.body.appendChild(newImage);
+
+// Try loading from a tracking iframe URI (2)
+var newFrame = document.createElement("iframe");
+newFrame.id = "badframe2";
+newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html";
+newFrame.addEventListener("load", function() { frameItem2 = "spoiled"; });
+document.body.appendChild(newFrame);
+
+// Try doing an XHR against a tracking domain (bug 1216793)
+function reqListener() {
+ xhrItem = "loaded";
+ loaded("xhr");
+}
+function transferFailed() {
+ xhrItem = "failed";
+ loaded("xhr");
+}
+function transferCanceled() {
+ xhrItem = "canceled";
+ loaded("xhr");
+}
+var oReq = new XMLHttpRequest();
+oReq.addEventListener("load", reqListener);
+oReq.addEventListener("error", transferFailed);
+oReq.addEventListener("abort", transferCanceled);
+oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js");
+oReq.send();
+
+// Fetch from a tracking domain
+fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) {
+ if (response.ok) {
+ fetchItem = "loaded";
+ loaded("fetch");
+ } else {
+ fetchItem = "badresponse";
+ loaded("fetch");
+ }
+ }).catch(function(error) {
+ fetchItem = "error";
+ loaded("fetch");
+});
+</script>
+
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html
new file mode 100644
index 0000000000..ecd89e91d6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+
+<link id="badcss" rel="stylesheet" type="text/css" href="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+</head>
+<body>
+
+<script id="badscript" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<script id="goodscript" data-touched="not sure" src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<!-- The image cache can cache JS handlers, so make sure we use a different URL for raptor.jpg each time -->
+<img id="badimage" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/>
+
+<img id="goodimage" data-touched="not sure" src="https://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test2" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/>
+
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
new file mode 100644
index 0000000000..49ae6647b0
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+);
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(
+ Ci.nsIUrlListManager
+);
+
+var timer;
+function setTimeout(callback, delay) {
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ { notify: callback },
+ delay,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+}
+
+function doUpdate(update) {
+ let listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
+ updateUrlRequested(url) {},
+ streamFinished(status) {},
+ updateError(errorCode) {
+ sendAsyncMessage("updateError", errorCode);
+ },
+ updateSuccess(requestedTimeout) {
+ sendAsyncMessage("updateSuccess");
+ },
+ };
+
+ try {
+ dbService.beginUpdate(
+ listener,
+ "test-malware-simple,test-unwanted-simple",
+ ""
+ );
+ dbService.beginStream("", "");
+ dbService.updateStream(update);
+ dbService.finishStream();
+ dbService.finishUpdate();
+ } catch (e) {
+ // beginUpdate may fail if there's an existing update in progress
+ // retry until success or testcase timeout.
+ setTimeout(() => {
+ doUpdate(update);
+ }, 1000);
+ }
+}
+
+function doReload() {
+ try {
+ dbService.reloadDatabase();
+ sendAsyncMessage("reloadSuccess");
+ } catch (e) {
+ setTimeout(() => {
+ doReload();
+ }, 1000);
+ }
+}
+
+// SafeBrowsing.jsm is initialized after mozEntries are added. Add observer
+// to receive "finished" event. For the case when this function is called
+// after the event had already been notified, we lookup entries to see if
+// they are already added to database.
+function waitForInit() {
+ if (listmanager.isRegistered()) {
+ sendAsyncMessage("safeBrowsingInited");
+ } else {
+ setTimeout(() => {
+ waitForInit();
+ }, 1000);
+ }
+}
+
+addMessageListener("doUpdate", ({ testUpdate }) => {
+ doUpdate(testUpdate);
+});
+
+addMessageListener("doReload", () => {
+ doReload();
+});
+
+addMessageListener("waitForInit", () => {
+ waitForInit();
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
new file mode 100644
index 0000000000..1e3617b9d8
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
@@ -0,0 +1,57 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+var scriptItem = "untouched";
+
+function checkLoads() {
+ // Make sure the javascript did not load.
+ window.parent.is(scriptItem, "untouched", "Should not load bad javascript");
+
+ // Make sure the css did not load.
+ var elt = document.getElementById("styleCheck");
+ var style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+
+ elt = document.getElementById("styleBad");
+ style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+
+ elt = document.getElementById("styleImport");
+ style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "visible", "Should import clean css");
+
+ // Call parent.loadTestFrame again to test classification metadata in HTTP
+ // cache entries.
+ if (window.parent.firstLoad) {
+ window.parent.info("Reloading from cache...");
+ window.parent.firstLoad = false;
+ window.parent.loadTestFrame();
+ return;
+ }
+
+ // End (parent) test.
+ window.parent.SimpleTest.finish();
+}
+
+</script>
+
+<!-- Try loading from a malware javascript URI -->
+<script type="text/javascript" src="http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
+
+<!-- Try loading from an uwanted software css URI -->
+<link rel="stylesheet" type="text/css" href="http://unwanted.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+<!-- Try loading a marked-as-malware css through an @import from a clean URI -->
+<link rel="stylesheet" type="text/css" href="import2.css"></link>
+</head>
+
+<body onload="checkLoads()">
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+<div id="styleBad">STYLE BAD</div>
+<div id="styleImport">STYLE IMPORT</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
new file mode 100644
index 0000000000..5eebc556ca
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
@@ -0,0 +1,187 @@
+if (typeof classifierHelper == "undefined") {
+ var classifierHelper = {};
+}
+
+const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js");
+var gScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL);
+
+const PREFS = {
+ PROVIDER_LISTS: "browser.safebrowsing.provider.mozilla.lists",
+ DISALLOW_COMPLETIONS: "urlclassifier.disallow_completions",
+ PROVIDER_GETHASHURL: "browser.safebrowsing.provider.mozilla.gethashURL",
+};
+
+classifierHelper._curAddChunkNum = 1;
+
+// addUrlToDB is asynchronous, queue the task to ensure
+// the callback follow correct order.
+classifierHelper._updates = [];
+
+// Keep urls added to database, those urls should be automatically
+// removed after test complete.
+classifierHelper._updatesToCleanup = [];
+
+classifierHelper._initsCB = [];
+
+// This function return a Promise, promise is resolved when SafeBrowsing.jsm
+// is initialized.
+classifierHelper.waitForInit = function () {
+ return new Promise(function (resolve, reject) {
+ classifierHelper._initsCB.push(resolve);
+ gScript.sendAsyncMessage("waitForInit");
+ });
+};
+
+// This function is used to allow completion for specific "list",
+// some lists like "test-malware-simple" is default disabled to ask for complete.
+// "list" is the db we would like to allow it
+// "url" is the completion server
+classifierHelper.allowCompletion = async function (lists, url) {
+ for (var list of lists) {
+ // Add test db to provider
+ var pref = await SpecialPowers.getParentCharPref(PREFS.PROVIDER_LISTS);
+ pref += "," + list;
+ await SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref);
+
+ // Rename test db so we will not disallow it from completions
+ pref = await SpecialPowers.getParentCharPref(PREFS.DISALLOW_COMPLETIONS);
+ pref = pref.replace(list, list + "-backup");
+ await SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref);
+ }
+
+ // Set get hash url
+ await SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, url);
+};
+
+// Pass { url: ..., db: ... } to add url to database,
+// onsuccess/onerror will be called when update complete.
+classifierHelper.addUrlToDB = function (updateData) {
+ return new Promise(function (resolve, reject) {
+ var testUpdate = "";
+ for (var update of updateData) {
+ var LISTNAME = update.db;
+ var CHUNKDATA = update.url;
+ var CHUNKLEN = CHUNKDATA.length;
+ var HASHLEN = update.len ? update.len : 32;
+
+ update.addChunk = classifierHelper._curAddChunkNum;
+ classifierHelper._curAddChunkNum += 1;
+
+ classifierHelper._updatesToCleanup.push(update);
+ testUpdate +=
+ "n:1000\n" +
+ "i:" +
+ LISTNAME +
+ "\n" +
+ "ad:1\n" +
+ "a:" +
+ update.addChunk +
+ ":" +
+ HASHLEN +
+ ":" +
+ CHUNKLEN +
+ "\n" +
+ CHUNKDATA;
+ }
+
+ classifierHelper._update(testUpdate, resolve, reject);
+ });
+};
+
+// This API is used to expire all add/sub chunks we have updated
+// by using addUrlToDB.
+classifierHelper.resetDatabase = function () {
+ function removeDatabase() {
+ return new Promise(function (resolve, reject) {
+ var testUpdate = "";
+ for (var update of classifierHelper._updatesToCleanup) {
+ testUpdate +=
+ "n:1000\ni:" + update.db + "\nad:" + update.addChunk + "\n";
+ }
+
+ classifierHelper._update(testUpdate, resolve, reject);
+ });
+ }
+
+ // Remove and then reload will ensure both database and cache will
+ // be cleared.
+ return Promise.resolve()
+ .then(removeDatabase)
+ .then(classifierHelper.reloadDatabase);
+};
+
+classifierHelper.reloadDatabase = function () {
+ return new Promise(function (resolve, reject) {
+ gScript.addMessageListener("reloadSuccess", function handler() {
+ gScript.removeMessageListener("reloadSuccess", handler);
+ resolve();
+ });
+
+ gScript.sendAsyncMessage("doReload");
+ });
+};
+
+classifierHelper._update = function (testUpdate, onsuccess, onerror) {
+ // Queue the task if there is still an on-going update
+ classifierHelper._updates.push({
+ data: testUpdate,
+ onsuccess,
+ onerror,
+ });
+ if (classifierHelper._updates.length != 1) {
+ return;
+ }
+
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+};
+
+classifierHelper._updateSuccess = function () {
+ var update = classifierHelper._updates.shift();
+ update.onsuccess();
+
+ if (classifierHelper._updates.length) {
+ var testUpdate = classifierHelper._updates[0].data;
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+ }
+};
+
+classifierHelper._updateError = function (errorCode) {
+ var update = classifierHelper._updates.shift();
+ update.onerror(errorCode);
+
+ if (classifierHelper._updates.length) {
+ var testUpdate = classifierHelper._updates[0].data;
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+ }
+};
+
+classifierHelper._inited = function () {
+ classifierHelper._initsCB.forEach(function (cb) {
+ cb();
+ });
+ classifierHelper._initsCB = [];
+};
+
+classifierHelper._setup = function () {
+ gScript.addMessageListener("updateSuccess", classifierHelper._updateSuccess);
+ gScript.addMessageListener("updateError", classifierHelper._updateError);
+ gScript.addMessageListener("safeBrowsingInited", classifierHelper._inited);
+
+ // cleanup will be called at end of each testcase to remove all the urls added to database.
+ SimpleTest.registerCleanupFunction(classifierHelper._cleanup);
+};
+
+classifierHelper._cleanup = function () {
+ // clean all the preferences may touch by helper
+ for (var pref in PREFS) {
+ SpecialPowers.clearUserPref(pref);
+ }
+
+ if (!classifierHelper._updatesToCleanup) {
+ return Promise.resolve();
+ }
+
+ return classifierHelper.resetDatabase();
+};
+
+classifierHelper._setup();
diff --git a/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
new file mode 100644
index 0000000000..e7361119b6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
@@ -0,0 +1,12 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ try {
+ importScripts("evilWorker.js");
+ } catch (ex) {
+ postMessage("success");
+ return;
+ }
+
+ postMessage("failure");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.html b/toolkit/components/url-classifier/tests/mochitest/dnt.html
new file mode 100644
index 0000000000..2246263688
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/dnt.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+function makeXHR(url, callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, true);
+ xhr.onload = function() {
+ callback(xhr.response);
+ };
+ xhr.send();
+}
+
+function loaded(type) {
+ window.parent.postMessage("navigator.doNotTrack=" + navigator.doNotTrack, "*");
+
+ makeXHR("dnt.sjs", (res) => {
+ window.parent.postMessage("DNT=" + res, "*");
+ window.parent.postMessage("finish", "*");
+ });
+}
+
+</script>
+</head>
+
+<body onload="loaded('onload')">
+</body>
+
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.sjs b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs
new file mode 100644
index 0000000000..bbb836482a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs
@@ -0,0 +1,9 @@
+function handleRequest(request, response) {
+ var dnt = "unspecified";
+ if (request.hasHeader("DNT")) {
+ dnt = "1";
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(dnt);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css b/toolkit/components/url-classifier/tests/mochitest/evil.css
new file mode 100644
index 0000000000..62d506c899
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.css
@@ -0,0 +1 @@
+#styleCheck { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js b/toolkit/components/url-classifier/tests/mochitest/evil.js
new file mode 100644
index 0000000000..3e8b165587
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.js
@@ -0,0 +1,3 @@
+/* global scriptItem:true */
+
+scriptItem = "loaded malware javascript!";
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^
new file mode 100644
index 0000000000..3eced96143
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: *
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/evilWorker.js b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js
new file mode 100644
index 0000000000..b4e8a47602
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js
@@ -0,0 +1,5 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ postMessage("loaded bad file");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/features.js b/toolkit/components/url-classifier/tests/mochitest/features.js
new file mode 100644
index 0000000000..0004693d81
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/features.js
@@ -0,0 +1,291 @@
+var tests = [
+ // Config is an array with 4 elements:
+ // - annotation blocklist
+ // - annotation entitylist
+ // - tracking blocklist
+ // - tracking entitylist
+
+ // All disabled.
+ {
+ config: [false, false, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just entitylisted.
+ {
+ config: [false, false, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just blocklisted.
+ {
+ config: [false, false, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // entitylist + blocklist => entitylist wins
+ {
+ config: [false, false, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // just annotated in entitylist.
+ {
+ config: [false, true, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // TP and annotation entitylisted.
+ {
+ config: [false, true, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Annotation entitylisted, but TP blocklisted.
+ {
+ config: [false, true, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // Annotation entitylisted. TP blocklisted and entitylisted: entitylist wins.
+ {
+ config: [false, true, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just blocklist annotated.
+ {
+ config: [true, false, false, false],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated but TP entitylisted.
+ {
+ config: [true, false, false, true],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated and blocklisted.
+ {
+ config: [true, false, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // annotated, TP blocklisted and entitylisted: entitylist wins.
+ {
+ config: [true, false, true, true],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated in white and blocklist.
+ {
+ config: [true, true, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // annotated in white and blocklist. TP Whiteslited
+ {
+ config: [true, true, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // everywhere. TP entitylist wins.
+ {
+ config: [true, true, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+];
+
+function prefBlacklistValue(value) {
+ return value ? "example.com" : "";
+}
+
+function prefWhitelistValue(value) {
+ return value ? "mochi.test,mochi.xorigin-test" : "";
+}
+
+async function runTest(test, expectedFlag, expectedTrackingResource, prefs) {
+ let config = [
+ [
+ "urlclassifier.trackingAnnotationTable.testEntries",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+
+ [
+ "urlclassifier.trackingAnnotationWhitelistTable.testEntries",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+
+ [
+ "urlclassifier.trackingTable.testEntries",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+
+ [
+ "urlclassifier.trackingWhitelistTable.testEntries",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ ];
+
+ info("Testing: " + JSON.stringify(config) + "\n");
+
+ await SpecialPowers.pushPrefEnv({ set: config.concat(prefs) });
+
+ // This promise will be resolved when the chromeScript knows if the channel
+ // is annotated or not.
+ let annotationPromise;
+ if (test.loadExpected) {
+ info("We want to have annotation information");
+ annotationPromise = new Promise(resolve => {
+ chromeScript.addMessageListener(
+ "last-channel-flags",
+ data => resolve(data),
+ { once: true }
+ );
+ });
+ }
+
+ // Let's load a script with a random query string to avoid network cache.
+ // Using a script as the fingerprinting feature does not block display content
+ let result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly");
+
+ if (annotationPromise) {
+ let data = await annotationPromise;
+ is(
+ !!data.classificationFlags,
+ test.annotationExpected,
+ "The annotation happened correctly"
+ );
+ if (test.annotationExpected) {
+ is(data.classificationFlags, expectedFlag, "Correct flag");
+ is(
+ data.isThirdPartyTrackingResource,
+ expectedTrackingResource,
+ "Tracking resource flag matches"
+ );
+ }
+ }
+}
+
+var chromeScript;
+
+function runTests(flag, prefs, trackingResource) {
+ chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel);
+ if (
+ !channel ||
+ !classifiedChannel ||
+ !channel.URI.spec.startsWith(
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"
+ )
+ ) {
+ return;
+ }
+
+ sendAsyncMessage("last-channel-flags", {
+ classificationFlags: classifiedChannel.classificationFlags,
+ isThirdPartyTrackingResource:
+ classifiedChannel.isThirdPartyTrackingResource(),
+ });
+ }
+
+ addMessageListener("done", __ => {
+ Services.obs.removeObserver(onExamResp, "http-on-examine-response");
+ });
+
+ Services.obs.addObserver(onExamResp, "http-on-examine-response");
+
+ sendAsyncMessage("start-test");
+ });
+
+ chromeScript.addMessageListener(
+ "start-test",
+ async _ => {
+ for (let test in tests) {
+ await runTest(tests[test], flag, trackingResource, prefs);
+ }
+
+ chromeScript.sendAsyncMessage("done");
+ SimpleTest.finish();
+ },
+ { once: true }
+ );
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/gethash.sjs b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs
new file mode 100644
index 0000000000..12e8c11835
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs
@@ -0,0 +1,86 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ var responseBody;
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ // gethash count return how many gethash request received.
+ // This is used by client to know if a gethash request is triggered by gecko
+ } else if ("gethashcount" == request.queryString) {
+ let counter = getState("counter");
+ responseBody = counter == "" ? "0" : counter;
+ } else {
+ let body = new BinaryInputStream(request.bodyInputStream);
+ let avail;
+ let bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ let counter = getState("counter");
+ counter = counter == "" ? "1" : (parseInt(counter) + 1).toString();
+ setState("counter", counter);
+
+ responseBody = parseV2Request(bytes);
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var request = String.fromCharCode.apply(this, bytes);
+ var [HEADER, PREFIXES] = request.split("\n");
+ var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => {
+ return parseInt(val);
+ });
+
+ var ret = "";
+ for (var start = 0; start < LENGTH; start += PREFIXSIZE) {
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ var completions = getState(list).split("\n");
+
+ for (var completion of completions) {
+ if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) {
+ ret += list + ":1:32\n";
+ ret += completion;
+ }
+ }
+ });
+ }
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
new file mode 100644
index 0000000000..4f518dabe0
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+var scriptItem = "untouched";
+
+function checkLoads() {
+ var title = document.getElementById("title");
+ title.textContent = window.parent.shouldLoad ?
+ "The following should be hidden:" :
+ "The following should not be hidden:";
+
+ if (window.parent.shouldLoad) {
+ window.parent.is(scriptItem, "loaded malware javascript!", "Should load bad javascript");
+ } else {
+ window.parent.is(scriptItem, "untouched", "Should not load bad javascript");
+ }
+
+ var elt = document.getElementById("styleImport");
+ var style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "visible", "Should load clean css");
+
+ // Make sure the css did not load.
+ elt = document.getElementById("styleCheck");
+ style = document.defaultView.getComputedStyle(elt);
+ if (window.parent.shouldLoad) {
+ window.parent.isnot(style.visibility, "visible", "Should load bad css");
+ } else {
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+ }
+
+ elt = document.getElementById("styleBad");
+ style = document.defaultView.getComputedStyle(elt);
+ if (window.parent.shouldLoad) {
+ window.parent.isnot(style.visibility, "visible", "Should import bad css");
+ } else {
+ window.parent.isnot(style.visibility, "hidden", "Should not import bad css");
+ }
+}
+
+</script>
+
+<!-- Try loading from a malware javascript URI -->
+<script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
+
+<!-- Try loading from an uwanted software css URI -->
+<link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+<!-- Try loading a marked-as-malware css through an @import from a clean URI -->
+<link rel="stylesheet" type="text/css" href="import.css"></link>
+</head>
+
+<body onload="checkLoads()">
+<div id="title"></div>
+<div id="styleCheck">STYLE EVIL</div>
+<div id="styleBad">STYLE BAD</div>
+<div id="styleImport">STYLE IMPORT</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/good.js b/toolkit/components/url-classifier/tests/mochitest/good.js
new file mode 100644
index 0000000000..a3c8fcb0c8
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/good.js
@@ -0,0 +1,3 @@
+/* global scriptItem:true */
+
+scriptItem = "loaded whitelisted javascript!";
diff --git a/toolkit/components/url-classifier/tests/mochitest/head.js b/toolkit/components/url-classifier/tests/mochitest/head.js
new file mode 100644
index 0000000000..715ac1a785
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/head.js
@@ -0,0 +1,48 @@
+// calculate the fullhash and send it to gethash server
+function addCompletionToServer(list, url, mochitestUrl) {
+ return new Promise(function (resolve, reject) {
+ var listParam = "list=" + list;
+ var fullhashParam = "fullhash=" + hash(url);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("PUT", mochitestUrl + "?" + listParam + "&" + fullhashParam, true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function () {
+ if (this.readyState == this.DONE) {
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+function hash(str) {
+ function bytesFromString(str1) {
+ var converter = SpecialPowers.Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(SpecialPowers.Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+ return converter.convertToByteArray(str1);
+ }
+
+ var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"].createInstance(
+ SpecialPowers.Ci.nsICryptoHash
+ );
+
+ var data = bytesFromString(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(true);
+}
+
+var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({ set: p });
+
+function whenDelayedStartupFinished(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ Services.obs.removeObserver(observer, aTopic);
+ setTimeout(aCallback, 0);
+ }
+ }, "browser-delayed-startup-finished");
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/import.css b/toolkit/components/url-classifier/tests/mochitest/import.css
new file mode 100644
index 0000000000..9b86c82169
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/import.css
@@ -0,0 +1,3 @@
+/* malware.example.com is in the malware database. */
+@import url("http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/bad.css");
+#styleImport { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/import2.css b/toolkit/components/url-classifier/tests/mochitest/import2.css
new file mode 100644
index 0000000000..55de698e0c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/import2.css
@@ -0,0 +1,3 @@
+/* malware.mochi.test is in the malware database. */
+@import url("http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/bad.css");
+#styleImport { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/mochitest.ini b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
new file mode 100644
index 0000000000..dea8d2752e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
@@ -0,0 +1,101 @@
+[DEFAULT]
+tags = condprof
+support-files =
+ classifierCommon.js
+ classifierFrame.html
+ classifierHelper.js
+ cleanWorker.js
+ good.js
+ head.js
+ evil.css
+ evil.css^headers^
+ evil.js
+ evil.js^headers^
+ evilWorker.js
+ import.css
+ import2.css
+ raptor.jpg
+ track.html
+ trackerFrame.html
+ trackerFrame.sjs
+ trackingRequest.js
+ trackingRequest.js^headers^
+ unwantedWorker.js
+ vp9.webm
+ whitelistFrame.html
+ workerFrame.html
+ ping.sjs
+ basic.vtt
+ basic.vtt^headers^
+ dnt.html
+ dnt.sjs
+ update.sjs
+ bad.css
+ bad.css^headers^
+ gethash.sjs
+ gethashFrame.html
+ seek.webm
+ cache.sjs
+ features.js
+ sw_register.html
+ sw_unregister.html
+ sw_worker.js
+ sandboxed.html
+ sandboxed.html^headers^
+
+[test_classifier.html]
+skip-if =
+ http3
+[test_classifier_match.html]
+skip-if =
+ http3
+[test_classifier_worker.html]
+skip-if =
+ http3
+[test_classify_by_default.html]
+skip-if =
+ http3
+[test_classify_ping.html]
+skip-if =
+ (verify && debug && (os == 'win' || os == 'mac'))
+ http3
+[test_classify_top_sandboxed.html]
+skip-if =
+ http3
+[test_classify_track.html]
+skip-if =
+ http3
+[test_gethash.html]
+skip-if =
+ http3
+[test_bug1254766.html]
+skip-if =
+ http3
+[test_cachemiss.html]
+skip-if =
+ verify
+ http3
+[test_annotation_vs_TP.html]
+skip-if =
+ http3
+[test_fingerprinting.html]
+skip-if =
+ http3
+[test_fingerprinting_annotate.html]
+skip-if =
+ http3
+[test_cryptomining.html]
+skip-if =
+ http3
+[test_cryptomining_annotate.html]
+skip-if =
+ http3
+[test_emailtracking.html]
+skip-if =
+ http3
+[test_socialtracking.html]
+skip-if =
+ http3
+[test_socialtracking_annotate.html]
+skip-if =
+ http3
diff --git a/toolkit/components/url-classifier/tests/mochitest/ping.sjs b/toolkit/components/url-classifier/tests/mochitest/ping.sjs
new file mode 100644
index 0000000000..09a5f17356
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/ping.sjs
@@ -0,0 +1,15 @@
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var [name, value] = val.split("=");
+ query[name] = unescape(value);
+ });
+
+ if (request.method == "POST") {
+ setState(query.id, "ping");
+ } else {
+ var value = getState(query.id);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(value);
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/raptor.jpg b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg
new file mode 100644
index 0000000000..243ba9e2d4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs
new file mode 100644
index 0000000000..563abae5cb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs
@@ -0,0 +1,7 @@
+const gURL =
+ "http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+
+function handleRequest(request, response) {
+ response.setStatusLine("1.1", 302, "found");
+ response.setHeader("Location", gURL, false);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/report.sjs b/toolkit/components/url-classifier/tests/mochitest/report.sjs
new file mode 100644
index 0000000000..c6b461f86d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/report.sjs
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const SJS = "report.sjs?";
+const REDIRECT =
+ "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/" +
+ SJS;
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function createBlockedIframePage() {
+ return `<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title></title>
+ </head>
+ <body>
+ <iframe id="phishingFrame" ></iframe>
+ </body>
+ </html>`;
+}
+
+function createPage() {
+ return `<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>Hello World</title>
+ </head>
+ <body>
+ <script></script>
+ </body>
+ </html>`;
+}
+
+function handleRequest(request, response) {
+ var params = new URLSearchParams(request.queryString);
+ var action = params.get("action");
+
+ if (action === "create-blocked-iframe") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createBlockedIframePage());
+ return;
+ }
+
+ if (action === "redirect") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createPage());
+ return;
+ }
+
+ if (action === "reporturl") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createPage());
+ return;
+ }
+
+ if (action === "create-blocked-redirect") {
+ params.delete("action");
+ params.append("action", "redirect");
+ response.setStatusLine("1.1", 302, "found");
+ response.setHeader(
+ "Location",
+ "https://" + REDIRECT + params.toString(),
+ false
+ );
+ return;
+ }
+
+ response.write("I don't know action " + action);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html
new file mode 100644
index 0000000000..3eb8870edd
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<body>
+<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js">
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^
new file mode 100644
index 0000000000..4705ce9ded
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts;
diff --git a/toolkit/components/url-classifier/tests/mochitest/seek.webm b/toolkit/components/url-classifier/tests/mochitest/seek.webm
new file mode 100644
index 0000000000..72b0297233
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/seek.webm
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_register.html b/toolkit/components/url-classifier/tests/mochitest/sw_register.html
new file mode 100644
index 0000000000..2a536128fa
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_register.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<script>
+function waitForState(worker, state, context) {
+ return new Promise(resolve => {
+ function onStateChange() {
+ if (worker.state === state) {
+ worker.removeEventListener("statechange", onStateChange);
+ resolve(context);
+ }
+ }
+
+ // First add an event listener, so we won't miss any change that happens
+ // before we check the current state.
+ worker.addEventListener("statechange", onStateChange);
+
+ // Now check if the worker is already in the desired state.
+ onStateChange();
+ });
+}
+
+(async function () {
+ dump("[Dimi]register sw...\n");
+ let reg = await navigator.serviceWorker.register("sw_worker.js", {scope: "."});
+ await waitForState(reg.installing, "activated", reg);
+ window.parent.postMessage({status: "registrationdone"}, "*");
+})();
+
+</script>
+</head>
+<html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html
new file mode 100644
index 0000000000..b94fa0baac
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.opener.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_worker.js b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js
new file mode 100644
index 0000000000..bad16608b4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js
@@ -0,0 +1,10 @@
+self.addEventListener("fetch", function (event) {
+ let sep = "synth.html?";
+ let idx = event.request.url.indexOf(sep);
+ if (idx > 0) {
+ let url = event.request.url.substring(idx + sep.length);
+ event.respondWith(fetch(url, { credentials: "include" }));
+ } else {
+ event.respondWith(fetch(event.request));
+ }
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
new file mode 100644
index 0000000000..bf00da6d4e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test advisory link (Bug #1366384)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+var testDatas = [
+ { url: "itisaphishingsite.org/phishing.html",
+ list: "mochi1-phish-simple",
+ provider: "google",
+ },
+
+ { url: "fakeitisaphishingsite.org/phishing.html",
+ list: "fake1-phish-simple",
+ provider: "mozilla",
+ },
+
+ { url: "phishing.example.com/test.html",
+ list: "mochi2-phish-simple",
+ provider: "google4",
+ },
+];
+
+let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+function addUrlToDB(list, url) {
+ let testData = [{ db: list, url}];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setupTestData(data) {
+ let promises = [];
+ let providerList = "browser.safebrowsing.provider." + data.provider + ".lists";
+ promises.push(pushPrefs([providerList, data.list]));
+
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+
+ promises.push(addUrlToDB(data.list, data.url));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win);
+
+ let browser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURIString(browser, aTestData.url);
+ await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
+
+ let doc = win.gBrowser.contentDocument;
+
+ // This test works on a document which uses Fluent.
+ // Fluent localization may finish later than DOMContentLoaded,
+ // so let's wait for `document.l10n.ready` promise to resolve.
+ await doc.l10n.ready;
+ let advisoryEl = doc.getElementById("advisory_provider");
+ if (aTestData.provider != "google" && aTestData.provider != "google4") {
+ ok(!advisoryEl, "Advisory should not be shown");
+ } else {
+ ok(advisoryEl, "Advisory element should exist");
+ let expectedUrl =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider." +
+ aTestData.provider +
+ ".advisoryURL");
+ is(advisoryEl.href, expectedUrl, "Correct advisory url");
+ let expectedText =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider." +
+ aTestData.provider +
+ ".advisoryName");
+ is(advisoryEl.text, expectedText, "correct advisory text");
+ }
+
+ win.close();
+ resolve();
+ })();
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.phishing.enabled", true]]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await testOnWindow(testData);
+ await classifierHelper._cleanup();
+ }
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html
new file mode 100644
index 0000000000..2d5385d23b
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+
+// Add https://allowlisted.example.com to the permissions manager
+SpecialPowers.addPermission("trackingprotection",
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ { url: "https://allowlisted.example.com" });
+
+async function clearPermissions() {
+ await SpecialPowers.removePermission("trackingprotection",
+ { url: "https://allowlisted.example.com" });
+ ok(!await SpecialPowers.testPermission("trackingprotection",
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ { url: "https://allowlisted.example.com" }));
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"],
+ ["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ["channelclassifier.allowlist_example", true],
+ ["dom.security.skip_remote_script_assertion_in_system_priv_context", true]]},
+ test);
+
+function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ UrlClassifierTestUtils.addTestTrackers().then(() => {
+ document.getElementById("testFrame").src = "allowlistAnnotatedFrame.html";
+ });
+}
+
+// Expected finish() call is in "allowlistedAnnotatedFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html
new file mode 100644
index 0000000000..ad8e98005b
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs TP</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING,
+ [
+ ["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.annotate_channels", true],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ],
+ true /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
new file mode 100644
index 0000000000..d6a1cb0d5f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
@@ -0,0 +1,259 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "mochi-malware-simple";
+const MALWARE_HOST1 = "malware.example.com/";
+const MALWARE_HOST2 = "test1.example.com/";
+
+const UNWANTED_LIST = "mochi-unwanted-simple";
+const UNWANTED_HOST1 = "unwanted.example.com/";
+const UNWANTED_HOST2 = "test2.example.com/";
+
+
+const UNUSED_MALWARE_HOST = "unused.malware.com/";
+const UNUSED_UNWANTED_HOST = "unused.unwanted.com/";
+
+const GETHASH_URL =
+ "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs";
+
+var gPreGethashCounter = 0;
+var gCurGethashCounter = 0;
+
+var expectLoad = false;
+
+function loadTestFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "gethashFrame.html");
+ document.body.appendChild(iframe);
+
+ iframe.onload = function() {
+ document.body.removeChild(iframe);
+ resolve();
+ };
+ }).then(getGethashCounter);
+}
+
+function getGethashCounter() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("PUT", GETHASH_URL + "?gethashcount");
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ gPreGethashCounter = gCurGethashCounter;
+ gCurGethashCounter = parseInt(xhr.response);
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+// setup function allows classifier send gethash request for test database
+// also it calculate to fullhash for url and store those hashes in gethash sjs.
+async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ return Promise.all([
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST1, GETHASH_URL),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST2, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST1, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST2, GETHASH_URL),
+ ]);
+}
+
+// Reset function in helper try to simulate the behavior we restart firefox
+function reset() {
+ return classifierHelper.resetDatabase()
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function updateUnusedUrl() {
+ var testData = [
+ { url: UNUSED_MALWARE_HOST, db: MALWARE_LIST },
+ { url: UNUSED_UNWANTED_HOST, db: UNWANTED_LIST },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function addPrefixToDB() {
+ return update(true);
+}
+
+function addCompletionToDB() {
+ return update(false);
+}
+
+function update(prefix = false) {
+ var length = prefix ? 4 : 32;
+ var testData = [
+ { url: MALWARE_HOST1, db: MALWARE_LIST, len: length },
+ { url: MALWARE_HOST2, db: MALWARE_LIST, len: length },
+ { url: UNWANTED_HOST1, db: UNWANTED_LIST, len: length },
+ { url: UNWANTED_HOST2, db: UNWANTED_LIST, len: length },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+// This testcase is to make sure gethash works:
+// 1. Add prefixes to DB.
+// 2. Load test frame contains malware & unwanted url, those urls should be blocked.
+// 3. The second step should also trigger a gethash request since completions is not in
+// either cache or DB.
+// 4. Load test frame again, since completions is stored in cache now, no gethash
+// request should be triggered.
+function testGethash() {
+ return Promise.resolve()
+ .then(addPrefixToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcae is to make sure completions in update works:
+// 1. Add completions to DB.
+// 2. Load test frame, since completions is stored in DB, gethash request should
+// not be triggered.
+function testUpdate() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure an update request will not clear completions in DB:
+// 1. Add completions to DB.
+// 2. Load test frame to make sure completions is stored in database, in this case, gethash
+// should not be triggered.
+// 3. Trigger an update, cache is cleared, but completions in DB should still remain.
+// 4. Load test frame again, since completions is in DB, gethash request should not be triggered.
+function testUpdateNotClearCompletions() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(updateUnusedUrl)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure completion store in DB will properly load after restarting.
+// 1. Add completions to DB.
+// 2. Simulate firefox restart by calling reloadDatabase.
+// 3. Load test frame, since completions should be loaded from DB, no gethash request should
+// be triggered.
+function testUpdateCompletionsAfterReload() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(classifierHelper.reloadDatabase)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure cache will be cleared after restarting
+// 1. Add prefixes to DB.
+// 2. Load test frame, this should trigger a gethash request and completions will be stored in
+// cache.
+// 3. Load test frame again, no gethash should be triggered because of cache.
+// 4. Simulate firefox restart by calling reloadDatabase.
+// 5. Load test frame again, since cache is cleared, gethash request should be triggered.
+function testGethashCompletionsAfterReload() {
+ return Promise.resolve()
+ .then(addPrefixToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(classifierHelper.reloadDatabase)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(reset);
+}
+
+function runTest() {
+ Promise.resolve()
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(testGethash)
+ .then(testUpdate)
+ .then(testUpdateNotClearCompletions)
+ .then(testUpdateCompletionsAfterReload)
+ .then(testGethashCompletionsAfterReload)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "mochi-malware-simple,mochi-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
new file mode 100644
index 0000000000..2af0285884
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "test-malware-simple";
+const MALWARE_HOST = "malware.example.com/";
+
+const UNWANTED_LIST = "test-unwanted-simple";
+const UNWANTED_HOST = "unwanted.example.com/";
+
+const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/cache.sjs";
+
+var shouldLoad = false;
+
+var gPreGethashCounter = 0;
+var gCurGethashCounter = 0;
+
+function loadTestFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "gethashFrame.html");
+ document.body.appendChild(iframe);
+
+ iframe.onload = function() {
+ document.body.removeChild(iframe);
+ resolve();
+ };
+ }).then(getGethashCounter);
+}
+
+function getGethashCounter() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("PUT", GETHASH_URL + "?gethashcount");
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ gPreGethashCounter = gCurGethashCounter;
+ gCurGethashCounter = parseInt(xhr.response);
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+// add 4-bytes prefixes to local database, so when we access the url,
+// it will trigger gethash request.
+function addPrefixToDB(list, url) {
+ var testData = [{ db: list, url, len: 4 }];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+// manually reset DB to make sure next test won't be affected by cache.
+function reset() {
+ return classifierHelper.resetDatabase();
+}
+
+// This test has to come before testPositiveCache to ensure gethash server doesn't
+// contain completions.
+function testNegativeCache() {
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+ shouldLoad = true;
+
+ async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ // Only add prefix to database. not server, so gethash will not return
+ // result.
+ return Promise.all([
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ ]);
+ }
+
+ return Promise.resolve()
+ .then(setup)
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ // Second load should not trigger gethash request because cache.
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered.");
+})
+ .then(reset);
+}
+
+function testPositiveCache() {
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+ shouldLoad = false;
+
+ async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ return Promise.all([
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL),
+ ]);
+ }
+
+ return Promise.resolve()
+ .then(setup)
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ // Second load should not trigger gethash request because cache.
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered.");
+})
+ .then(reset);
+}
+
+function runTest() {
+ Promise.resolve()
+ // This test resources get blocked when gethash returns successfully
+ .then(classifierHelper.waitForInit)
+ .then(testNegativeCache)
+ .then(testPositiveCache)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html
new file mode 100644
index 0000000000..73724f2241
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html";
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow() {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("OnLoadComplete", function innerLoad2(e) {
+ win.content.removeEventListener("OnLoadComplete", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ checkLoads(win, JSON.parse(e.detail));
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var badids = [
+ "badscript1",
+ "badscript2",
+ "badimage1",
+ "badimage2",
+ "badframe1",
+ "badframe2",
+ "badmedia1",
+ "badcss",
+];
+
+function checkLoads(aWindow, aData) {
+ var win = aWindow.content;
+
+ is(aData.scriptItem1, "untouched", "Should not load tracking javascript");
+ is(aData.scriptItem2, "untouched", "Should not load tracking javascript (2)");
+
+ is(aData.imageItem1, "untouched", "Should not load tracking images");
+ is(aData.imageItem2, "untouched", "Should not load tracking images (2)");
+
+ is(aData.frameItem1, "untouched", "Should not load tracking iframes");
+ is(aData.frameItem2, "untouched", "Should not load tracking iframes (2)");
+ is(aData.mediaItem1, "error", "Should not load tracking videos");
+ is(aData.xhrItem, "failed", "Should not load tracking XHRs");
+ is(aData.fetchItem, "error", "Should not fetch from tracking URLs");
+
+ var elt = win.document.getElementById("styleCheck");
+ var style = win.document.defaultView.getComputedStyle(elt);
+ isnot(style.visibility, "hidden", "Should not load tracking css");
+
+ is(win.document.blockedNodeByClassifierCount, badids.length,
+ "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ ok(allNodeMatch, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ if (!nodeMatch) {
+ console.log(badids[j] + " was not found in blockedNodes");
+ }
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ ok(allNodeMatch, "All tracking nodes are expected to be annotated as such");
+
+ // End (parent) test.
+}
+
+function cleanup() {
+ SpecialPowers.clearUserPref("privacy.trackingprotection.enabled");
+ SpecialPowers.clearUserPref("channelclassifier.allowlist_example");
+ SpecialPowers.clearUserPref("privacy.trackingprotection.testing.report_blocked_node");
+
+ UrlClassifierTestUtils.cleanupTestTrackers();
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"]]},
+ test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(cleanup);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true);
+ // Make sure chrome:// URIs are processed. This does not white-list
+ // any URIs unless 'https://allowlisted.example.com' is added in the
+ // permission manager (see test_allowlisted_annotations.html)
+ await SpecialPowers.setBoolPref("channelclassifier.allowlist_example", true);
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.testing.report_blocked_node", true);
+
+ await testOnWindow().then(function(aWindow) {
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+// Expected finish() call is in "classifiedAnnotatedFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
new file mode 100644
index 0000000000..787b70798a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+var firstLoad = true;
+
+// Add some URLs to the malware database.
+var testData = [
+ { url: "malware.mochi.test/",
+ db: "test-malware-simple",
+ },
+ { url: "unwanted.mochi.test/",
+ db: "test-unwanted-simple",
+ },
+ { url: "blocked.mochi.test/",
+ db: "test-block-simple",
+ },
+ { url: "harmful.mochi.test/",
+ db: "test-harmful-simple",
+ },
+ { url: "phish.mochi.test/firefox/its-a-trap.html",
+ db: "test-phish-simple",
+ },
+];
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const Cr = SpecialPowers.Cr;
+
+var testURLs = [
+ { url: "http://mochi.test",
+ table: "",
+ result: Cr.NS_OK,
+ },
+ { url: "http://malware.mochi.test",
+ table: "test-malware-simple",
+ result: Cr.NS_ERROR_MALWARE_URI,
+ },
+ { url: "http://unwanted.mochi.test",
+ table: "test-unwanted-simple",
+ result: Cr.NS_ERROR_UNWANTED_URI,
+ },
+ { url: "http://phish.mochi.test/firefox/its-a-trap.html",
+ table: "test-phish-simple",
+ result: Cr.NS_ERROR_PHISHING_URI,
+ },
+ { url: "http://harmful.mochi.test",
+ table: "test-harmful-simple",
+ result: Cr.NS_ERROR_HARMFUL_URI,
+ },
+ { url: "http://blocked.mochi.test",
+ table: "test-block-simple",
+ result: Cr.NS_ERROR_BLOCKED_URI,
+ },
+];
+
+function loadTestFrame() {
+ document.getElementById("testFrame").src = "classifierFrame.html";
+}
+
+// Expected finish() call is in "classifierFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+function updateSuccess() {
+ return SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]});
+}
+
+function updateError(errorCode) {
+ ok(false, "Couldn't update classifier. Error code: " + errorCode);
+ // Abort test.
+ SimpleTest.finish();
+}
+
+function testService() {
+ return new Promise(resolve => {
+ function runNextTest() {
+ if (!testURLs.length) {
+ resolve();
+ return;
+ }
+ let test = testURLs.shift();
+ let tables = "test-malware-simple,test-unwanted-simple,test-phish-simple,test-block-simple,test-harmful-simple";
+ let uri = SpecialPowers.Services.io.newURI(test.url);
+ let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {});
+ SpecialPowers.doUrlClassify(prin, function(errorCode) {
+ is(errorCode, test.result,
+ `Successful asynchronous classification of ${test.url}`);
+ SpecialPowers.doUrlClassifyLocal(uri, tables, function(results) {
+ if (!results.length) {
+ is(test.table, "",
+ `Successful asynchronous local classification of ${test.url}`);
+ } else {
+ let result = results[0].QueryInterface(Ci.nsIUrlClassifierFeatureResult);
+ is(result.list, test.table,
+ `Successful asynchronous local classification of ${test.url}`);
+ }
+ runNextTest();
+ });
+ });
+ }
+ runNextTest(resolve);
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple,test-harmful-simple"],
+ ["urlclassifier.blockedTable", "test-block-simple"],
+ ["urlclassifier.phishTable", "test-phish-simple"],
+ ["browser.safebrowsing.phishing.enabled", true],
+ ["browser.safebrowsing.malware.enabled", true],
+ ["browser.safebrowsing.blockedURIs.enabled", true],
+ ["browser.safebrowsing.debug", true]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => classifierHelper.addUrlToDB(testData))
+ .then(updateSuccess)
+ .catch(err => {
+ updateError(err);
+ })
+ .then(testService)
+ .then(loadTestFrame);
+ });
+
+</script>
+
+</pre>
+<iframe id="testFrame" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html
new file mode 100644
index 0000000000..dac019fe59
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html
@@ -0,0 +1,155 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1281083 - Changing the urlclassifier.*Table prefs doesn't take effect before the next browser restart.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html";
+
+const testTable = "moz-track-digest256";
+const UPDATE_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/update.sjs";
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+
+// If default preference contain the table we want to test,
+// We should change test table to a different one.
+var trackingTables = SpecialPowers.getCharPref("urlclassifier.trackingTable").split(",");
+ok(!trackingTables.includes(testTable), "test table should not be in the preference");
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+is(listmanager.getGethashUrl(testTable), "",
+ "gethash url for test table should be empty before setting to preference");
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+}
+
+function testOnWindow() {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2(e) {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+function setup() {
+ return new Promise(function(resolve, reject) {
+ // gethash url of test table "moz-track-digest256" should be updated
+ // after setting preference.
+ var url = listmanager.getGethashUrl(testTable);
+ var expected = SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.gethashURL");
+
+ is(url, expected, testTable + " matches its gethash url");
+
+ // Trigger update
+ listmanager.disableUpdate(testTable);
+ listmanager.enableUpdate(testTable);
+ listmanager.maybeToggleUpdateChecking();
+
+ // We wait until "nextupdattime" was set as a signal that update is complete.
+ waitForUpdateSuccess(function() {
+ resolve();
+ });
+ });
+}
+
+function waitForUpdateSuccess(callback) {
+ let nextupdatetime =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.nextupdatetime");
+
+ if (nextupdatetime !== "1") {
+ callback();
+ return;
+ }
+
+ timer.initWithCallback(function() {
+ waitForUpdateSuccess(callback);
+ }, 10, Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+async function runTest() {
+ // To make sure url is not blocked by an already blocked url.
+ // Here we use non-tracking.example.com as a tracked url.
+ // Since this table is only used in this bug, so it won't affect other testcases.
+ await addCompletionToServer(testTable, "bug1281083.example.com/", UPDATE_URL);
+
+ /**
+ * In this test we try to modify only urlclassifier.*Table preference to see if
+ * url specified in the table will be blocked after update.
+ */
+ await SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", testTable]]});
+
+ await setup();
+
+ await testOnWindow().then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+// Set nextupdatetime to 1 to trigger an update
+SpecialPowers.pushPrefEnv(
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["channelclassifier.allowlist_example", true],
+ ["browser.safebrowsing.provider.mozilla.nextupdatetime", "1"],
+ ["browser.safebrowsing.provider.mozilla.lists", testTable],
+ ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL]]},
+ runTest);
+
+// Expected finish() call is in "bug_1281083.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html
new file mode 100644
index 0000000000..df3ac79d8a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Bug 1395411 - Changing the urlclassifier.*Table prefs doesn't remove them from the update checker.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const testTableV2 = "mochi1-phish-simple";
+const testTableV4 = "mochi1-phish-proto";
+const UPDATE_URL_V2 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV2";
+const UPDATE_URL_V4 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV4";
+
+let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.phishing.enabled", true],
+ ["browser.safebrowsing.provider.mozilla.lists", testTableV2],
+ ["browser.safebrowsing.provider.mozilla4.lists", testTableV4],
+ ["browser.safebrowsing.provider.mozilla4.updateURL", UPDATE_URL_V4],
+ ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL_V2]]},
+ runTest);
+
+function runTest() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV2 + "," + testTableV4]);
+ is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV2]);
+ is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV4]);
+ is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", ""]);
+ is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2");
+
+ await classifierHelper._cleanup();
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html
new file mode 100644
index 0000000000..84fe4547ed
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html
@@ -0,0 +1,187 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier Matched Info (bug 1288633) </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="application/javascript">
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+var Cr = SpecialPowers.Cr;
+
+var inputDatas = [
+ { url: "malware.mochi.test/",
+ db: "mochi-block-simple",
+ },
+ { url: "malware1.mochi.test/",
+ db: "mochi1-block-simple",
+ },
+ { url: "malware1.mochi.test/",
+ db: "mochi1-malware-simple",
+ provider: "mozilla",
+ },
+ { url: "malware2.mochi.test/",
+ db: "mochi2-unwanted-simple",
+ provider: "mozilla",
+ },
+ { url: "malware2.mochi.test/",
+ db: "mochi2-malware-simple",
+ provider: "mozilla",
+ },
+ { url: "malware3.mochi.test/",
+ db: "mochig3-malware-simple",
+ provider: "google",
+ },
+ { url: "malware3.mochi.test/",
+ db: "mochim3-malware-simple",
+ provider: "mozilla",
+ },
+];
+
+function hash(str) {
+ function bytesFromString(str1) {
+ let converter =
+ Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+ return converter.convertToByteArray(str1);
+ }
+
+ let hasher = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+
+ let data = bytesFromString(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(false);
+}
+
+var testDatas = [
+ // Match empty provider
+ { url: "http://malware.mochi.test",
+ expect: { error: Cr.NS_ERROR_BLOCKED_URI,
+ table: "mochi-block-simple",
+ provider: "",
+ fullhash: (function() {
+ return hash("malware.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, only one has valid provider
+ { url: "http://malware1.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochi1-malware-simple",
+ provider: "mozilla",
+ fullhash: (function() {
+ return hash("malware1.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, handle order
+ { url: "http://malware2.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochi2-malware-simple",
+ provider: "mozilla",
+ fullhash: (function() {
+ return hash("malware2.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, handle order
+ { url: "http://malware3.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochig3-malware-simple",
+ provider: "google",
+ fullhash: (function() {
+ return hash("malware3.mochi.test/");
+ })(),
+ },
+ },
+
+];
+
+SimpleTest.waitForExplicitFinish();
+
+function setupTestData(datas) {
+ let prefValues = {};
+ for (let data of datas) {
+ if (!data.provider) {
+ continue;
+ }
+ let providerPref = "browser.safebrowsing.provider." + data.provider + ".lists";
+ let prefValue;
+ if (!prefValues[providerPref]) {
+ prefValue = data.db;
+ } else {
+ prefValue = prefValues[providerPref] + "," + data.db;
+ }
+ prefValues[providerPref] = prefValue;
+ }
+
+ // Convert map to array
+ let prefArray = [];
+ for (var pref in prefValues) {
+ prefArray.push([pref, prefValues[pref]]);
+ }
+
+ let activeTablePref = "urlclassifier.malwareTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ for (let data of datas) {
+ activeTable += "," + data.db;
+ }
+ prefArray.push([activeTablePref, activeTable]);
+
+ return SpecialPowers.pushPrefEnv({set: prefArray});
+}
+
+function runTest() {
+ return new Promise(resolve => {
+ function runNextTest() {
+ if (!testDatas.length) {
+ resolve();
+ return;
+ }
+ let test = testDatas.shift();
+ let uri = SpecialPowers.Services.io.newURI(test.url);
+ let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {});
+ SpecialPowers.doUrlClassify(prin, function(errorCode, table, provider, fullhash) {
+ is(errorCode, test.expect.error, `Test url ${test.url} correct error`);
+ is(table, test.expect.table, `Test url ${test.url} correct table`);
+ is(provider, test.expect.provider, `Test url ${test.url} correct provider`);
+ is(fullhash, btoa(test.expect.fullhash), `Test url ${test.url} correct full hash`);
+ runNextTest();
+ });
+ }
+ runNextTest();
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => setupTestData(inputDatas))
+ .then(() => classifierHelper.addUrlToDB(inputDatas))
+ .then(runTest)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some tests failed with error " + e);
+ SimpleTest.finish();
+ });
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html
new file mode 100644
index 0000000000..0e984c2a64
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+// Add some URLs to the malware database.
+var testData = [
+ { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/evilWorker.js",
+ db: "test-malware-simple",
+ },
+ { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js",
+ db: "test-unwanted-simple",
+ },
+];
+
+function loadTestFrame() {
+ document.getElementById("testFrame").src =
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/workerFrame.html";
+}
+
+function onmessage(event) {
+ var pieces = event.data.split(":");
+ if (pieces[0] == "finish") {
+ SimpleTest.finish();
+ return;
+ }
+
+ is(pieces[0], "success", pieces[1]);
+}
+
+function updateSuccess() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]},
+ loadTestFrame);
+}
+
+function updateError(errorCode) {
+ ok(false, "Couldn't update classifier. Error code: " + errorCode);
+ // Abort test.
+ SimpleTest.finish();
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => classifierHelper.addUrlToDB(testData))
+ .then(updateSuccess)
+ .catch(err => {
+ updateError(err);
+ });
+ });
+
+window.addEventListener("message", onmessage);
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html
new file mode 100644
index 0000000000..c10a0c62f9
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1442496</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1442496">Mozilla Bug 1442496</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script type="text/javascript" src="classifierHelper.js"></script>
+<script class="testbody" type="text/javascript">
+
+// To add a request to test, add the request in trackerFrame.html
+// and the id of query string "?id=xxx" here.
+const trackersAll = [
+ "img-src",
+ "object-data",
+ "script-src",
+ "iframe-src",
+ "link-rel-stylesheet",
+ "video-src",
+ "track-src",
+ "ping",
+ "fetch",
+ "xmlhttprequest",
+ "send-beacon",
+ "fetch-in-sw",
+];
+
+const TRACKER_DOMAIN = "itisatracker.org";
+const TEST_TOP_DOMAIN = "example.com";
+
+const TEST_TOP_PAGE = "trackerFrame.html";
+const TRACKER_SERVER = "trackerFrame.sjs";
+
+const TEST_PATH = "/tests/toolkit/components/url-classifier/tests/mochitest/";
+
+const TEST_TOP_SITE = "https:///" + TEST_TOP_DOMAIN + TEST_PATH;
+const TRACKER_SITE = "https://" + TRACKER_DOMAIN + TEST_PATH;
+const TRACKER_SJS = "https://" + TRACKER_DOMAIN + TEST_PATH + TRACKER_SERVER;
+
+// This function ask the server to set the cookie
+async function setupAndRun(hasCookie, topLevelSite = TEST_TOP_SITE) {
+ // In order to apply the correct cookieBehavior, we need to first open a new
+ // window to setup cookies. So, the new window will use the correct
+ // cookieBehavior. Otherwise, it will use the default cookieBehavior.
+ let setup_win = window.open();
+ await setup_win.fetch(TRACKER_SJS + "?init=" + trackersAll.length, {
+ credentials: "include",
+ });
+ setup_win.close();
+
+ return new Promise(resolve => {
+ let win;
+ let query = hasCookie ? "with-cookie" : "without-cookie";
+ fetch(TRACKER_SJS + "?callback=" + query).then(r => {
+ r.text().then((body) => {
+ let trackers_found = body.split(",");
+ for (let tracker of trackersAll) {
+ let description = "Tracker request " + tracker + "received " +
+ (hasCookie ? "with" : "without") + " cookie";
+ ok(trackers_found.includes(tracker), description);
+ }
+ win.close();
+ resolve();
+ });
+ });
+
+ win = window.open(topLevelSite + TEST_TOP_PAGE);
+ });
+}
+
+async function cleanup(topLevelSite = TEST_TOP_SITE) {
+ function clearServiceWorker() {
+ return new Promise(resolve => {
+ let w;
+ window.onmessage = function(e) {
+ if (e.data.status == "unregistrationdone") {
+ w.close();
+ resolve();
+ }
+ }
+ w = window.open(TEST_TOP_SITE + "sw_unregister.html");
+ });
+ }
+
+ // Ensure we clear the stylesheet cache so that we re-classify.
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+
+ await clearServiceWorker();
+}
+
+async function runTests() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["urlclassifier.trackingAnnotationTable.testEntries", TRACKER_DOMAIN],
+ ["urlclassifier.trackingAnnotationWhitelistTable", "test-trackwhite-simple"],
+ ["network.cookie.cookieBehavior", 4],
+ ["privacy.trackingprotection.enabled", false ],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["browser.send_pings", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]});
+
+ await classifierHelper.waitForInit();
+
+ let hasCookie;
+
+ info("Test all requests should be sent with cookies when ETP is off");
+ hasCookie = true;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent without cookies when ETP is on");
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "privacy.trackingprotection.annotate_channels", true],
+ ]});
+
+ hasCookie = false;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent with cookies when top-level is in the whitelist");
+ await classifierHelper.addUrlToDB([{
+ url: TEST_TOP_DOMAIN + "/?resource=" + TRACKER_DOMAIN,
+ db: "test-trackwhite-simple",
+ }]);
+
+ hasCookie = true;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent with cookies when the tracker is a first-party");
+ hasCookie = true;
+ await setupAndRun(hasCookie, TRACKER_SITE);
+ await cleanup(TRACKER_SITE);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
new file mode 100644
index 0000000000..6a8a189ed2
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1233914 - ping doesn't honor the TP list here.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Delay to make sure ping is made prior than XHR");
+
+ const timeout = 200;
+ const host_nottrack = "http://not-tracking.example.com/";
+ const host_track = "http://trackertest.org/";
+ const path_ping = "tests/toolkit/components/url-classifier/tests/mochitest/ping.sjs";
+ const TP_ENABLE_PREF = "privacy.trackingprotection.enabled";
+ const RETRY_TIMEOUT_MS = 200;
+
+ async function testPingNonBlocklist() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+
+ var msg = "ping should reach page not in blacklist";
+ var expectPing = true;
+ var id = "1111";
+ ping(id, host_nottrack);
+
+ return new Promise(function(resolve, reject) {
+ // Retry at most 30 seconds.
+ isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
+ });
+ }
+
+ async function testPingBlocklistSafebrowsingOff() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, false);
+
+ var msg = "ping should reach page in blacklist when tracking protection is off";
+ var expectPing = true;
+ var id = "2222";
+ ping(id, host_track);
+
+ return new Promise(function(resolve, reject) {
+ // Retry at most 30 seconds.
+ isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
+ });
+ }
+
+ async function testPingBlocklistSafebrowsingOn() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+
+ var msg = "ping should not reach page in blacklist when tracking protection is on";
+ var expectPing = false;
+ var id = "3333";
+ ping(id, host_track);
+
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ isPinged(id, expectPing, msg, resolve);
+ }, timeout);
+ });
+ }
+
+ function ping(id, host) {
+ var elm = document.createElement("a");
+ elm.setAttribute("ping", host + path_ping + "?id=" + id);
+ elm.setAttribute("href", "#");
+ document.body.appendChild(elm);
+
+ // Trigger ping.
+ elm.click();
+
+ document.body.removeChild(elm);
+ }
+
+ function isPingedWithRetry(id, expected, msg, callback, retryCnt) {
+ var url = "http://mochi.test:8888/" + path_ping;
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url + "?id=" + id);
+ xhr.onload = function() {
+ let pinged = xhr.response === "ping";
+ let success = pinged === expected;
+ if (success || 0 === retryCnt) {
+ is(expected, pinged, msg);
+ callback();
+ return;
+ }
+ // Retry on failure.
+ setTimeout(() => {
+ isPingedWithRetry(id, expected, msg, callback, retryCnt - 1);
+ }, RETRY_TIMEOUT_MS);
+ };
+ xhr.send();
+ }
+
+ function isPinged(id, expected, msg, callback) {
+ isPingedWithRetry(id, expected, msg, callback, 0);
+ }
+
+ function cleanup() {
+ SpecialPowers.clearUserPref(TP_ENABLE_PREF);
+ }
+
+ function runTest() {
+ classifierHelper.waitForInit()
+ .then(testPingNonBlocklist)
+ .then(testPingBlocklistSafebrowsingOff)
+ .then(testPingBlocklistSafebrowsingOn)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(cleanup);
+ SpecialPowers.pushPrefEnv({"set": [
+ ["browser.send_pings", true],
+ ]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html
new file mode 100644
index 0000000000..7e3ae97751
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1647681</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+
+async function runTests() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["privacy.trackingprotection.annotate_channels", true],
+ ]});
+
+ var chromeScript;
+ chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel);
+ if (
+ !channel ||
+ !classifiedChannel ||
+ !channel.URI.spec.startsWith(
+ "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"
+ )
+ ) {
+ return;
+ }
+
+ sendAsyncMessage("last-channel-flags", {
+ classified: Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING == classifiedChannel.classificationFlags,
+ });
+ }
+
+ addMessageListener("done", __ => {
+ Services.obs.removeObserver(onExamResp, "http-on-examine-response");
+ });
+
+ Services.obs.addObserver(onExamResp, "http-on-examine-response");
+
+ sendAsyncMessage("start-test");
+ });
+
+ chromeScript.addMessageListener(
+ "start-test",
+ async _ => {
+ window.open("https://example.org/tests/toolkit/components/url-classifier/tests/mochitest/sandboxed.html")
+ },
+ { once: true }
+ );
+
+ chromeScript.addMessageListener(
+ "last-channel-flags",
+ data => {
+ ok(data.classified, "tracker script should be classified when the top-level is sandboxed");
+ chromeScript.sendAsyncMessage("done");
+ SimpleTest.finish();
+ },
+ { once: true }
+ );
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html
new file mode 100644
index 0000000000..38f1c8a04b
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1262406 - Track element doesn't use the URL classifier.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+ const PREF = "browser.safebrowsing.malware.enabled";
+ const track_path = "tests/toolkit/components/url-classifier/tests/mochitest/basic.vtt";
+ const malware_url = "http://malware.example.com/" + track_path;
+ const validtrack_url = "http://mochi.test:8888/" + track_path;
+
+ var video = document.createElement("video");
+ video.src = "seek.webm";
+ video.crossOrigin = "anonymous";
+
+ document.body.appendChild(video);
+
+ function testValidTrack() {
+ SpecialPowers.setBoolPref(PREF, true);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ track.src = validtrack_url;
+ video.appendChild(track);
+
+ function onload() {
+ ok(true, "Track should be loaded when url is not in blocklist");
+ finish();
+ }
+
+ function onerror() {
+ ok(false, "Error while loading track");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function testBlocklistTrackSafebrowsingOff() {
+ SpecialPowers.setBoolPref(PREF, false);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ track.src = malware_url;
+ video.appendChild(track);
+
+ function onload() {
+ ok(true, "Track should be loaded when url is in blocklist and safebrowsing is off");
+ finish();
+ }
+
+ function onerror() {
+ ok(false, "Error while loading track");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function testBlocklistTrackSafebrowsingOn() {
+ SpecialPowers.setBoolPref(PREF, true);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ // Add a query string parameter here to avoid url classifier bypass classify
+ // because of cache.
+ track.src = malware_url + "?testsbon";
+ video.appendChild(track);
+
+ function onload() {
+ ok(false, "Unexpected result while loading track in blocklist");
+ finish();
+ }
+
+ function onerror() {
+ ok(true, "Track should not be loaded when url is in blocklist and safebrowsing is on");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function cleanup() {
+ SpecialPowers.clearUserPref(PREF);
+ }
+
+ function setup() {
+ var testData = [
+ { url: "malware.example.com/",
+ db: "test-malware-simple",
+ },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+ }
+
+ function runTest() {
+ Promise.resolve()
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(testValidTrack)
+ .then(testBlocklistTrackSafebrowsingOff)
+ .then(testBlocklistTrackSafebrowsingOn)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(cleanup);
+ SpecialPowers.pushPrefEnv({"set": [
+ ["media.webvtt.regions.enabled", true],
+ ["urlclassifier.malwareTable", "test-malware-simple"],
+ ]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
new file mode 100644
index 0000000000..8cfa53a401
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the cryptomining classifier</title>
+ <script 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">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist 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,mochi.xorigin-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.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\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(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html
new file mode 100644
index 0000000000..ecf5cd02a4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - cryptomining</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_CRYPTOMINING,
+ [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["privacy.trackingprotection.cryptomining.enabled", true],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.socialtracking.enabled", false],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ false /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
new file mode 100644
index 0000000000..9f75b48d39
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1258033 - Fix the DNT loophole for tracking protection</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+const tests = [
+ // DNT turned on and TP turned off, DNT signal sent in both private browsing
+ // and normal mode.
+ {
+ setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: false},
+ expected: {dnt: "1"},
+ },
+ // DNT turned off and TP turned on globally, DNT signal sent in both private
+ // browsing and normal mode.
+ {
+ setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: false},
+ expected: {dnt: "1"},
+ },
+ // DNT turned off and TP in Private Browsing only, DNT signal sent in private
+ // browsing mode only.
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: false},
+ expected: {dnt: "unspecified"},
+ },
+ // DNT turned off and TP turned off, DNT signal is never sent.
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: true},
+ expected: {dnt: "unspecified"},
+ },
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: false},
+ expected: {dnt: "unspecified"},
+ },
+];
+
+const DNT_PREF = "privacy.donottrackheader.enabled";
+const TP_PREF = "privacy.trackingprotection.enabled";
+const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
+
+const contentPage =
+ "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/dnt.html";
+
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function executeTest(test) {
+ SpecialPowers.pushPrefEnv({"set": [
+ [DNT_PREF, test.setting.dntPref],
+ [TP_PREF, test.setting.tpPref],
+ [TP_PB_PREF, test.setting.tppbPref],
+ ]});
+
+ var win = mainWindow.OpenBrowserWindow({private: test.setting.pbMode});
+
+ return new Promise(function(resolve, reject) {
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("message", function(event) {
+ let [key, value] = event.data.split("=");
+ if (key == "finish") {
+ win.close();
+ resolve();
+ } else if (key == "navigator.doNotTrack") {
+ is(value, test.expected.dnt, "navigator.doNotTrack should be " + test.expected.dnt);
+ } else if (key == "DNT") {
+ let msg = test.expected.dnt == "1" ? "" : "not ";
+ is(value, test.expected.dnt, "DNT header should " + msg + "be sent");
+ } else {
+ ok(false, "unexpected message");
+ }
+ });
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+let loop = function loop(index) {
+ if (index >= tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ let test = tests[index];
+ let next = function next() {
+ loop(index + 1);
+ };
+ let result = executeTest(test);
+ result.then(next, next);
+};
+
+SimpleTest.waitForExplicitFinish();
+loop(0);
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html
new file mode 100644
index 0000000000..25c534ea9e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the email tracking classifier</title>
+ <script 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">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.emailtracking.blocklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.emailtracking.allowlistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.emailtracking.blocklistTables", prefValue(test.config[0], "mochitest5-track-simple") ],
+ [ "urlclassifier.features.emailtracking.allowlistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", false ],
+ [ "privacy.trackingprotection.emailtracking.enabled", true ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string 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, "Image loading happened correctly");
+
+ // Let's load an image with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://email-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, "Image loading happened correctly (by table)");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "Script loading happened correctly");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://email-tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "Script loading happened correctly (by table)");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
new file mode 100644
index 0000000000..422278bc7c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the fingerprinting classifier</title>
+ <script 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">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], imgLoadExpected: true, scriptLoadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], imgLoadExpected: true, scriptLoadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], imgLoadExpected: true, scriptLoadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], imgLoadExpected: true, scriptLoadExpected: true },
+];
+
+function prefValue(value, what) {
+ return 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,mochi.xorigin-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.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", true ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string 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.imgLoadExpected, "Image loading happened correctly");
+
+ // Let's load an image with a random query string 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.imgLoadExpected, "Image loading happened correctly (by table)");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.scriptLoadExpected, "Script loading happened correctly");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.scriptLoadExpected, "Script loading happened correctly");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html
new file mode 100644
index 0000000000..0bc01645b1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - fingerprinting</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_FINGERPRINTING,
+ [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["privacy.trackingprotection.fingerprinting.enabled", true],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.socialtracking.enabled", false],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ true /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_gethash.html b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
new file mode 100644
index 0000000000..b9d3a2fd44
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<iframe id="testFrame1" onload=""></iframe>
+<iframe id="testFrame2" onload=""></iframe>
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "test-malware-simple";
+const MALWARE_HOST = "malware.example.com/";
+
+const UNWANTED_LIST = "test-unwanted-simple";
+const UNWANTED_HOST = "unwanted.example.com/";
+
+const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs";
+const NOTEXIST_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/nonexistserver.sjs";
+
+var shouldLoad = false;
+
+// In this testcase we store prefixes to localdb and send the fullhash to gethash server.
+// When access the test page gecko should trigger gethash request to server and
+// get the completion response.
+function loadTestFrame(id) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.getElementById(id);
+ iframe.setAttribute("src", "gethashFrame.html");
+
+ iframe.onload = function() {
+ resolve();
+ };
+ });
+}
+
+// add 4-bytes prefixes to local database, so when we access the url,
+// it will trigger gethash request.
+function addPrefixToDB(list, url) {
+ var testData = [{ db: list, url, len: 4 }];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setup404() {
+ shouldLoad = true;
+
+ return Promise.all([
+ classifierHelper.allowCompletion(
+ [MALWARE_LIST, UNWANTED_LIST], NOTEXIST_URL),
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ ]);
+}
+
+function setup() {
+ return Promise.all([
+ classifierHelper.allowCompletion(
+ [MALWARE_LIST, UNWANTED_LIST], GETHASH_URL),
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL),
+ ]);
+}
+
+// manually reset DB to make sure next test won't be affected by cache.
+function reset() {
+ return classifierHelper.resetDatabase();
+}
+
+function runTest() {
+ Promise.resolve()
+ // This test resources get blocked when gethash returns successfully
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(() => loadTestFrame("testFrame1"))
+ .then(reset)
+ // This test resources are not blocked when gethash returns an error
+ .then(setup404)
+ .then(() => loadTestFrame("testFrame2"))
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
new file mode 100644
index 0000000000..5dca8eb7b9
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "https://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow(aPrivate) {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow({private: aPrivate});
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var badids = [
+ "badscript",
+ "badimage",
+ "badcss",
+];
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+ is(win.document.getElementById("badimage").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking images");
+ is(win.document.getElementById("goodscript").dataset.touched, "yes", "Should load entitylisted tracking javascript");
+ is(win.document.getElementById("goodimage").dataset.touched, "yes", "Should load non-blocklisted image");
+
+ var elt = win.document.getElementById("styleCheck");
+ var style = win.document.defaultView.getComputedStyle(elt);
+ isnot(style.visibility, aBlocked ? "hidden" : "", "Should not load tracking css");
+
+ is(win.document.blockedNodeByClassifierCount, aBlocked ? badids.length : 0, "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, aBlocked, "All tracking nodes are expected to be annotated as such");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.pbmode.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ]}, test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ // Normal mode, with the pref (trackers should be loaded)
+ await testOnWindow(false).then(function(aWindow) {
+ checkLoads(aWindow, false);
+ aWindow.close();
+ });
+
+ // Private Browsing, with the pref (trackers should be blocked)
+ await testOnWindow(true).then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ // Private Browsing, without the pref (trackers should be loaded)
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.pbmode.enabled", false);
+ await testOnWindow(true).then(function(aWindow) {
+ checkLoads(aWindow, false);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
new file mode 100644
index 0000000000..3b426a95cd
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
@@ -0,0 +1,213 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test report matched URL info (Bug #1288633)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+;
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+var mainWindow = window.browsingContext.topChromeWindow;
+const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs";
+const BASE_URL = "http://" + SJS + "?";
+
+var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+function addUrlToDB(list, url) {
+ let testData = [{ db: list, url}];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setupTestData(data) {
+ let promises = [];
+ let providerList = "browser.safebrowsing.provider." + data.provider + ".lists";
+ if (!Services.prefs.prefHasUserValue(providerList)) {
+ promises.push(pushPrefs([providerList, data.list]));
+ } else {
+ let pref = SpecialPowers.getCharPref(providerList);
+ pref += "," + data.list;
+ promises.push(pushPrefs([providerList, pref]));
+ }
+
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+
+ promises.push(addUrlToDB(data.list, data.testUrl));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData, aCallback, aTestCreater) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win);
+
+ let browser = win.gBrowser.selectedBrowser;
+ aTestCreater(win, browser, aTestData.topUrl, aTestData.testUrl);
+
+ let notification = await BrowserTestUtils.waitForNotificationBar(win.gBrowser, browser, "blocked-badware-page");
+ ok(notification, "Notification box should be displayed");
+
+ let buttons = notification.buttonContainer.getElementsByTagName("button");
+ let button = buttons[1];
+ if (aTestData.provider != "google" && aTestData.provider != "google4") {
+ is(button, undefined, "Report button should not be showed");
+ win.close();
+ resolve();
+ return;
+ }
+
+ button.click();
+
+ let newTabBrowser = win.gBrowser.selectedTab.linkedBrowser;
+ await BrowserTestUtils.browserLoaded(newTabBrowser);
+
+ aCallback(newTabBrowser);
+ win.close();
+ resolve();
+ })();
+ });
+}
+
+var createBlockedIframe = function(aWindow, aBrowser, aTopUrl, aUrl) {
+ (async function() {
+ BrowserTestUtils.loadURIString(aBrowser, aTopUrl);
+ await BrowserTestUtils.browserLoaded(aBrowser);
+
+ await SpecialPowers.spawn(aBrowser, [aUrl], async function(url) {
+ return new Promise(resolve => {
+ let listener = e => {
+ docShell.chromeEventHandler.removeEventListener("AboutBlockedLoaded", listener, false, true);
+ resolve();
+ };
+ docShell.chromeEventHandler.addEventListener("AboutBlockedLoaded", listener, false, true);
+ let frame = content.document.getElementById("phishingFrame");
+ frame.setAttribute("src", "http://" + url);
+ });
+ });
+
+ let doc = aWindow.gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument;
+ let ignoreWarningLink = doc.getElementById("ignore_warning_link");
+ ok(ignoreWarningLink, "Ignore warning link should exist");
+ ignoreWarningLink.click();
+ })();
+};
+
+var createBlockedPage = function(aWindow, aBrowser, aTopUrl, aUrl) {
+ (async function() {
+ BrowserTestUtils.loadURIString(aBrowser, aTopUrl);
+ await BrowserTestUtils.waitForContentEvent(aBrowser, "DOMContentLoaded");
+
+ let doc = aWindow.gBrowser.contentDocument;
+ let ignoreWarningLink = doc.getElementById("ignore_warning_link");
+ ok(ignoreWarningLink, "Ignore warning link should exist");
+ ignoreWarningLink.click();
+ })();
+};
+
+function checkReportURL(aReportBrowser, aUrl) {
+ let expectedReportUrl = BASE_URL + "action=reporturl&reporturl=" + encodeURIComponent(aUrl);
+ is(aReportBrowser.contentDocument.location.href, expectedReportUrl, "Correct report URL");
+}
+
+var testDatas = [
+ { topUrl: "http://itisaphishingsite.org/phishing.html",
+ testUrl: "itisaphishingsite.org/phishing.html",
+ list: "mochi1-phish-simple",
+ provider: "google",
+ blockCreater: createBlockedPage,
+ expectedReportUri: "http://itisaphishingsite.org/phishing.html",
+ },
+
+ // Non-google provider, no report button is showed.
+ // Test provider needs a valid update URL (mozilla for example) otherwise
+ // the updates inserting the test data will fail.
+ { topUrl: "http://fakeitisaphishingsite.org/phishing.html",
+ testUrl: "fakeitisaphishingsite.org/phishing.html",
+ list: "fake-phish-simple",
+ provider: "mozilla",
+ blockCreater: createBlockedPage,
+ },
+
+ // Iframe case:
+ // A top level page at
+ // http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe
+ // contains an iframe to http://phishing.example.com/test.html (blocked).
+
+ { topUrl: "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe",
+ testUrl: "phishing.example.com/test.html",
+ list: "mochi2-phish-simple",
+ provider: "google4",
+ blockCreater: createBlockedIframe,
+ expectedReportUri: "http://phishing.example.com/test.html",
+ },
+
+ // Redirect case:
+ // A top level page at
+ // http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect (blocked)
+ // will get redirected to
+ // https://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect.
+ { topUrl: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect",
+ testUrl: "prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect",
+ list: "mochi3-phish-simple",
+ provider: "google4",
+ blockCreater: createBlockedPage,
+ expectedReportUri: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs",
+ },
+
+];
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.provider.google.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="],
+ ["browser.safebrowsing.provider.google4.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="],
+ ["browser.safebrowsing.phishing.enabled", true]]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await testOnWindow(testData, function(browser) {
+ checkReportURL(browser, testData.expectedReportUri);
+ }, testData.blockCreater);
+
+ await classifierHelper._cleanup();
+ }
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html
new file mode 100644
index 0000000000..475145591d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Only tables with provider could register gethash url in listmanager.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+// List all the tables
+const prefs = [
+ "urlclassifier.phishTable",
+ "urlclassifier.malwareTable",
+ "urlclassifier.downloadBlockTable",
+ "urlclassifier.downloadAllowTable",
+ "urlclassifier.trackingTable",
+ "urlclassifier.trackingWhitelistTable",
+ "urlclassifier.blockedTable",
+];
+
+// Get providers
+var providers = {};
+
+var branch = SpecialPowers.Services.prefs.getBranch("browser.safebrowsing.provider.");
+var children = branch.getChildList("");
+
+for (var child of children) {
+ var prefComponents = child.split(".");
+ var providerName = prefComponents[0];
+ providers[providerName] = {};
+}
+
+// Get lists from |browser.safebrowsing.provider.PROVIDER_NAME.lists| preference.
+var listsWithProvider = [];
+var listsToProvider = [];
+for (let provider in providers) {
+ let pref = "browser.safebrowsing.provider." + provider + ".lists";
+ let list = SpecialPowers.getCharPref(pref).split(",");
+
+ listsToProvider = listsToProvider.concat(list.map( () => { return provider; }));
+ listsWithProvider = listsWithProvider.concat(list);
+}
+
+// Get all the lists
+var lists = [];
+for (let pref of prefs) {
+ lists = lists.concat(SpecialPowers.getCharPref(pref).split(","));
+}
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+let googleKey = SpecialPowers.Services.urlFormatter.formatURL("%GOOGLE_SAFEBROWSING_API_KEY%").trim();
+
+for (let list of lists) {
+ if (!list)
+ continue;
+
+ // For lists having a provider, it should have a correct gethash url
+ // For lists without a provider, for example, moztest-malware-simple, it should not
+ // have a gethash url.
+ var url = listmanager.getGethashUrl(list);
+ var index = listsWithProvider.indexOf(list);
+ if (index >= 0) {
+ let provider = listsToProvider[index];
+ let pref = "browser.safebrowsing.provider." + provider + ".gethashURL";
+ if ((provider == "google" || provider == "google4") &&
+ (!googleKey || googleKey == "no-google-safebrowsing-api-key")) {
+ is(url, "", "getHash url of " + list + " should be empty");
+ } else {
+ is(url, SpecialPowers.getCharPref(pref), list + " matches its gethash url");
+ }
+ } else {
+ is(url, "", list + " should not have a gethash url");
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html
new file mode 100644
index 0000000000..d90e860ad2
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the socialtracking classifier</title>
+ <script 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">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.socialtracking.blacklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.socialtracking.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.socialtracking.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
+ [ "urlclassifier.features.socialtracking.whitelistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", false ],
+ [ "privacy.trackingprotection.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", true ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\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(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = 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.sendAsyncMessage("unloadTrackers");
+ });
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html
new file mode 100644
index 0000000000..f12dcf11ec
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - socialtracking</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_SOCIALTRACKING,
+ [
+ ["privacy.socialtracking.block_cookies.enabled", false],
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["privacy.trackingprotection.socialtracking.enabled", true],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ false /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
new file mode 100644
index 0000000000..758add242a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -0,0 +1,241 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test threathit repoty </title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/threathit.sjs";
+
+function hash(str) {
+ function bytesFromString(str1) {
+ let converter =
+ Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+ return converter.convertToByteArray(str1);
+ }
+
+ let hasher = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+
+ let data = bytesFromString(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(false);
+}
+
+var testDatas = [
+ { url: "itisaphishingsite1.org/phishing.html",
+ list: "test-phish-proto",
+ provider: "test",
+ // The base64 of binary protobuf representation of response:
+ //
+ // [
+ // {
+ // 'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
+ // 'response_type': 2, // FULL_UPDATE
+ // 'new_client_state': 'sta\x0te', // NEW_CLIENT_STATE
+ // 'additions': { 'compression_type': RAW,
+ // 'prefix_size': 1,
+ // 'raw_hashes': "xxxx"} // hash prefix of url itisaphishingsite.org/phishing.html
+ // 'minimumWaitDuration': "8.1s",
+ // }
+ // ]
+ //
+ updateProtobuf: "ChoIAiACKgwIARIICAQSBM9UdYs6BnN0YQB0ZRIECAwQCg==",
+ // The base64 of binary protobuf representation of response:
+ // {
+ // "matches": [
+ // {
+ // "threat_type": 2, // SOCIAL_ENGINEERING_PUBLIC
+ // "threat": {
+ // "hash": string,
+ // },
+ // "cacheDuration": "8.1",
+ // }
+ // ],
+ // "minimumWaitDuration": 12.0..1,
+ // "negativeCacheDuration": 12.0..1,
+ // }
+ fullhashProtobuf: "CiwIAhoiCiDPVHWLptJSc/UYiabk1/wo5OkJqbggiylVKISK28bfeSoECAwQChIECAwQChoECAwQCg==",
+ },
+];
+
+function addDataV4ToServer(list, type, data) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ let params = new URLSearchParams();
+ params.append("action", "store");
+ params.append("list", list);
+ params.append("type", type);
+ params.append("data", data);
+
+ xhr.open("PUT", "http://" + SJS + "?" + params.toString(), true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+/**
+ * Grabs the results via XHR
+ */
+function checkResults(aTestdata, aExpected) {
+ let xhr = new XMLHttpRequest();
+ xhr.responseType = "text";
+ xhr.onload = function() {
+ is(aExpected, xhr.response, "Correct report request");
+ SimpleTest.finish();
+ };
+ xhr.onerror = function() {
+ ok(false, "Can't get results from server.");
+ SimpleTest.finish();
+ };
+ let params = new URLSearchParams();
+ params.append("action", "getreport");
+ params.append("list", aTestdata.list);
+ let url = "http://" + SJS + "?" + params.toString();
+
+ xhr.open("GET", url, true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.send();
+}
+
+function waitForUpdate(data) {
+ listmanager.checkForUpdates(data.updateUrl);
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ Services.obs.removeObserver(observer, aTopic);
+ resolve();
+ }, "safebrowsing-update-finished");
+ });
+}
+
+function addUpdateDataV4ToServer(list, data) {
+ return addDataV4ToServer(list, "update", data);
+}
+
+function addFullhashV4DataToServer(list, data) {
+ return addDataV4ToServer(list, "fullhash", data);
+}
+
+function setupTestData(data) {
+ let updateParams = new URLSearchParams();
+ updateParams.append("action", "get");
+ updateParams.append("list", data.list);
+ updateParams.append("type", "update");
+ data.updateUrl = "http://" + SJS + "?" + updateParams.toString();
+
+ let gethashParams = new URLSearchParams();
+ gethashParams.append("action", "get");
+ gethashParams.append("list", data.list);
+ gethashParams.append("type", "fullhash");
+ data.gethashUrl = "http://" + SJS + "?" + gethashParams.toString();
+
+ listmanager.registerTable(data.list,
+ data.provider,
+ data.updateUrl,
+ data.gethashUrl);
+
+ let promises = [];
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+
+ let reportPref = "browser.safebrowsing.provider." + data.provider + ".dataSharingURL";
+ let reportParams = new URLSearchParams();
+ reportParams.append("action", "report");
+ reportParams.append("list", data.list);
+ data.reportUrl = "http://" + SJS + "?" + reportParams.toString();
+
+ let reportEnabledPref = "browser.safebrowsing.provider." + data.provider + ".dataSharing.enabled";
+
+ promises.push(pushPrefs([reportPref, data.reportUrl]));
+ promises.push(pushPrefs([reportEnabledPref, true]));
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+ promises.push(addUpdateDataV4ToServer(data.list, data.updateProtobuf));
+ promises.push(addFullhashV4DataToServer(data.list, data.fullhashProtobuf));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await new Promise(rs => whenDelayedStartupFinished(win, rs));
+
+ let expected;
+ let browser = win.gBrowser.selectedBrowser;
+ let progressListener = {
+ onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
+ expected = aTestData.reportUrl;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
+ };
+ win.gBrowser.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING);
+
+ BrowserTestUtils.loadURIString(browser, aTestData.url);
+ await BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ `http://${aTestData.url}`,
+ true
+ );
+ checkResults(aTestData, expected);
+ win.close();
+ resolve();
+ })();
+ });
+}
+SpecialPowers.pushPrefEnv(
+ {"set": [
+ ["browser.safebrowsing.phishing.enabled", true],
+ ["dom.testing.sync-content-blocking-notifications", true],
+ ]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await waitForUpdate(testData);
+ await testOnWindow(testData);
+ await classifierHelper._cleanup();
+ }
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
new file mode 100644
index 0000000000..3cb17a9869
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection with and without Safe Browsing (Bug #1157081)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow(aCallback) {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+}
+
+var badids = [
+ "badscript",
+];
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"],
+ ["privacy.trackingprotection.enabled", true],
+ ["browser.safebrowsing.malware.enabled", false],
+ ["browser.safebrowsing.phishing.enabled", false],
+ ["channelclassifier.allowlist_example", true]]},
+ test);
+
+function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ UrlClassifierTestUtils.addTestTrackers().then(() => {
+ // Safe Browsing turned OFF, tracking protection should work nevertheless
+ testOnWindow(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+
+ // Safe Browsing turned ON, tracking protection should still work
+ SpecialPowers.setBoolPref("browser.safebrowsing.phishing.enabled", true);
+ testOnWindow(function(aWindow1) {
+ checkLoads(aWindow1, true);
+ aWindow1.close();
+ SimpleTest.finish();
+ });
+ });
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
new file mode 100644
index 0000000000..2c5c1e928c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Bug 1312515</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+
+<p><b>To see more of what is happening: <code>export MOZ_LOG=nsChannelClassifier:3</code></b></p>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow(aPrivate, aCallback) {
+ var win = mainWindow.OpenBrowserWindow({private: aPrivate});
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.loadURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() { BrowserTestUtils.loadURIString(win.gBrowser, contentPage); });
+ });
+ }, {capture: true, once: true});
+}
+
+const topic = "http-on-before-connect";
+var testUrl;
+var testWindow;
+var resolve;
+
+function checkLowestPriority(aSubject) {
+ checkPriority(aSubject, checkLowestPriority, Ci.nsISupportsPriority.PRIORITY_LOWEST, "Priority should be lowest.");
+}
+
+function checkNormalPriority(aSubject) {
+ checkPriority(aSubject, checkNormalPriority, Ci.nsISupportsPriority.PRIORITY_NORMAL, "Priority should be normal.");
+}
+
+function checkPriority(aSubject, aCallback, aPriority, aMessage) {
+ var channel = aSubject.QueryInterface(Ci.nsIChannel);
+ info("Channel classified: " + channel.name);
+ if (channel.name !== testUrl) {
+ return;
+ }
+
+ SpecialPowers.removeObserver(aCallback, topic);
+
+ var p = aSubject.QueryInterface(Ci.nsISupportsPriority);
+ is(p.priority, aPriority, aMessage);
+
+ info("Resolving promise for " + channel.name);
+ resolve();
+}
+
+function testXHR1() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://tracking.example.com/";
+ info("Not blocklisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkNormalPriority, topic);
+ testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*");
+ });
+}
+
+function testXHR2() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://trackertest.org/";
+ info("Blocklisted and not entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkLowestPriority, topic);
+ testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*");
+ });
+}
+
+function testFetch1() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://itisatracker.org/"; // only entitylisted in TP, not for annotations
+ info("Blocklisted and not entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkLowestPriority, topic);
+ testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*");
+ });
+}
+
+function testFetch2() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://tracking.example.org/"; // only entitylisted for annotations, not in TP
+ info("Blocklisted but also entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkNormalPriority, topic);
+ testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*");
+ });
+}
+
+function endTest() {
+ info("Finishing up...");
+ testWindow.close();
+ testWindow = null;
+ SimpleTest.finish();
+}
+
+async function test() {
+ await SpecialPowers.pushPrefEnv(
+ {"set": [["network.http.tailing.enabled", false],
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", true],
+ ["privacy.trackingprotection.lower_network_priority", true],
+ ["dom.security.https_first", false]]});
+ await UrlClassifierTestUtils.addTestTrackers();
+ testOnWindow(false, async function(aWindow) {
+ testWindow = aWindow;
+ await testXHR1();
+ await testXHR2();
+ await testFetch1();
+ await testFetch2();
+ await endTest();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.registerCleanupFunction(function() {
+ info("Cleaning up prefs...");
+ UrlClassifierTestUtils.cleanupTestTrackers();
+});
+test();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html
new file mode 100644
index 0000000000..f333876328
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.window.browsingContext.topChromeWindow;
+var contentPage1 = "http://www.example.com/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html";
+
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow(contentPage) {
+ return new Promise((resolve, reject) => {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.loadURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ BrowserTestUtils.loadURIString(win.gBrowser, contentPage);
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var testData = [
+ { url: "apps.fbsbx.com/",
+ db: "test-track-simple",
+ },
+ { url: "www.example.com/?resource=apps.fbsbx.com",
+ db: "test-trackwhite-simple",
+ },
+];
+
+function checkLoads(aWindow, aWhitelisted) {
+ var win = aWindow.content;
+
+ is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript");
+}
+
+SpecialPowers.pushPrefEnv(
+ // Disable STS preloadlist because apps.fbsbx.com is in the list.
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["urlclassifier.trackingTable", "test-track-simple"],
+ ["urlclassifier.trackingWhitelistTable", "test-trackwhite-simple"],
+ ["dom.security.https_first", false],
+ ["network.stricttransportsecurity.preloadlist", false]]},
+ test);
+
+async function test() {
+ await classifierHelper.waitForInit();
+ await classifierHelper.addUrlToDB(testData);
+
+ // Load the test from a URL on the whitelist
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
new file mode 100644
index 0000000000..e43cd47707
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
@@ -0,0 +1,159 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage1 = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
+var contentPage2 = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
+const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
+
+function testOnWindow(contentPage) {
+ return new Promise((resolve, reject) => {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.loadURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ BrowserTestUtils.loadURIString(win.gBrowser, contentPage);
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var alwaysbadids = [
+ "badscript",
+];
+
+function checkLoads(aWindow, aWhitelisted, tpEnabled) {
+ var win = aWindow.content;
+ if (!tpEnabled) {
+ is(win.document.getElementById("badscript").dataset.touched, "yes", "Should load tracking javascript");
+ is(win.document.blockedNodeByClassifierCount, 0, "Should not identify any tracking elements");
+ return;
+ }
+
+ is(win.document.getElementById("badscript").dataset.touched, "no", "Should not load tracking javascript");
+ is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript");
+
+ var badids = alwaysbadids.slice();
+ if (!aWhitelisted) {
+ badids.push("goodscript");
+ }
+ is(win.document.blockedNodeByClassifierCount, badids.length, "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All tracking nodes are expected to be annotated as such");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ["dom.security.https_first", false],
+ ["channelclassifier.allowlist_example", true]]},
+ test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ // Load the test from a URL that's NOT on the whitelist with tracking protection disabled
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", false);
+ await testOnWindow(contentPage2).then(function(aWindow) {
+ checkLoads(aWindow, false, false);
+ aWindow.close();
+ });
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true);
+
+ // Load the test from a URL that's NOT on the whitelist
+ await testOnWindow(contentPage2).then(function(aWindow) {
+ checkLoads(aWindow, false, true);
+ aWindow.close();
+ });
+
+ // Load the test from a URL on the whitelist
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, true, true);
+ aWindow.close();
+ });
+
+ // Load the test from a URL on the whitelist but without the whitelist
+ await SpecialPowers.setCharPref("urlclassifier.trackingWhitelistTable", "");
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, false, true);
+ aWindow.close();
+ });
+ await SpecialPowers.clearUserPref("urlclassifier.trackingWhitelistTable");
+ await SpecialPowers.clearUserPref("privacy.trackingprotection.enabled");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/threathit.sjs b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs
new file mode 100644
index 0000000000..378a32e0f6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ Cu.importGlobalProperties(["URLSearchParams"]);
+ let params = new URLSearchParams(request.queryString);
+ var action = params.get("action");
+
+ var responseBody;
+
+ // Store data in the server side.
+ if (action == "store") {
+ // In the server side we will store:
+ // All the full hashes or update for a given list
+ let state = params.get("list") + params.get("type");
+ let dataStr = params.get("data");
+ setState(state, dataStr);
+ } else if (action == "get") {
+ let state = params.get("list") + params.get("type");
+ responseBody = atob(getState(state));
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(responseBody, responseBody.length);
+ } else if (action == "report") {
+ let state = params.get("list") + "report";
+ let requestUrl =
+ request.scheme +
+ "://" +
+ request.host +
+ ":" +
+ request.port +
+ request.path +
+ "?" +
+ request.queryString;
+ setState(state, requestUrl);
+ } else if (action == "getreport") {
+ let state = params.get("list") + "report";
+ responseBody = getState(state);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/track.html b/toolkit/components/url-classifier/tests/mochitest/track.html
new file mode 100644
index 0000000000..8785e7c5b1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/track.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <h1>Tracking Works!</h1>
+ </body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html
new file mode 100644
index 0000000000..73409b5cda
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+</head>
+<body>
+<div id="content" style="display: none">
+
+<img src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=img-src">
+
+<!--nsObjectLoadingContent::OpenChannel-->
+<object data="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=object-data"></object>
+
+<!--ScriptLoader::StartLoad-->
+<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=script-src"></script>
+
+<!--nsDocShell::DoURILoad-->
+<iframe src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=iframe-src"></iframe>
+
+<!--Loader::LoadSheet-->
+<link rel="stylesheet" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-stylesheet" />
+
+<!--nsPrefetchNode::OpenChannel-->
+<!-- Temporarily disable this because it doesn't work in fission when the scheme is https -->
+<!--<link rel="prefetch" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-prefetch" />-->
+
+<!--HTMLMediaElement::ChannelLoader::LoadInternal-->
+<video src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=video-src">
+</video>
+
+<video src="https://mochi.test:8888/basic.vtt", crossorigin=use-credentials>
+ <track default src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=track-src" ></track>
+</video>
+
+<!--SendPing-->
+<a ping="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=ping" id="a-ping" href="#"></a>
+<script>
+ (function() {
+ document.getElementById("a-ping").click();
+ })();
+</script>
+
+<script>
+
+// FetchDriver::HttpFetch
+(function() {
+ try {
+ fetch(new Request("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch"), {
+ credentials: "include",
+ });
+ } catch (err) {
+ console.log(err);
+ }
+})();
+
+// XMLHttpRequestMainThread::CreateChannel
+(function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=xmlhttprequest");
+ xhr.withCredentials = true;
+ xhr.send();
+})();
+
+// Navigator::SendBeaconInternal
+(function() {
+ navigator.sendBeacon("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=send-beacon");
+})();
+
+</script>
+
+// Fetch inside service worker's script
+<iframe id="sw" src="https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/sw_register.html"></iframe>
+<script>
+ let iframe = document.getElementById("sw");
+ window.onmessage = function(e) {
+ if (e.data.status == "registrationdone") {
+ iframe.remove();
+ iframe = document.createElement("iframe");
+ document.getElementById("content").appendChild(iframe);
+ iframe.src = "https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/synth.html?" +
+ "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch-in-sw";
+ }
+ };
+</script>
+
+</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs
new file mode 100644
index 0000000000..6b960beefb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs
@@ -0,0 +1,82 @@
+/* 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/. */
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+const stateTotalRequests = "total-request";
+const stateCallback = "callback-response";
+const stateTrackersWithCookie = "trackers-with-cookie";
+const stateTrackersWithoutCookie = "trackers-without-cookie";
+const stateReceivedTrackers = "received-trackers";
+const stateResponseType = "response-tracker-with-cookie";
+
+function reset() {
+ setState(stateCallback, "");
+ setState(stateTrackersWithCookie, "");
+ setState(stateTrackersWithoutCookie, "");
+ setState(stateReceivedTrackers, "");
+ setState(stateResponseType, "");
+}
+
+function handleRequest(aRequest, aResponse) {
+ let params = new URLSearchParams(aRequest.queryString);
+
+ // init the server and tell the server the total number requests to process
+ // server set the cookie
+ if (params.has("init")) {
+ setState(stateTotalRequests, params.get("init"));
+
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.setHeader("Content-Type", "text/plain", false);
+
+ // Prepare the cookie
+ aResponse.setHeader("Set-Cookie", "cookie=1234; SameSite=None; Secure");
+ aResponse.setHeader(
+ "Access-Control-Allow-Origin",
+ aRequest.getHeader("Origin"),
+ false
+ );
+ aResponse.setHeader("Access-Control-Allow-Credentials", "true", false);
+ aResponse.write("begin-test");
+ // register the callback response, the response will be fired after receiving
+ // all the request
+ } else if (params.has("callback")) {
+ aResponse.processAsync();
+ aResponse.setHeader("Content-Type", "text/plain", false);
+ aResponse.setHeader(
+ "Access-Control-Allow-Origin",
+ aRequest.getHeader("Origin"),
+ false
+ );
+ aResponse.setHeader("Access-Control-Allow-Credentials", "true", false);
+
+ setState(stateResponseType, params.get("callback"));
+ setObjectState(stateCallback, aResponse);
+ } else {
+ let count = parseInt(getState(stateReceivedTrackers) || 0) + 1;
+ setState(stateReceivedTrackers, count.toString());
+
+ let state = "";
+ if (aRequest.hasHeader("Cookie")) {
+ state = stateTrackersWithCookie;
+ } else {
+ state = stateTrackersWithoutCookie;
+ }
+
+ let ids = params.get("id").concat(",", getState(state));
+ setState(state, ids);
+
+ if (getState(stateTotalRequests) == getState(stateReceivedTrackers)) {
+ getObjectState(stateCallback, r => {
+ if (getState(stateResponseType) == "with-cookie") {
+ r.write(getState(stateTrackersWithCookie));
+ } else {
+ r.write(getState(stateTrackersWithoutCookie));
+ }
+ r.finish();
+ reset();
+ });
+ }
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html
new file mode 100644
index 0000000000..ea0f92c481
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+
+</head>
+<body>
+
+<!--
+ This domain is not blocklisted for annotations but it is for tracking protection.
+ Therefore if tracking protection is accidentally enabled, this test will fail. On
+ the other hand, tracking.example.com will not be used in any of the same-origin
+ comparisons since we always look for the top window URI when there is one and
+ that's set to be www.itisatrap.org.
+-->
+<script id="badscript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js
new file mode 100644
index 0000000000..2720578eed
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js
@@ -0,0 +1,9 @@
+window.addEventListener("message", function onMessage(evt) {
+ if (evt.data.type === "doXHR") {
+ var request = new XMLHttpRequest();
+ request.open("GET", evt.data.url, true);
+ request.send(null);
+ } else if (evt.data.type === "doFetch") {
+ fetch(evt.data.url);
+ }
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^
new file mode 100644
index 0000000000..3eced96143
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: *
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js
new file mode 100644
index 0000000000..b4e8a47602
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js
@@ -0,0 +1,5 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ postMessage("loaded bad file");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/update.sjs b/toolkit/components/url-classifier/tests/mochitest/update.sjs
new file mode 100644
index 0000000000..f3984b2a6f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/update.sjs
@@ -0,0 +1,71 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ }
+
+ var body = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ var responseBody = parseV2Request(bytes);
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var table = String.fromCharCode.apply(this, bytes).slice(0, -2);
+
+ var ret = "";
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ if (list == table) {
+ var completions = getState(list).split("\n");
+ ret += "n:1000\n";
+ ret += "i:" + list + "\n";
+ ret += "a:1:32:" + 32 * (completions.length - 1) + "\n";
+
+ for (var completion of completions) {
+ ret += completion;
+ }
+ }
+ });
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/vp9.webm b/toolkit/components/url-classifier/tests/mochitest/vp9.webm
new file mode 100644
index 0000000000..221877e303
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/vp9.webm
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html
new file mode 100644
index 0000000000..620416fc74
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<script id="badscript" data-touched="not sure" src="http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<script id="goodscript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/workerFrame.html b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html
new file mode 100644
index 0000000000..69e8dd0074
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+function startCleanWorker() {
+ var worker = new Worker("cleanWorker.js");
+
+ worker.onmessage = function(event) {
+ if (event.data == "success") {
+ window.parent.postMessage("success:blocked importScripts('evilWorker.js')", "*");
+ } else {
+ window.parent.postMessage("failure:failed to block importScripts('evilWorker.js')", "*");
+ }
+ window.parent.postMessage("finish", "*");
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postmessage("failure:failed to load cleanWorker.js", "*");
+ window.parent.postMessage("finish", "*");
+ };
+
+ worker.postMessage("");
+}
+
+function startEvilWorker() {
+ var worker = new Worker("evilWorker.js");
+
+ worker.onmessage = function(event) {
+ window.parent.postMessage("failure:failed to block evilWorker.js", "*");
+ startUnwantedWorker();
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postMessage("success:blocked evilWorker.js", "*");
+ startUnwantedWorker();
+ };
+
+ worker.postMessage("");
+}
+
+function startUnwantedWorker() {
+ var worker = new Worker("unwantedWorker.js");
+
+ worker.onmessage = function(event) {
+ window.parent.postMessage("failure:failed to block unwantedWorker.js", "*");
+ startCleanWorker();
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postMessage("success:blocked unwantedWorker.js", "*");
+ startCleanWorker();
+ };
+
+ worker.postMessage("");
+}
+
+</script>
+
+</head>
+
+<body onload="startEvilWorker()">
+</body>
+</html>