summaryrefslogtreecommitdiffstats
path: root/toolkit/components/contentanalysis/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /toolkit/components/contentanalysis/tests
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/contentanalysis/tests')
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser.toml17
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js34
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js339
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js390
-rw-r--r--toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html12
-rw-r--r--toolkit/components/contentanalysis/tests/browser/head.js114
6 files changed, 903 insertions, 3 deletions
diff --git a/toolkit/components/contentanalysis/tests/browser/browser.toml b/toolkit/components/contentanalysis/tests/browser/browser.toml
index 0e21090299..bdbf350593 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser.toml
+++ b/toolkit/components/contentanalysis/tests/browser/browser.toml
@@ -1,3 +1,20 @@
[DEFAULT]
+run-if = ["os == 'win'"]
+support-files = [
+ "head.js",
+]
["browser_content_analysis_policies.js"]
+
+["browser_print_changing_page_content_analysis.js"]
+support-files = [
+ "!/toolkit/components/printing/tests/head.js",
+ "changing_page_for_print.html",
+]
+
+["browser_print_content_analysis.js"]
+support-files = [
+ "!/toolkit/components/printing/tests/head.js",
+ "!/toolkit/components/printing/tests/longerArticle.html",
+ "!/toolkit/components/printing/tests/simplifyArticleSample.html",
+]
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
index e2e001e9d1..b226c0a37a 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
+++ b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
@@ -11,15 +11,18 @@
"use strict";
-const { EnterprisePolicyTesting } = ChromeUtils.importESModule(
- "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
-);
+const { EnterprisePolicyTesting, PoliciesPrefTracker } =
+ ChromeUtils.importESModule(
+ "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
+ );
const kEnabledPref = "enabled";
const kPipeNamePref = "pipe_path_name";
const kTimeoutPref = "agent_timeout";
const kAllowUrlPref = "allow_url_regex_list";
const kDenyUrlPref = "deny_url_regex_list";
+const kAgentNamePref = "agent_name";
+const kClientSignaturePref = "client_signature";
const kPerUserPref = "is_per_user";
const kShowBlockedPref = "show_blocked_result";
const kDefaultAllowPref = "default_allow";
@@ -29,6 +32,7 @@ const ca = Cc["@mozilla.org/contentanalysis;1"].getService(
);
add_task(async function test_ca_active() {
+ PoliciesPrefTracker.start();
ok(!ca.isActive, "CA is inactive when pref and cmd line arg are missing");
// Set the pref without enterprise policy. CA should not be active.
@@ -62,11 +66,15 @@ add_task(async function test_ca_active() {
},
});
ok(ca.isActive, "CA is active when enabled by enterprise policy pref");
+ PoliciesPrefTracker.stop();
});
add_task(async function test_ca_enterprise_config() {
+ PoliciesPrefTracker.start();
const string1 = "this is a string";
const string2 = "this is another string";
+ const string3 = "an agent name";
+ const string4 = "a client signature";
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
policies: {
@@ -75,6 +83,8 @@ add_task(async function test_ca_enterprise_config() {
AgentTimeout: 99,
AllowUrlRegexList: string1,
DenyUrlRegexList: string2,
+ AgentName: string3,
+ ClientSignature: string4,
IsPerUser: true,
ShowBlockedResult: false,
DefaultAllow: true,
@@ -103,6 +113,18 @@ add_task(async function test_ca_enterprise_config() {
"deny urls match"
);
is(
+ Services.prefs.getStringPref("browser.contentanalysis." + kAgentNamePref),
+ string3,
+ "agent names match"
+ );
+ is(
+ Services.prefs.getStringPref(
+ "browser.contentanalysis." + kClientSignaturePref
+ ),
+ string4,
+ "client signatures match"
+ );
+ is(
Services.prefs.getBoolPref("browser.contentanalysis." + kPerUserPref),
true,
"per user match"
@@ -117,6 +139,7 @@ add_task(async function test_ca_enterprise_config() {
true,
"default allow match"
);
+ PoliciesPrefTracker.stop();
});
add_task(async function test_cleanup() {
@@ -124,4 +147,9 @@ add_task(async function test_cleanup() {
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
policies: {},
});
+ // These may have gotten set when ContentAnalysis was enabled through
+ // the policy and do not get cleared if there is no ContentAnalysis
+ // element - reset them manually here.
+ ca.isSetByEnterprisePolicy = false;
+ Services.prefs.setBoolPref("browser.contentanalysis." + kEnabledPref, false);
});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js
new file mode 100644
index 0000000000..72a7dcbb91
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js
@@ -0,0 +1,339 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/components/printing/tests/head.js",
+ this
+);
+
+const PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+);
+
+let mockCA = {
+ isActive: true,
+ mightBeActive: true,
+ errorValue: undefined,
+
+ setupForTest(shouldAllowRequest) {
+ this.shouldAllowRequest = shouldAllowRequest;
+ this.errorValue = undefined;
+ this.calls = [];
+ },
+
+ setupForTestWithError(errorValue) {
+ this.errorValue = errorValue;
+ this.calls = [];
+ },
+
+ getAction() {
+ if (this.shouldAllowRequest === undefined) {
+ this.shouldAllowRequest = true;
+ }
+ return this.shouldAllowRequest
+ ? Ci.nsIContentAnalysisResponse.eAllow
+ : Ci.nsIContentAnalysisResponse.eBlock;
+ },
+
+ // nsIContentAnalysis methods
+ async analyzeContentRequest(request, _autoAcknowledge) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequest, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ // Use setTimeout to simulate an async activity
+ await new Promise(res => setTimeout(res, 0));
+ return makeContentAnalysisResponse(this.getAction(), request.requestToken);
+ },
+
+ analyzeContentRequestCallback(request, autoAcknowledge, callback) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequestCallback, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ let response = makeContentAnalysisResponse(
+ this.getAction(),
+ request.requestToken
+ );
+ // Use setTimeout to simulate an async activity
+ setTimeout(() => {
+ callback.contentResult(response);
+ }, 0);
+ },
+};
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const TEST_PAGE_URL = PrintHelper.getTestPageUrlHTTPS(
+ "changing_page_for_print.html"
+);
+
+function addUniqueSuffix(prefix) {
+ return `${prefix}-${Services.uuid
+ .generateUUID()
+ .toString()
+ .slice(1, -1)}.pdf`;
+}
+
+async function printToDestination(aBrowser, aDestination) {
+ let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let fileName = addUniqueSuffix(`printDestinationTest-${aDestination}`);
+ let filePath = PathUtils.join(tmpDir.path, fileName);
+
+ info(`Printing to ${filePath}`);
+
+ let settings = PSSVC.createNewPrintSettings();
+ settings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+ settings.outputDestination = aDestination;
+
+ settings.headerStrCenter = "";
+ settings.headerStrLeft = "";
+ settings.headerStrRight = "";
+ settings.footerStrCenter = "";
+ settings.footerStrLeft = "";
+ settings.footerStrRight = "";
+
+ settings.unwriteableMarginTop = 1; /* Just to ensure settings are respected on both */
+ let outStream = null;
+ if (aDestination == Ci.nsIPrintSettings.kOutputDestinationFile) {
+ settings.toFileName = PathUtils.join(tmpDir.path, fileName);
+ } else {
+ is(aDestination, Ci.nsIPrintSettings.kOutputDestinationStream);
+ outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let tmpFile = tmpDir.clone();
+ tmpFile.append(fileName);
+ outStream.init(tmpFile, -1, 0o666, 0);
+ settings.outputStream = outStream;
+ }
+
+ await aBrowser.browsingContext.print(settings);
+
+ return filePath;
+}
+
+function assertContentAnalysisRequest(request) {
+ is(request.url.spec, TEST_PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.ePrint,
+ "request has print analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eOperationPrint,
+ "request has print operationTypeForDisplay"
+ );
+ is(request.textContent, "", "request textContent should be empty");
+ is(request.filePath, "", "request filePath should be empty");
+ isnot(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ isnot(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ // This effectively tests that the PDF content sent to Content Analysis
+ // and the content that is actually printed matches. This is necessary
+ // because a previous iteration of the Content Analysis code didn't use
+ // a static Document clone for this and so the content would differ. (since
+ // the .html file in question adds content to the page when print events
+ // happen)
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+
+ await IOUtils.remove(filePath);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_CONTENT_BLOCKED/.test(e.toString()),
+ "Got content blocked error"
+ );
+ }
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(async function testPrintToStreamWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_NOT_AVAILABLE/.test(e.toString()),
+ "Error in mock CA was propagated out"
+ );
+ }
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(async function testPrintThroughDialogWithContentAnalysisActive() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ await helper.startPrint();
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ file.path,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
new file mode 100644
index 0000000000..9b4c0ffa60
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
@@ -0,0 +1,390 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/components/printing/tests/head.js",
+ this
+);
+
+const PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+);
+
+let mockCA = {
+ isActive: true,
+ mightBeActive: true,
+ errorValue: undefined,
+
+ setupForTest(shouldAllowRequest) {
+ this.shouldAllowRequest = shouldAllowRequest;
+ this.errorValue = undefined;
+ this.calls = [];
+ },
+
+ setupForTestWithError(errorValue) {
+ this.errorValue = errorValue;
+ this.calls = [];
+ },
+
+ clearCalls() {
+ this.calls = [];
+ },
+
+ getAction() {
+ if (this.shouldAllowRequest === undefined) {
+ this.shouldAllowRequest = true;
+ }
+ return this.shouldAllowRequest
+ ? Ci.nsIContentAnalysisResponse.eAllow
+ : Ci.nsIContentAnalysisResponse.eBlock;
+ },
+
+ // nsIContentAnalysis methods
+ async analyzeContentRequest(request, _autoAcknowledge) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequest, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ // Use setTimeout to simulate an async activity
+ await new Promise(res => setTimeout(res, 0));
+ return makeContentAnalysisResponse(this.getAction(), request.requestToken);
+ },
+
+ analyzeContentRequestCallback(request, autoAcknowledge, callback) {
+ info(
+ "Mock ContentAnalysis service: analyzeContentRequestCallback, this.shouldAllowRequest=" +
+ this.shouldAllowRequest +
+ ", this.errorValue=" +
+ this.errorValue
+ );
+ this.calls.push(request);
+ if (this.errorValue) {
+ throw this.errorValue;
+ }
+ let response = makeContentAnalysisResponse(
+ this.getAction(),
+ request.requestToken
+ );
+ // Use setTimeout to simulate an async activity
+ setTimeout(() => {
+ callback.contentResult(response);
+ }, 0);
+ },
+};
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const TEST_PAGE_URL =
+ "https://example.com/browser/toolkit/components/printing/tests/simplifyArticleSample.html";
+const TEST_PAGE_URL_2 =
+ "https://example.com/browser/toolkit/components/printing/tests/longerArticle.html";
+
+function addUniqueSuffix(prefix) {
+ return `${prefix}-${Services.uuid
+ .generateUUID()
+ .toString()
+ .slice(1, -1)}.pdf`;
+}
+
+async function printToDestination(aBrowser, aDestination) {
+ let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let fileName = addUniqueSuffix(`printDestinationTest-${aDestination}`);
+ let filePath = PathUtils.join(tmpDir.path, fileName);
+
+ info(`Printing to ${filePath}`);
+
+ let settings = PSSVC.createNewPrintSettings();
+ settings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+ settings.outputDestination = aDestination;
+
+ settings.headerStrCenter = "";
+ settings.headerStrLeft = "";
+ settings.headerStrRight = "";
+ settings.footerStrCenter = "";
+ settings.footerStrLeft = "";
+ settings.footerStrRight = "";
+
+ settings.unwriteableMarginTop = 1; /* Just to ensure settings are respected on both */
+ let outStream = null;
+ if (aDestination == Ci.nsIPrintSettings.kOutputDestinationFile) {
+ settings.toFileName = PathUtils.join(tmpDir.path, fileName);
+ } else {
+ is(aDestination, Ci.nsIPrintSettings.kOutputDestinationStream);
+ outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let tmpFile = tmpDir.clone();
+ tmpFile.append(fileName);
+ outStream.init(tmpFile, -1, 0o666, 0);
+ settings.outputStream = outStream;
+ }
+
+ await aBrowser.browsingContext.print(settings);
+
+ return filePath;
+}
+
+function assertContentAnalysisRequest(request, expectedUrl) {
+ is(request.url.spec, expectedUrl ?? TEST_PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.ePrint,
+ "request has print analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eOperationPrint,
+ "request has print operationTypeForDisplay"
+ );
+ is(request.textContent, "", "request textContent should be empty");
+ is(request.filePath, "", "request filePath should be empty");
+ isnot(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ isnot(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+// Printing to a stream is different than going through the print preview dialog because it
+// doesn't make a static clone of the document before the print, which causes the
+// Content Analysis code to go through a different code path. This is similar to what
+// happens when various preferences are set to skip the print preview dialog, for example
+// print.prefer_system_dialog.
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+
+ await IOUtils.remove(filePath);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamAfterNavigationWithContentAnalysisActiveAndAllowing() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ let filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ mockCA.clearCalls();
+
+ await IOUtils.remove(filePath);
+
+ BrowserTestUtils.startLoadingURIString(
+ helper.sourceBrowser,
+ TEST_PAGE_URL_2
+ );
+ await BrowserTestUtils.browserLoaded(helper.sourceBrowser);
+
+ filePath = await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0], TEST_PAGE_URL_2);
+ await waitForFileToAlmostMatchSize(
+ filePath,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintToStreamWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_CONTENT_BLOCKED/.test(e.toString()),
+ "Got content blocked error"
+ );
+ }
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(async function testPrintToStreamWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ try {
+ await printToDestination(
+ helper.sourceBrowser,
+ Ci.nsIPrintSettings.kOutputDestinationFile
+ );
+ ok(false, "Content analysis should make this fail to print");
+ } catch (e) {
+ ok(
+ /NS_ERROR_NOT_AVAILABLE/.test(e.toString()),
+ "Error in mock CA was propagated out"
+ );
+ }
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(async function testPrintThroughDialogWithContentAnalysisActive() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(true);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0]);
+
+ await waitForFileToAlmostMatchSize(
+ file.path,
+ mockCA.calls[0].printDataSize
+ );
+ },
+ TEST_PAGE_URL,
+ true
+ );
+});
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisActiveAndBlocking() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ mockCA.setupForTest(false);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
+
+add_task(
+ async function testPrintThroughDialogWithContentAnalysisReturningError() {
+ await PrintHelper.withTestPage(
+ async helper => {
+ expectUncaughtException();
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ await helper.startPrint();
+ let fileName = addUniqueSuffix(`printDialogTest`);
+ let file = helper.mockFilePicker(fileName);
+ info(`Printing to ${file.path}`);
+ try {
+ await helper.assertPrintToFile(file, () => {
+ EventUtils.sendKey("return", helper.win);
+ });
+ } catch (e) {
+ ok(
+ /Wait for target file to get created/.test(e.toString()),
+ "Target file should not get created"
+ );
+ }
+ ok(!file.exists(), "File should not exist");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0]);
+ },
+ TEST_PAGE_URL,
+ true
+ );
+ }
+);
diff --git a/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html b/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html
new file mode 100644
index 0000000000..de6f9001aa
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/changing_page_for_print.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<p>Some random text</p>
+<button onclick="print()">Print the page</button>
+<pre id="log"></pre>
+<script>
+let i = 0;
+for (let t of ["beforeprint", "afterprint"]) {
+ addEventListener(t, () => {
+ document.getElementById("log").appendChild(document.createTextNode(`[${i++}] ${t}\n`));
+ });
+}
+</script>
diff --git a/toolkit/components/contentanalysis/tests/browser/head.js b/toolkit/components/contentanalysis/tests/browser/head.js
new file mode 100644
index 0000000000..e645caa2d7
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/head.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+// Wraps the given object in an XPConnect wrapper and, if an interface
+// is passed, queries the result to that interface.
+function xpcWrap(obj, iface) {
+ let ifacePointer = Cc[
+ "@mozilla.org/supports-interface-pointer;1"
+ ].createInstance(Ci.nsISupportsInterfacePointer);
+
+ ifacePointer.data = obj;
+ if (iface) {
+ return ifacePointer.data.QueryInterface(iface);
+ }
+ return ifacePointer.data;
+}
+
+/**
+ * Mock a (set of) service(s) as the object mockService.
+ *
+ * @param {[string]} serviceNames
+ * array of services names that mockService will be
+ * allowed to QI to. Must include the name of the
+ * service referenced by contractId.
+ * @param {string} contractId
+ * the component ID that will reference the mock object
+ * instead of the original service
+ * @param {object} interfaceObj
+ * interface object for the component
+ * @param {object} mockService
+ * object that satisfies the contract well
+ * enough to use as a mock of it
+ * @returns {object} The newly-mocked service
+ */
+function mockService(serviceNames, contractId, interfaceObj, mockService) {
+ // xpcWrap allows us to mock [implicit_jscontext] methods.
+ let newService = {
+ ...mockService,
+ QueryInterface: ChromeUtils.generateQI(serviceNames),
+ };
+ let o = xpcWrap(newService, interfaceObj);
+ let cid = MockRegistrar.register(contractId, o);
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(cid);
+ });
+ return newService;
+}
+
+/**
+ * Mock the nsIContentAnalysis service with the object mockCAService.
+ *
+ * @param {object} mockCAService
+ * the service to mock for nsIContentAnalysis
+ * @returns {object} The newly-mocked service
+ */
+function mockContentAnalysisService(mockCAService) {
+ return mockService(
+ ["nsIContentAnalysis"],
+ "@mozilla.org/contentanalysis;1",
+ Ci.nsIContentAnalysis,
+ mockCAService
+ );
+}
+
+/**
+ * Make an nsIContentAnalysisResponse.
+ *
+ * @param {number} action The action to take, from the
+ * nsIContentAnalysisResponse.Action enum.
+ * @param {string} token The requestToken.
+ * @returns {object} An object that conforms to nsIContentAnalysisResponse.
+ */
+function makeContentAnalysisResponse(action, token) {
+ return {
+ action,
+ shouldAllowContent: action != Ci.nsIContentAnalysisResponse.eBlock,
+ requestToken: token,
+ acknowledge: _acknowledgement => {},
+ };
+}
+
+async function waitForFileToAlmostMatchSize(filePath, expectedSize) {
+ // In Cocoa the CGContext adds a hash, plus there are other minor
+ // non-user-visible differences, so we need to be a bit more sloppy there.
+ //
+ // We see one byte difference in Windows and Linux on automation sometimes,
+ // though files are consistently the same locally, that needs
+ // investigation, but it's probably harmless.
+ // Note that this is copied from browser_print_stream.js.
+ const maxSizeDifference = AppConstants.platform == "macosx" ? 100 : 3;
+
+ // Buffering shenanigans? Wait for sizes to match... There's no great
+ // IOUtils methods to force a flush without writing anything...
+ // Note that this means if this results in a timeout this is exactly
+ // the same as a test failure.
+ // This is taken from toolkit/components/printing/tests/browser_print_stream.js
+ await TestUtils.waitForCondition(async function () {
+ let fileStat = await IOUtils.stat(filePath);
+
+ info("got size: " + fileStat.size + " expected: " + expectedSize);
+ Assert.greater(
+ fileStat.size,
+ 0,
+ "File should not be empty: " + fileStat.size
+ );
+ return Math.abs(fileStat.size - expectedSize) <= maxSizeDifference;
+ }, "Sizes should (almost) match");
+}