summaryrefslogtreecommitdiffstats
path: root/dom/reporting/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/reporting/tests
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/reporting/tests')
-rw-r--r--dom/reporting/tests/browser.ini6
-rw-r--r--dom/reporting/tests/browser_cleanup.js276
-rw-r--r--dom/reporting/tests/common_deprecated.js214
-rw-r--r--dom/reporting/tests/delivering.sjs113
-rw-r--r--dom/reporting/tests/empty.html1
-rw-r--r--dom/reporting/tests/gtest/TestReportToParser.cpp418
-rw-r--r--dom/reporting/tests/gtest/moz.build13
-rw-r--r--dom/reporting/tests/iframe_delivering.html92
-rw-r--r--dom/reporting/tests/mochitest.ini15
-rw-r--r--dom/reporting/tests/test_delivering.html45
-rw-r--r--dom/reporting/tests/test_deprecated.html51
-rw-r--r--dom/reporting/tests/test_memoryPressure.html33
-rw-r--r--dom/reporting/tests/worker_delivering.js5
-rw-r--r--dom/reporting/tests/worker_deprecated.js28
14 files changed, 1310 insertions, 0 deletions
diff --git a/dom/reporting/tests/browser.ini b/dom/reporting/tests/browser.ini
new file mode 100644
index 0000000000..f9cf651f75
--- /dev/null
+++ b/dom/reporting/tests/browser.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ delivering.sjs
+ empty.html
+
+[browser_cleanup.js]
diff --git a/dom/reporting/tests/browser_cleanup.js b/dom/reporting/tests/browser_cleanup.js
new file mode 100644
index 0000000000..e50b8db1da
--- /dev/null
+++ b/dom/reporting/tests/browser_cleanup.js
@@ -0,0 +1,276 @@
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+const TEST_HOST = "example.org";
+const TEST_DOMAIN = "https://" + TEST_HOST;
+const TEST_PATH = "/browser/dom/reporting/tests/";
+const TEST_TOP_PAGE = TEST_DOMAIN + TEST_PATH + "empty.html";
+const TEST_SJS = TEST_DOMAIN + TEST_PATH + "delivering.sjs";
+
+async function storeReportingHeader(browser, extraParams = "") {
+ await SpecialPowers.spawn(
+ browser,
+ [{ url: TEST_SJS, extraParams }],
+ async obj => {
+ await content
+ .fetch(
+ obj.url +
+ "?task=header" +
+ (obj.extraParams.length ? "&" + obj.extraParams : "")
+ )
+ .then(r => r.text())
+ .then(text => {
+ is(text, "OK", "Report-to header sent");
+ });
+ }
+ );
+}
+
+add_task(async function () {
+ await SpecialPowers.flushPrefEnv();
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.reporting.enabled", true],
+ ["dom.reporting.header.enabled", true],
+ ["dom.reporting.testing.enabled", true],
+ ["dom.reporting.delivering.timeout", 1],
+ ["dom.reporting.cleanup.timeout", 1],
+ ["privacy.userContext.enabled", true],
+ ],
+ });
+});
+
+add_task(async function () {
+ info("Testing a total cleanup");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser);
+ ok(ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN), "We have data");
+
+ await new Promise(resolve => {
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+ resolve()
+ );
+ });
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before a full cleanup"
+ );
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function () {
+ info("Testing a total QuotaManager cleanup");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser);
+ ok(ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN), "We have data");
+
+ await new Promise(resolve => {
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_REPORTS, value =>
+ resolve()
+ );
+ });
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before a reports cleanup"
+ );
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function () {
+ info("Testing a QuotaManager host cleanup");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser);
+ ok(ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN), "We have data");
+
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromHost(
+ TEST_HOST,
+ true,
+ Ci.nsIClearDataService.CLEAR_REPORTS,
+ value => resolve()
+ );
+ });
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before a reports cleanup"
+ );
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function () {
+ info("Testing a 410 endpoint status");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser, "410=true");
+ ok(ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN), "We have data");
+
+ await SpecialPowers.spawn(browser, [], async _ => {
+ let testingInterface = new content.TestingDeprecatedInterface();
+ ok(!!testingInterface, "Created a deprecated interface");
+ });
+
+ await new Promise((resolve, reject) => {
+ let count = 0;
+ let id = setInterval(_ => {
+ ++count;
+ if (count > 10) {
+ ok(false, "Something went wrong.");
+ clearInterval(id);
+ reject();
+ }
+
+ if (!ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN)) {
+ ok(true, "No data after a 410!");
+ clearInterval(id);
+ resolve();
+ }
+ }, 1000);
+ });
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function () {
+ info("Creating a new container");
+
+ let identity = ContextualIdentityService.create(
+ "Report-To-Test",
+ "fingerprint",
+ "orange"
+ );
+
+ info("Creating a new container tab");
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE, {
+ userContextId: identity.userContextId,
+ });
+ is(
+ tab.getAttribute("usercontextid"),
+ "" + identity.userContextId,
+ "New tab has the right UCI"
+ );
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser);
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "We don't have data for the origin"
+ );
+ ok(
+ ChromeUtils.hasReportingHeaderForOrigin(
+ TEST_DOMAIN + "^userContextId=" + identity.userContextId
+ ),
+ "We have data for the origin + userContextId"
+ );
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+
+ ContextualIdentityService.remove(identity.userContextId);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(
+ TEST_DOMAIN + "^userContextId=" + identity.userContextId
+ ),
+ "No more data after a container removal"
+ );
+});
+
+add_task(async function () {
+ info("TTL cleanup");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ ok(
+ !ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "No data before the test"
+ );
+
+ await storeReportingHeader(browser);
+ ok(
+ ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN),
+ "We have data for the origin"
+ );
+
+ // Let's wait a bit.
+ await new Promise(resolve => {
+ setTimeout(resolve, 5000);
+ });
+
+ ok(!ChromeUtils.hasReportingHeaderForOrigin(TEST_DOMAIN), "No data anymore");
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function () {
+ info("Cleaning up.");
+ await new Promise(resolve => {
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+ resolve()
+ );
+ });
+});
diff --git a/dom/reporting/tests/common_deprecated.js b/dom/reporting/tests/common_deprecated.js
new file mode 100644
index 0000000000..1cee9481c2
--- /dev/null
+++ b/dom/reporting/tests/common_deprecated.js
@@ -0,0 +1,214 @@
+let testingInterface;
+
+// eslint-disable-next-line no-unused-vars
+function test_deprecatedInterface() {
+ info("Testing DeprecatedTestingInterface report");
+ return new Promise(resolve => {
+ let obs = new ReportingObserver((reports, o) => {
+ is(obs, o, "Same observer!");
+ ok(reports.length == 1, "We have 1 report");
+
+ let report = reports[0];
+ is(report.type, "deprecation", "Deprecation report received");
+ is(report.url, location.href, "URL is location");
+ ok(!!report.body, "The report has a body");
+ ok(
+ report.body instanceof DeprecationReportBody,
+ "Correct type for the body"
+ );
+ is(
+ report.body.id,
+ "DeprecatedTestingInterface",
+ "report.body.id matches DeprecatedTestingMethod"
+ );
+ ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
+ ok(
+ report.body.message.includes("TestingDeprecatedInterface"),
+ "We have a message"
+ );
+ is(
+ report.body.sourceFile,
+ location.href
+ .split("?")[0]
+ .replace("test_deprecated.html", "common_deprecated.js")
+ .replace("worker_deprecated.js", "common_deprecated.js"),
+ "We have a sourceFile"
+ );
+ is(report.body.lineNumber, 48, "We have a lineNumber");
+ is(report.body.columnNumber, 23, "We have a columnNumber");
+
+ obs.disconnect();
+ resolve();
+ });
+ ok(!!obs, "ReportingObserver is a thing");
+
+ obs.observe();
+ ok(true, "ReportingObserver.observe() is callable");
+
+ testingInterface = new TestingDeprecatedInterface();
+ ok(true, "Created a deprecated interface");
+ });
+}
+
+// eslint-disable-next-line no-unused-vars
+function test_deprecatedMethod() {
+ info("Testing DeprecatedTestingMethod report");
+ return new Promise(resolve => {
+ let obs = new ReportingObserver((reports, o) => {
+ is(obs, o, "Same observer!");
+ ok(reports.length == 1, "We have 1 report");
+
+ let report = reports[0];
+ is(report.type, "deprecation", "Deprecation report received");
+ is(report.url, location.href, "URL is location");
+ ok(!!report.body, "The report has a body");
+ ok(
+ report.body instanceof DeprecationReportBody,
+ "Correct type for the body"
+ );
+ is(
+ report.body.id,
+ "DeprecatedTestingMethod",
+ "report.body.id matches DeprecatedTestingMethod"
+ );
+ ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
+ ok(
+ report.body.message.includes(
+ "TestingDeprecatedInterface.deprecatedMethod"
+ ),
+ "We have a message"
+ );
+ is(
+ report.body.sourceFile,
+ location.href
+ .split("?")[0]
+ .replace("test_deprecated.html", "common_deprecated.js")
+ .replace("worker_deprecated.js", "common_deprecated.js"),
+ "We have a sourceFile"
+ );
+ is(report.body.lineNumber, 100, "We have a lineNumber");
+ is(report.body.columnNumber, 21, "We have a columnNumber");
+
+ obs.disconnect();
+ resolve();
+ });
+ ok(!!obs, "ReportingObserver is a thing");
+
+ obs.observe();
+ ok(true, "ReportingObserver.observe() is callable");
+
+ testingInterface.deprecatedMethod();
+ ok(true, "Run a deprecated method.");
+ });
+}
+
+// eslint-disable-next-line no-unused-vars
+function test_deprecatedMethodWithDataURI() {
+ info("Testing deprecatedMethodWithDataURI report");
+
+ const uri = `data:text/html,<script>
+ window.onload = () => {
+ let obs = new ReportingObserver((reports, o) => {
+ obs.disconnect();
+ let report = reports[0];
+ const message = (report.url == "data:...") ? "passed" : "failed";
+ window.opener.postMessage(message, "http://mochi.test:8888");
+ close();
+ });
+
+ obs.observe();
+ let testingInterface = new TestingDeprecatedInterface();
+ testingInterface.deprecatedMethod();
+ };
+ </script>`;
+
+ return new Promise((resolve, reject) => {
+ window.open(uri);
+ window.addEventListener("message", e => {
+ is(e.data, "passed", "The data URI is truncated");
+ resolve();
+ });
+ });
+}
+
+// eslint-disable-next-line no-unused-vars
+function test_deprecatedAttribute() {
+ info("Testing DeprecatedTestingAttribute report");
+ return new Promise(resolve => {
+ let obs = new ReportingObserver((reports, o) => {
+ is(obs, o, "Same observer!");
+ ok(reports.length == 1, "We have 1 report");
+
+ let report = reports[0];
+ is(report.type, "deprecation", "Deprecation report received");
+ is(report.url, location.href, "URL is location");
+ ok(!!report.body, "The report has a body");
+ ok(
+ report.body instanceof DeprecationReportBody,
+ "Correct type for the body"
+ );
+ is(
+ report.body.id,
+ "DeprecatedTestingAttribute",
+ "report.body.id matches DeprecatedTestingAttribute"
+ );
+ ok(!report.body.anticipatedRemoval, "We don't have a anticipatedRemoval");
+ ok(
+ report.body.message.includes(
+ "TestingDeprecatedInterface.deprecatedAttribute"
+ ),
+ "We have a message"
+ );
+ is(
+ report.body.sourceFile,
+ location.href
+ .split("?")[0]
+ .replace("test_deprecated.html", "common_deprecated.js")
+ .replace("worker_deprecated.js", "common_deprecated.js"),
+ "We have a sourceFile"
+ );
+ is(report.body.lineNumber, 181, "We have a lineNumber");
+ is(report.body.columnNumber, 4, "We have a columnNumber");
+
+ obs.disconnect();
+ resolve();
+ });
+ ok(!!obs, "ReportingObserver is a thing");
+
+ obs.observe();
+ ok(true, "ReportingObserver.observe() is callable");
+
+ ok(testingInterface.deprecatedAttribute, "Attributed called");
+ });
+}
+
+// eslint-disable-next-line no-unused-vars
+function test_takeRecords() {
+ info("Testing ReportingObserver.takeRecords()");
+ let p = new Promise(resolve => {
+ let obs = new ReportingObserver((reports, o) => {
+ is(obs, o, "Same observer!");
+ resolve(obs);
+ });
+ ok(!!obs, "ReportingObserver is a thing");
+
+ obs.observe();
+ ok(true, "ReportingObserver.observe() is callable");
+
+ testingInterface.deprecatedMethod();
+ ok(true, "Run a deprecated method.");
+ });
+
+ return p.then(obs => {
+ let reports = obs.takeRecords();
+ is(reports.length, 0, "No reports after an callback");
+
+ testingInterface.deprecatedAttribute + 1;
+
+ reports = obs.takeRecords();
+ ok(reports.length >= 1, "We have at least 1 report");
+
+ reports = obs.takeRecords();
+ is(reports.length, 0, "No more reports");
+ });
+}
diff --git a/dom/reporting/tests/delivering.sjs b/dom/reporting/tests/delivering.sjs
new file mode 100644
index 0000000000..9d378a9c82
--- /dev/null
+++ b/dom/reporting/tests/delivering.sjs
@@ -0,0 +1,113 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(aRequest, aResponse) {
+ var params = new URLSearchParams(aRequest.queryString);
+
+ // Report-to setter
+ if (aRequest.method == "GET" && params.get("task") == "header") {
+ let extraParams = [];
+
+ if (params.has("410")) {
+ extraParams.push("410=true");
+ }
+
+ if (params.has("worker")) {
+ extraParams.push("worker=true");
+ }
+
+ let body = {
+ max_age: 1,
+ endpoints: [
+ {
+ url:
+ "https://example.org/tests/dom/reporting/tests/delivering.sjs" +
+ (extraParams.length ? "?" + extraParams.join("&") : ""),
+ priority: 1,
+ weight: 1,
+ },
+ ],
+ };
+
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+ aResponse.setHeader("Report-to", JSON.stringify(body), false);
+ aResponse.write("OK");
+ return;
+ }
+
+ // Report check
+ if (aRequest.method == "GET" && params.get("task") == "check") {
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+
+ let reports = getState("report");
+ if (!reports) {
+ aResponse.write("");
+ return;
+ }
+
+ if (params.has("min")) {
+ let json = JSON.parse(reports);
+ if (json.length < params.get("min")) {
+ aResponse.write("");
+ return;
+ }
+ }
+
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+ aResponse.write(getState("report"));
+
+ setState("report", "");
+ return;
+ }
+
+ if (aRequest.method == "POST") {
+ var body = new BinaryInputStream(aRequest.bodyInputStream);
+
+ var avail;
+ var bytes = [];
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ let reports = getState("report");
+ if (!reports) {
+ reports = [];
+ } else {
+ reports = JSON.parse(reports);
+ }
+
+ const incoming_reports = JSON.parse(String.fromCharCode.apply(null, bytes));
+ for (let report of incoming_reports) {
+ let data = {
+ contentType: aRequest.getHeader("content-type"),
+ origin: aRequest.getHeader("origin"),
+ body: report,
+ url:
+ aRequest.scheme +
+ "://" +
+ aRequest.host +
+ aRequest.path +
+ (aRequest.queryString ? "&" + aRequest.queryString : ""),
+ };
+ reports.push(data);
+ }
+
+ setState("report", JSON.stringify(reports));
+
+ if (params.has("410")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 410, "Gone");
+ } else {
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+ }
+ return;
+ }
+
+ aResponse.setStatusLine(aRequest.httpVersion, 500, "Internal error");
+ aResponse.write("Invalid request");
+}
diff --git a/dom/reporting/tests/empty.html b/dom/reporting/tests/empty.html
new file mode 100644
index 0000000000..cd0875583a
--- /dev/null
+++ b/dom/reporting/tests/empty.html
@@ -0,0 +1 @@
+Hello world!
diff --git a/dom/reporting/tests/gtest/TestReportToParser.cpp b/dom/reporting/tests/gtest/TestReportToParser.cpp
new file mode 100644
index 0000000000..a3549886a3
--- /dev/null
+++ b/dom/reporting/tests/gtest/TestReportToParser.cpp
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/dom/ReportingHeader.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+TEST(ReportToParser, Basic)
+{
+ nsCOMPtr<nsIURI> uri;
+
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), "https://example.com");
+ ASSERT_EQ(NS_OK, rv);
+
+ bool urlEqual = false;
+
+ // Empty header.
+ UniquePtr<ReportingHeader::Client> client =
+ ReportingHeader::ParseHeader(nullptr, uri, ""_ns);
+ ASSERT_TRUE(!client);
+
+ // Empty header.
+ client = ReportingHeader::ParseHeader(nullptr, uri, " "_ns);
+ ASSERT_TRUE(!client);
+
+ // No minimal attributes
+ client = ReportingHeader::ParseHeader(nullptr, uri, "{}"_ns);
+ ASSERT_TRUE(!client);
+
+ // Single client
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 42, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
+ ASSERT_FALSE(client->mGroups.ElementAt(0).mIncludeSubdomains);
+ ASSERT_EQ(42, client->mGroups.ElementAt(0).mTTL);
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 clients, same group name.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 43, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 44, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
+ ASSERT_EQ(43, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first one with an invalid group name.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 43, \"group\": 123, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 44, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
+ ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first one with an invalid group name.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 43, \"group\": null, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 44, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
+ ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first one with an invalid group name.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 43, \"group\": {}, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 44, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("default"));
+ ASSERT_EQ(44, client->mGroups.ElementAt(0).mTTL);
+
+ // Single client: optional params
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 45, \"group\": \"foobar\", \"include_subdomains\": "
+ "true, \"endpoints\": [{\"url\": \"https://example.com\", "
+ "\"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mName.EqualsLiteral("foobar"));
+ ASSERT_TRUE(client->mGroups.ElementAt(0).mIncludeSubdomains);
+ ASSERT_EQ(45, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: missing max_age.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"endpoints\": [{\"url\": \"https://example.com\", \"priority\": "
+ "1, \"weight\": 2}]},"
+ "{\"max_age\": 46, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid max_age.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": null, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 46, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid max_age.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": \"foobar\", \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 46, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid max_age.
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": {}, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]},"
+ "{\"max_age\": 46, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(46, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: missing endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 47},"
+ "{\"max_age\": 48, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 47, \"endpoints\": null },"
+ "{\"max_age\": 48, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 47, \"endpoints\": \"abc\" },"
+ "{\"max_age\": 48, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 47, \"endpoints\": 42 },"
+ "{\"max_age\": 48, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: invalid endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 47, \"endpoints\": {} },"
+ "{\"max_age\": 48, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(48, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 clients, the first incomplete: empty endpoints
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 49, \"endpoints\": []},"
+ "{\"max_age\": 50, \"endpoints\": [{\"url\": "
+ "\"https://example.com\", \"priority\": 1, \"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ(50, client->mGroups.ElementAt(0).mTTL);
+
+ // 2 endpoints, the first incomplete: missing url
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
+ " {\"priority\": 1, \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 1, "
+ "\"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid url
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
+ " {\"url\": 42, \"priority\": 1, \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 1, "
+ "\"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid url
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 51, \"endpoints\": ["
+ " {\"url\": \"something here\", \"priority\": 1, \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 1, \"weight\": "
+ "2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid url
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 51, \"endpoints\": ["
+ " {\"url\": {}, \"priority\": 1, \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 1, "
+ "\"weight\": 2}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: missing priority
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
+ " {\"url\": \"https://example.com\", \"weight\": 3}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)3,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid priority
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
+ " {\"url\": \"https://example.com\", \"priority\": "
+ "{}, \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 2, "
+ "\"weight\": 3}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)3,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid priority
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
+ " {\"url\": \"https://example.com\", \"priority\": "
+ "\"ok\", \"weight\": 2},"
+ " {\"url\": \"https://example.com\", \"priority\": 2, "
+ "\"weight\": 3}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)2,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)3,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: missing weight
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString(
+ "{\"max_age\": 52, \"endpoints\": ["
+ " {\"url\": \"https://example.com\", \"priority\": 5}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)5,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)1,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+
+ // 2 endpoints, the first incomplete: invalid weight
+ client = ReportingHeader::ParseHeader(
+ nullptr, uri,
+ nsLiteralCString("{\"max_age\": 52, \"endpoints\": ["
+ " {\"url\": \"https://example.com\", \"priority\": 4, "
+ "\"weight\": []},"
+ " {\"url\": \"https://example.com\", \"priority\": 5, "
+ "\"weight\": 6}]}"));
+ ASSERT_TRUE(!!client);
+ ASSERT_EQ((uint32_t)1, client->mGroups.Length());
+ ASSERT_EQ((uint32_t)1, client->mGroups.ElementAt(0).mEndpoints.Length());
+ ASSERT_TRUE(
+ NS_SUCCEEDED(
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mUrl->Equals(
+ uri, &urlEqual)) &&
+ urlEqual);
+ ASSERT_EQ((uint32_t)5,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mPriority);
+ ASSERT_EQ((uint32_t)6,
+ client->mGroups.ElementAt(0).mEndpoints.ElementAt(0).mWeight);
+}
diff --git a/dom/reporting/tests/gtest/moz.build b/dom/reporting/tests/gtest/moz.build
new file mode 100644
index 0000000000..860ef48d1e
--- /dev/null
+++ b/dom/reporting/tests/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES = [
+ "TestReportToParser.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/dom/reporting/tests/iframe_delivering.html b/dom/reporting/tests/iframe_delivering.html
new file mode 100644
index 0000000000..c77ff50b9c
--- /dev/null
+++ b/dom/reporting/tests/iframe_delivering.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for delivering reports</title>
+</head>
+<body>
+
+<script type="application/javascript">
+
+function ok(a, msg) {
+ parent.postMessage({type: "test", check: !!a, msg }, "*");
+}
+
+function is(a, b, msg) {
+ ok(a === b, msg);
+}
+
+function finish() {
+ parent.postMessage({type: "finish" }, "*");
+}
+
+function checkReport() {
+ return new Promise(resolve => {
+ let id = setInterval(_ => {
+ fetch("delivering.sjs?task=check&min=3")
+ .then(r => r.text())
+ .then(text => {
+ if (text) {
+ resolve(JSON.parse(text));
+ clearInterval(id);
+ }
+ });
+ }, 1000);
+ });
+}
+
+function runTests(extraParams = "") {
+ // Call a deprecating operation.
+ for (let i = 0; i < 100; ++i) {
+ new TestingDeprecatedInterface();
+ }
+ ok(true, "Created a deprecated interface");
+
+ // Check if the report has been received.
+ return checkReport()
+ .then(reports => {
+ is(reports.length, 3, "We have 1 report");
+
+ let report = reports[0];
+ is(report.contentType, "application/reports+json", "Correct mime-type");
+ is(report.origin, "https://example.org", "Origin correctly set");
+ is(report.url, "https://example.org/tests/dom/reporting/tests/delivering.sjs" + extraParams, "URL is correctly set");
+ ok(!!report.body, "We have a report.body");
+ ok(report.body.age > 0, "Age is correctly set");
+ is(report.body.url, window.location.href, "URL is correctly set");
+ is(report.body.user_agent, navigator.userAgent, "User-agent matches");
+ ok(report.body.type, "deprecation", "Type is fine.");
+ ok(!!report.body.body, "We have the real report.body");
+ is(report.body.body.id, "DeprecatedTestingInterface", "Correct report.body.id");
+ is(report.body.body.message, "TestingDeprecatedInterface is a testing-only interface and this is its testing deprecation message.", "We have a report.body.message");
+ is(report.body.body.sourceFile, "https://example.org/tests/dom/reporting/tests/iframe_delivering.html", "report.body.sourceFile");
+ is(report.body.body.lineNumber, 40, "report.body.lineNumber");
+ is(report.body.body.columnNumber, 4, "report.body.columnNumber");
+ });
+}
+
+// Let's register a group + endpoint
+fetch("delivering.sjs?task=header")
+.then(r => r.text())
+.then(text => {
+ is(text, "OK", "Report-to header sent");
+})
+.then(_ => {
+ return runTests();
+})
+
+// Let's register a group + endpoint from a worker
+.then(_ => {
+ return new Promise(resolve => {
+ let w = new Worker("worker_delivering.js");
+ w.onmessage = e => resolve();
+ });
+})
+.then(_ => {
+ return runTests("&worker=true");
+})
+
+.then(finish);
+
+</script>
+</body>
+</html>
diff --git a/dom/reporting/tests/mochitest.ini b/dom/reporting/tests/mochitest.ini
new file mode 100644
index 0000000000..6b9cc0c7d2
--- /dev/null
+++ b/dom/reporting/tests/mochitest.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+prefs =
+ dom.reporting.enabled=true
+ dom.reporting.header.enabled=true
+ dom.reporting.testing.enabled=true
+
+[test_deprecated.html]
+support-files = common_deprecated.js worker_deprecated.js
+skip-if =
+ http3
+[test_memoryPressure.html]
+[test_delivering.html]
+support-files = delivering.sjs iframe_delivering.html worker_delivering.js
+skip-if =
+ http3
diff --git a/dom/reporting/tests/test_delivering.html b/dom/reporting/tests/test_delivering.html
new file mode 100644
index 0000000000..2c552273e0
--- /dev/null
+++ b/dom/reporting/tests/test_delivering.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for delivering reports</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+
+// Setting prefs.
+SpecialPowers.pushPrefEnv({ set: [
+ ["dom_reporting_delivering_timeout", 1],
+ ["dom_reporting_delivering_maxFailures", 2],
+ ["dom.reporting.delivering.maxReports", 3],
+]})
+
+// Tests run in iframes because the origin must be secure for report-to header.
+.then(_ => {
+ window.addEventListener("message", e => {
+ if (e.data.type == "finish") {
+ SimpleTest.finish();
+ return;
+ }
+
+ if (e.data.type == "test") {
+ ok(e.data.check, e.data.msg);
+ return;
+ }
+
+ ok(false, "Invalid message");
+ });
+
+ let ifr = document.createElement("iframe");
+ ifr.src = "https://example.org/tests/dom/reporting/tests/iframe_delivering.html";
+
+ document.body.appendChild(ifr);
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/dom/reporting/tests/test_deprecated.html b/dom/reporting/tests/test_deprecated.html
new file mode 100644
index 0000000000..da55978e9b
--- /dev/null
+++ b/dom/reporting/tests/test_deprecated.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Deprecated reports</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="common_deprecated.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+
+test_deprecatedInterface()
+.then(() => test_deprecatedMethod())
+.then(() => test_deprecatedMethodWithDataURI())
+.then(() => test_deprecatedAttribute())
+.then(() => test_takeRecords())
+.then(() => {
+ info("Workers!");
+
+ return new Promise(resolve => {
+ const w = new Worker("worker_deprecated.js");
+ w.onmessage = e => {
+ switch (e.data.type) {
+ case "info":
+ info(e.data.msg);
+ break;
+
+ case "check":
+ ok(e.data.check, e.data.msg);
+ break;
+
+ case "finish":
+ resolve();
+ break;
+
+ default:
+ ok(false, "Invalid message");
+ break;
+ }
+ }
+ });
+})
+
+.then(() => { SimpleTest.finish(); });
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/dom/reporting/tests/test_memoryPressure.html b/dom/reporting/tests/test_memoryPressure.html
new file mode 100644
index 0000000000..1bb887b05e
--- /dev/null
+++ b/dom/reporting/tests/test_memoryPressure.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for ReportingObserver + memory-pressure</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+
+info("Testing TakeRecords() without memory-pressure");
+let o = new ReportingObserver(() => {});
+o.observe();
+
+new TestingDeprecatedInterface();
+let r = o.takeRecords();
+is(r.length, 1, "We have 1 report");
+
+r = o.takeRecords();
+is(r.length, 0, "We have 0 reports after a takeRecords()");
+
+info("Testing DeprecatedTestingMethod report");
+
+new TestingDeprecatedInterface();
+SpecialPowers.Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
+
+r = o.takeRecords();
+is(r.length, 0, "We have 0 reports after a memory-pressure");
+
+</script>
+</body>
+</html>
diff --git a/dom/reporting/tests/worker_delivering.js b/dom/reporting/tests/worker_delivering.js
new file mode 100644
index 0000000000..539bcd231c
--- /dev/null
+++ b/dom/reporting/tests/worker_delivering.js
@@ -0,0 +1,5 @@
+fetch("delivering.sjs?task=header&worker=true")
+ .then(r => r.text())
+ .then(text => {
+ postMessage("All good!");
+ });
diff --git a/dom/reporting/tests/worker_deprecated.js b/dom/reporting/tests/worker_deprecated.js
new file mode 100644
index 0000000000..f6b57896f6
--- /dev/null
+++ b/dom/reporting/tests/worker_deprecated.js
@@ -0,0 +1,28 @@
+/* eslint-disable no-undef */
+
+// eslint-disable-next-line no-unused-vars
+function ok(a, msg) {
+ postMessage({ type: "check", check: !!a, msg });
+}
+
+// eslint-disable-next-line no-unused-vars
+function is(a, b, msg) {
+ ok(a === b, msg);
+}
+
+// eslint-disable-next-line no-unused-vars
+function info(msg) {
+ postMessage({ type: "info", msg });
+}
+
+function finish() {
+ postMessage({ type: "finish" });
+}
+
+importScripts("common_deprecated.js");
+
+test_deprecatedInterface()
+ .then(() => test_deprecatedMethod())
+ .then(() => test_deprecatedAttribute())
+ .then(() => test_takeRecords())
+ .then(() => finish());