summaryrefslogtreecommitdiffstats
path: root/toolkit/components/contentanalysis/tests/browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
commita90a5cba08fdf6c0ceb95101c275108a152a3aed (patch)
tree532507288f3defd7f4dcf1af49698bcb76034855 /toolkit/components/contentanalysis/tests/browser
parentAdding debian version 126.0.1-1. (diff)
downloadfirefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz
firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/contentanalysis/tests/browser')
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser.toml27
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_content_analysis.js363
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_file_content_analysis.js202
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_inputandtextarea_content_analysis.js112
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_noformatting_content_analysis.js92
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_prompt_content_analysis.js92
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_clipboard_read_async_content_analysis.js195
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js17
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_changing_page_content_analysis.js64
-rw-r--r--toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js68
-rw-r--r--toolkit/components/contentanalysis/tests/browser/clipboard_paste_file.html61
-rw-r--r--toolkit/components/contentanalysis/tests/browser/clipboard_paste_inputandtextarea.html41
-rw-r--r--toolkit/components/contentanalysis/tests/browser/clipboard_paste_noformatting.html46
-rw-r--r--toolkit/components/contentanalysis/tests/browser/clipboard_paste_prompt.html7
-rw-r--r--toolkit/components/contentanalysis/tests/browser/clipboard_read_async.html95
-rw-r--r--toolkit/components/contentanalysis/tests/browser/head.js124
16 files changed, 1472 insertions, 134 deletions
diff --git a/toolkit/components/contentanalysis/tests/browser/browser.toml b/toolkit/components/contentanalysis/tests/browser/browser.toml
index bdbf350593..7a9a3533d1 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser.toml
+++ b/toolkit/components/contentanalysis/tests/browser/browser.toml
@@ -4,6 +4,33 @@ support-files = [
"head.js",
]
+["browser_clipboard_content_analysis.js"]
+
+["browser_clipboard_paste_file_content_analysis.js"]
+support-files = [
+ "clipboard_paste_file.html",
+]
+
+["browser_clipboard_paste_inputandtextarea_content_analysis.js"]
+support-files = [
+ "clipboard_paste_inputandtextarea.html",
+]
+
+["browser_clipboard_paste_noformatting_content_analysis.js"]
+support-files = [
+ "clipboard_paste_noformatting.html",
+]
+
+["browser_clipboard_paste_prompt_content_analysis.js"]
+support-files = [
+ "clipboard_paste_prompt.html",
+]
+
+["browser_clipboard_read_async_content_analysis.js"]
+support-files = [
+ "clipboard_read_async.html",
+]
+
["browser_content_analysis_policies.js"]
["browser_print_changing_page_content_analysis.js"]
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_content_analysis.js
new file mode 100644
index 0000000000..875dbaa6e3
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_content_analysis.js
@@ -0,0 +1,363 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test is used to check copy and paste in editable areas to ensure that non-text
+// types (html and images) are copied to and pasted from the clipboard properly.
+
+var testPage =
+ "<body style='margin: 0'>" +
+ " <img id='img' tabindex='1' src='http://example.org/browser/browser/base/content/test/general/moz.png'>" +
+ " <div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>" +
+ "</body>";
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+async function testClipboardWithContentAnalysis(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+ let tab = BrowserTestUtils.addTab(gBrowser);
+ let browser = gBrowser.getBrowserForTab(tab);
+
+ gBrowser.selectedTab = tab;
+
+ await promiseTabLoadEvent(tab, "data:text/html," + escape(testPage));
+ await SimpleTest.promiseFocus(browser);
+
+ function sendKey(key, code) {
+ return BrowserTestUtils.synthesizeKey(
+ key,
+ { code, accelKey: true },
+ browser
+ );
+ }
+
+ // On windows, HTML clipboard includes extra data.
+ // The values are from widget/windows/nsDataObj.cpp.
+ const htmlPrefix = navigator.platform.includes("Win")
+ ? "<html><body>\n<!--StartFragment-->"
+ : "";
+ const htmlPostfix = navigator.platform.includes("Win")
+ ? "<!--EndFragment-->\n</body>\n</html>"
+ : "";
+
+ await SpecialPowers.spawn(browser, [], () => {
+ var doc = content.document;
+ var main = doc.getElementById("main");
+ main.focus();
+
+ // Select an area of the text.
+ let selection = doc.getSelection();
+ selection.modify("move", "left", "line");
+ selection.modify("move", "right", "character");
+ selection.modify("move", "right", "character");
+ selection.modify("move", "right", "character");
+ selection.modify("extend", "right", "word");
+ selection.modify("extend", "right", "word");
+ });
+
+ // The data is empty as the selection was copied during the event default phase.
+ let copyEventPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "copy",
+ false,
+ event => {
+ return event.clipboardData.mozItemCount == 0;
+ }
+ );
+ await SpecialPowers.spawn(browser, [], () => {});
+ await sendKey("c");
+ await copyEventPromise;
+
+ let pastePromise = SpecialPowers.spawn(
+ browser,
+ [htmlPrefix, htmlPostfix, allowPaste],
+ (htmlPrefixChild, htmlPostfixChild, allowPaste) => {
+ let selection = content.document.getSelection();
+ selection.modify("move", "right", "line");
+
+ return new Promise((resolve, _reject) => {
+ content.addEventListener(
+ "paste",
+ event => {
+ let clipboardData = event.clipboardData;
+ Assert.equal(
+ clipboardData.mozItemCount,
+ 1,
+ "One item on clipboard"
+ );
+ Assert.equal(
+ clipboardData.types.length,
+ 2,
+ "Two types on clipboard"
+ );
+ Assert.equal(
+ clipboardData.types[0],
+ "text/html",
+ "text/html on clipboard"
+ );
+ Assert.equal(
+ clipboardData.types[1],
+ "text/plain",
+ "text/plain on clipboard"
+ );
+ Assert.equal(
+ clipboardData.getData("text/html"),
+ allowPaste
+ ? htmlPrefixChild + "t <b>Bold</b>" + htmlPostfixChild
+ : "",
+ "text/html value"
+ );
+ Assert.equal(
+ clipboardData.getData("text/plain"),
+ allowPaste ? "t Bold" : "",
+ "text/plain value"
+ );
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+ });
+ }
+ );
+
+ await SpecialPowers.spawn(browser, [], () => {});
+
+ await sendKey("v");
+ await pastePromise;
+ // 2 calls because there are two formats on the clipboard
+ is(mockCA.calls.length, 2, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(
+ mockCA.calls[0],
+ htmlPrefix + "t <b>Bold</b>" + htmlPostfix
+ );
+ assertContentAnalysisRequest(mockCA.calls[1], "t Bold");
+ mockCA.clearCalls();
+
+ let copyPromise = SpecialPowers.spawn(browser, [], () => {
+ var main = content.document.getElementById("main");
+
+ Assert.equal(
+ main.innerHTML,
+ "Test <b>Bold</b> After Textt <b>Bold</b>",
+ "Copy and paste html"
+ );
+
+ let selection = content.document.getSelection();
+ selection.modify("extend", "left", "word");
+ selection.modify("extend", "left", "word");
+ selection.modify("extend", "left", "character");
+
+ return new Promise((resolve, _reject) => {
+ content.addEventListener(
+ "cut",
+ event => {
+ event.clipboardData.setData("text/plain", "Some text");
+ event.clipboardData.setData("text/html", "<i>Italic</i> ");
+ selection.deleteFromDocument();
+ event.preventDefault();
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+ });
+ });
+
+ await SpecialPowers.spawn(browser, [], () => {});
+
+ await sendKey("x");
+ await copyPromise;
+
+ pastePromise = SpecialPowers.spawn(
+ browser,
+ [htmlPrefix, htmlPostfix, allowPaste],
+ (htmlPrefixChild, htmlPostfixChild, allowPaste) => {
+ let selection = content.document.getSelection();
+ selection.modify("move", "left", "line");
+
+ return new Promise((resolve, _reject) => {
+ content.addEventListener(
+ "paste",
+ event => {
+ let clipboardData = event.clipboardData;
+ Assert.equal(
+ clipboardData.mozItemCount,
+ 1,
+ "One item on clipboard 2"
+ );
+ Assert.equal(
+ clipboardData.types.length,
+ 2,
+ "Two types on clipboard 2"
+ );
+ Assert.equal(
+ clipboardData.types[0],
+ "text/html",
+ "text/html on clipboard 2"
+ );
+ Assert.equal(
+ clipboardData.types[1],
+ "text/plain",
+ "text/plain on clipboard 2"
+ );
+ Assert.equal(
+ clipboardData.getData("text/html"),
+ allowPaste
+ ? htmlPrefixChild + "<i>Italic</i> " + htmlPostfixChild
+ : "",
+ "text/html value 2"
+ );
+ Assert.equal(
+ clipboardData.getData("text/plain"),
+ allowPaste ? "Some text" : "",
+ "text/plain value 2"
+ );
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+ });
+ }
+ );
+
+ await SpecialPowers.spawn(browser, [], () => {});
+
+ await sendKey("v");
+ await pastePromise;
+ // 2 calls because there are two formats on the clipboard
+ is(mockCA.calls.length, 2, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(
+ mockCA.calls[0],
+ htmlPrefix + "<i>Italic</i> " + htmlPostfix
+ );
+ assertContentAnalysisRequest(mockCA.calls[1], "Some text");
+ mockCA.clearCalls();
+
+ await SpecialPowers.spawn(browser, [], () => {
+ var main = content.document.getElementById("main");
+ Assert.equal(
+ main.innerHTML,
+ "<i>Italic</i> Test <b>Bold</b> After<b></b>",
+ "Copy and paste html 2"
+ );
+ });
+
+ // Next, check that the Copy Image command works.
+
+ // The context menu needs to be opened to properly initialize for the copy
+ // image command to run.
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let contextMenuShown = promisePopupShown(contextMenu);
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#img",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ await contextMenuShown;
+
+ document.getElementById("context-copyimage-contents").doCommand();
+
+ contextMenu.hidePopup();
+ await promisePopupHidden(contextMenu);
+
+ // Focus the content again
+ await SimpleTest.promiseFocus(browser);
+
+ pastePromise = SpecialPowers.spawn(
+ browser,
+ [htmlPrefix, htmlPostfix, allowPaste],
+ (htmlPrefixChild, htmlPostfixChild, allowPaste) => {
+ var doc = content.document;
+ var main = doc.getElementById("main");
+ main.focus();
+
+ return new Promise((resolve, reject) => {
+ content.addEventListener(
+ "paste",
+ event => {
+ let clipboardData = event.clipboardData;
+
+ // DataTransfer doesn't support the image types yet, so only text/html
+ // will be present.
+ let clipboardText = clipboardData.getData("text/html");
+ if (allowPaste) {
+ if (
+ clipboardText !==
+ htmlPrefixChild +
+ '<img id="img" tabindex="1" src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
+ htmlPostfixChild
+ ) {
+ reject(
+ "Clipboard Data did not contain an image, was " +
+ clipboardText
+ );
+ }
+ } else if (clipboardText !== "") {
+ reject("Clipboard Data should be empty, was " + clipboardText);
+ }
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+ });
+ }
+ );
+
+ await SpecialPowers.spawn(browser, [], () => {});
+ await sendKey("v");
+ await pastePromise;
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(
+ mockCA.calls[0],
+ htmlPrefix +
+ '<img id="img" tabindex="1" src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
+ htmlPostfix
+ );
+ mockCA.clearCalls();
+
+ // The new content should now include an image.
+ await SpecialPowers.spawn(browser, [], () => {
+ var main = content.document.getElementById("main");
+ Assert.equal(
+ main.innerHTML,
+ '<i>Italic</i> <img id="img" tabindex="1" ' +
+ 'src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
+ "Test <b>Bold</b> After<b></b>",
+ "Paste after copy image"
+ );
+ });
+
+ gBrowser.removeCurrentTab();
+}
+
+function assertContentAnalysisRequest(request, expectedText) {
+ is(
+ request.url.spec,
+ "data:text/html," + escape(testPage),
+ "request has correct URL"
+ );
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, "", "request filePath should be empty");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+add_task(async function testClipboardWithContentAnalysisAllow() {
+ await testClipboardWithContentAnalysis(true);
+});
+
+add_task(async function testClipboardWithContentAnalysisBlock() {
+ await testClipboardWithContentAnalysis(false);
+});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_file_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_file_content_analysis.js
new file mode 100644
index 0000000000..8441c4d7fd
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_file_content_analysis.js
@@ -0,0 +1,202 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that (real) files can be pasted into chrome/content.
+// Pasting files should also hide all other data from content.
+
+function setClipboard(path) {
+ const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(path);
+
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ trans.addDataFlavor("application/x-moz-file");
+ trans.setTransferData("application/x-moz-file", file);
+
+ trans.addDataFlavor("text/plain");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = "Alternate";
+ trans.setTransferData("text/plain", str);
+
+ // Write to clipboard.
+ Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+}
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const PAGE_URL =
+ "https://example.com/browser/toolkit/components/contentanalysis/tests/browser/clipboard_paste_file.html";
+
+function assertContentAnalysisRequest(
+ request,
+ expectedDisplayType,
+ expectedFilePath,
+ expectedText
+) {
+ is(request.url.spec, PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ expectedDisplayType,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, expectedFilePath, "request filePath should match");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+function assertContentAnalysisRequestFile(request, expectedFilePath) {
+ assertContentAnalysisRequest(
+ request,
+ Ci.nsIContentAnalysisRequest.eCustomDisplayString,
+ expectedFilePath,
+ ""
+ );
+}
+function assertContentAnalysisRequestText(request, expectedText) {
+ assertContentAnalysisRequest(
+ request,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "",
+ expectedText
+ );
+}
+
+async function testClipboardPasteFileWithContentAnalysis(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.events.dataTransfer.mozFile.enabled", true]],
+ });
+
+ // Create a temporary file that will be pasted.
+ const file = await IOUtils.createUniqueFile(
+ PathUtils.tempDir,
+ "test-file.txt",
+ 0o600
+ );
+ const FILE_TEXT = "Hello World!";
+ await IOUtils.writeUTF8(file, FILE_TEXT);
+
+ // Put the data directly onto the native clipboard to make sure
+ // it isn't handled internally in Gecko somehow.
+ setClipboard(file);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+
+ let resultPromise = SpecialPowers.spawn(browser, [allowPaste], allowPaste => {
+ return new Promise(resolve => {
+ content.document.addEventListener("testresult", event => {
+ resolve(event.detail.result);
+ });
+ content.document.getElementById("pasteAllowed").checked = allowPaste;
+ });
+ });
+
+ // Focus <input> in content
+ await SpecialPowers.spawn(browser, [], async function () {
+ content.document.getElementById("input").focus();
+ });
+
+ // Paste file into <input> in content
+ await BrowserTestUtils.synthesizeKey("v", { accelKey: true }, browser);
+
+ let result = await resultPromise;
+ is(
+ result,
+ allowPaste ? PathUtils.filename(file) : "",
+ "Correctly pasted file in content"
+ );
+ is(mockCA.calls.length, 2, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequestFile(mockCA.calls[0], file);
+ assertContentAnalysisRequestText(mockCA.calls[1], "Alternate");
+ mockCA.clearCalls();
+
+ // The following part of the test is done in-process (note the use of document here instead of
+ // content.document) so none of this should go through content analysis.
+ var input = document.createElement("input");
+ document.documentElement.appendChild(input);
+ input.focus();
+
+ await new Promise((resolve, _reject) => {
+ input.addEventListener(
+ "paste",
+ function (event) {
+ let dt = event.clipboardData;
+ is(dt.types.length, 3, "number of types");
+ ok(dt.types.includes("text/plain"), "text/plain exists in types");
+ ok(
+ dt.types.includes("application/x-moz-file"),
+ "application/x-moz-file exists in types"
+ );
+ is(dt.types[2], "Files", "Last type should be 'Files'");
+ ok(
+ dt.mozTypesAt(0).contains("text/plain"),
+ "text/plain exists in mozTypesAt"
+ );
+ is(
+ dt.getData("text/plain"),
+ "Alternate",
+ "text/plain returned in getData"
+ );
+ is(
+ dt.mozGetDataAt("text/plain", 0),
+ "Alternate",
+ "text/plain returned in mozGetDataAt"
+ );
+
+ ok(
+ dt.mozTypesAt(0).contains("application/x-moz-file"),
+ "application/x-moz-file exists in mozTypesAt"
+ );
+ let mozFile = dt.mozGetDataAt("application/x-moz-file", 0);
+
+ ok(
+ mozFile instanceof Ci.nsIFile,
+ "application/x-moz-file returned nsIFile with mozGetDataAt"
+ );
+
+ is(
+ mozFile.leafName,
+ PathUtils.filename(file),
+ "nsIFile has correct leafName"
+ );
+
+ is(mozFile.fileSize, FILE_TEXT.length, "nsIFile has correct length");
+
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+
+ EventUtils.synthesizeKey("v", { accelKey: true });
+ });
+ is(mockCA.calls.length, 0, "Correct number of calls to Content Analysis");
+
+ input.remove();
+
+ BrowserTestUtils.removeTab(tab);
+
+ await IOUtils.remove(file);
+}
+
+add_task(async function testClipboardPasteFileWithContentAnalysisAllow() {
+ await testClipboardPasteFileWithContentAnalysis(true);
+});
+
+add_task(async function testClipboardPasteFileWithContentAnalysisBlock() {
+ await testClipboardPasteFileWithContentAnalysis(false);
+});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_inputandtextarea_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_inputandtextarea_content_analysis.js
new file mode 100644
index 0000000000..6fe37c3368
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_inputandtextarea_content_analysis.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const PAGE_URL =
+ "https://example.com/browser/toolkit/components/contentanalysis/tests/browser/clipboard_paste_inputandtextarea.html";
+const CLIPBOARD_TEXT_STRING = "Just some text";
+async function testClipboardPaste(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+
+ setClipboardData();
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+
+ await SpecialPowers.spawn(browser, [allowPaste], async allowPaste => {
+ content.document.getElementById("pasteAllowed").checked = allowPaste;
+ });
+ await testPasteWithElementId("testInput", browser, allowPaste);
+ await testPasteWithElementId("testTextArea", browser, allowPaste);
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+function setClipboardData() {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ trans.addDataFlavor("text/plain");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = CLIPBOARD_TEXT_STRING;
+ trans.setTransferData("text/plain", str);
+
+ // Write to clipboard.
+ Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+}
+
+async function testPasteWithElementId(elementId, browser, allowPaste) {
+ let resultPromise = SpecialPowers.spawn(browser, [], () => {
+ return new Promise(resolve => {
+ content.document.addEventListener(
+ "testresult",
+ event => {
+ resolve(event.detail.result);
+ },
+ { once: true }
+ );
+ });
+ });
+
+ // Paste into content
+ await SpecialPowers.spawn(browser, [elementId], async elementId => {
+ content.document.getElementById(elementId).focus();
+ });
+ await BrowserTestUtils.synthesizeKey("v", { accelKey: true }, browser);
+ let result = await resultPromise;
+ is(result, undefined, "Got unexpected result from page");
+
+ // Because we call event.clipboardData.getData in the test, this causes another call to
+ // content analysis.
+ is(mockCA.calls.length, 2, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0], CLIPBOARD_TEXT_STRING);
+ assertContentAnalysisRequest(mockCA.calls[1], CLIPBOARD_TEXT_STRING);
+ mockCA.clearCalls();
+ let value = await getElementValue(browser, elementId);
+ is(
+ value,
+ allowPaste ? CLIPBOARD_TEXT_STRING : "",
+ "element has correct value"
+ );
+}
+
+function assertContentAnalysisRequest(request, expectedText) {
+ is(request.url.spec, PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, "", "request filePath should match");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+async function getElementValue(browser, elementId) {
+ return await SpecialPowers.spawn(browser, [elementId], async elementId => {
+ return content.document.getElementById(elementId).value;
+ });
+}
+
+add_task(async function testClipboardPasteWithContentAnalysisAllow() {
+ await testClipboardPaste(true);
+});
+
+add_task(async function testClipboardPasteWithContentAnalysisBlock() {
+ await testClipboardPaste(false);
+});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_noformatting_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_noformatting_content_analysis.js
new file mode 100644
index 0000000000..0eedcddd17
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_noformatting_content_analysis.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+const PAGE_URL =
+ "https://example.com/browser/toolkit/components/contentanalysis/tests/browser/clipboard_paste_noformatting.html";
+async function testClipboardPasteNoFormatting(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ const CLIPBOARD_TEXT_STRING = "Some text";
+ const CLIPBOARD_HTML_STRING = "<b>Some HTML</b>";
+ {
+ trans.addDataFlavor("text/plain");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = CLIPBOARD_TEXT_STRING;
+ trans.setTransferData("text/plain", str);
+ }
+ {
+ trans.addDataFlavor("text/html");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = CLIPBOARD_HTML_STRING;
+ trans.setTransferData("text/html", str);
+ }
+
+ // Write to clipboard.
+ Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+ let result = await SpecialPowers.spawn(browser, [allowPaste], allowPaste => {
+ return new Promise(resolve => {
+ content.document.addEventListener("testresult", event => {
+ resolve(event.detail.result);
+ });
+ content.document.getElementById("pasteAllowed").checked = allowPaste;
+ content.document.dispatchEvent(new content.CustomEvent("teststart", {}));
+ });
+ });
+ is(result, true, "Got unexpected result from page");
+
+ // Because we call event.clipboardData.getData in the test, this causes another call to
+ // content analysis.
+ is(mockCA.calls.length, 2, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0], CLIPBOARD_TEXT_STRING);
+ assertContentAnalysisRequest(mockCA.calls[1], CLIPBOARD_TEXT_STRING);
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+function assertContentAnalysisRequest(request, expectedText) {
+ is(request.url.spec, PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, "", "request filePath should match");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+add_task(
+ async function testClipboardPasteNoFormattingWithContentAnalysisAllow() {
+ await testClipboardPasteNoFormatting(true);
+ }
+);
+
+add_task(
+ async function testClipboardPasteNoFormattingWithContentAnalysisBlock() {
+ await testClipboardPasteNoFormatting(false);
+ }
+);
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_prompt_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_prompt_content_analysis.js
new file mode 100644
index 0000000000..9e57250cc7
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_paste_prompt_content_analysis.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+});
+
+// Using an external page so the test can checks that the URL matches in the nsIContentAnalysisRequest
+const PAGE_URL =
+ "https://example.com/browser/toolkit/components/contentanalysis/tests/browser/clipboard_paste_prompt.html";
+const CLIPBOARD_TEXT_STRING = "Just some text";
+async function testClipboardPaste(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+
+ let promptPromise = SpecialPowers.spawn(browser, [], async () => {
+ return content.prompt();
+ });
+
+ let prompt = await PromptTestUtils.waitForPrompt(browser, {
+ modalType: Services.prompt.MODAL_TYPE_CONTENT,
+ });
+ // Paste text into prompt() in content
+ let pastePromise = new Promise(resolve => {
+ prompt.ui.loginTextbox.addEventListener(
+ "paste",
+ () => {
+ // Since mockCA uses setTimeout before invoking the callback,
+ // do it here too
+ setTimeout(() => {
+ resolve();
+ }, 0);
+ },
+ { once: true }
+ );
+ });
+ let ev = new ClipboardEvent("paste", {
+ dataType: "text/plain",
+ data: CLIPBOARD_TEXT_STRING,
+ });
+ prompt.ui.loginTextbox.dispatchEvent(ev);
+ await pastePromise;
+
+ // Close the prompt
+ await PromptTestUtils.handlePrompt(prompt);
+
+ let result = await promptPromise;
+ is(
+ result,
+ allowPaste ? CLIPBOARD_TEXT_STRING : "",
+ "prompt has correct value"
+ );
+ is(mockCA.calls.length, 1, "Correct number of calls to Content Analysis");
+ assertContentAnalysisRequest(mockCA.calls[0], CLIPBOARD_TEXT_STRING);
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+function assertContentAnalysisRequest(request, expectedText) {
+ is(request.url.spec, PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, null, "request filePath should match");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+add_task(async function testClipboardPasteWithContentAnalysisAllow() {
+ await testClipboardPaste(true);
+});
+
+add_task(async function testClipboardPasteWithContentAnalysisBlock() {
+ await testClipboardPaste(false);
+});
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_clipboard_read_async_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_read_async_content_analysis.js
new file mode 100644
index 0000000000..7d180a048b
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/browser_clipboard_read_async_content_analysis.js
@@ -0,0 +1,195 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let mockCA = makeMockContentAnalysis();
+
+add_setup(async function test_setup() {
+ mockCA = mockContentAnalysisService(mockCA);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ // This pref turns off the "Paste" popup
+ ["dom.events.testing.asyncClipboard", true],
+ ],
+ });
+});
+
+const PAGE_URL =
+ "https://example.com/browser/toolkit/components/contentanalysis/tests/browser/clipboard_read_async.html";
+const CLIPBOARD_TEXT_STRING = "Some plain text";
+const CLIPBOARD_HTML_STRING = "<b>Some HTML</b>";
+async function testClipboardReadAsync(allowPaste) {
+ mockCA.setupForTest(allowPaste);
+
+ setClipboardData();
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+ {
+ let result = await setDataAndStartTest(browser, allowPaste, "read");
+ is(result, true, "Got unexpected result from page for read()");
+
+ is(
+ mockCA.calls.length,
+ 2,
+ "Correct number of calls to Content Analysis for read()"
+ );
+ // On Windows, widget adds extra data into HTML clipboard.
+ let expectedHtml = navigator.platform.includes("Win")
+ ? `<html><body>\n<!--StartFragment-->${CLIPBOARD_HTML_STRING}<!--EndFragment-->\n</body>\n</html>`
+ : CLIPBOARD_HTML_STRING;
+
+ assertContentAnalysisRequest(mockCA.calls[0], expectedHtml);
+ assertContentAnalysisRequest(mockCA.calls[1], CLIPBOARD_TEXT_STRING);
+ mockCA.clearCalls();
+ }
+
+ {
+ let result = await setDataAndStartTest(browser, allowPaste, "readText");
+ is(result, true, "Got unexpected result from page for readText()");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis for read()"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0], CLIPBOARD_TEXT_STRING);
+ mockCA.clearCalls();
+ }
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+async function testClipboardReadAsyncWithErrorHelper() {
+ mockCA.setupForTestWithError(Cr.NS_ERROR_NOT_AVAILABLE);
+
+ setClipboardData();
+
+ // This test throws a number of exceptions, so tell the framework this is OK.
+ // If an exception is thrown we won't get the right response from setDataAndStartTest()
+ // so this should be safe to do.
+ ignoreAllUncaughtExceptions();
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
+ let browser = tab.linkedBrowser;
+ {
+ let result = await setDataAndStartTest(browser, false, "read", true);
+ is(result, true, "Got unexpected result from page for read()");
+
+ is(
+ mockCA.calls.length,
+ 2,
+ "Correct number of calls to Content Analysis for read()"
+ );
+ // On Windows, widget adds extra data into HTML clipboard.
+ let expectedHtml = navigator.platform.includes("Win")
+ ? `<html><body>\n<!--StartFragment-->${CLIPBOARD_HTML_STRING}<!--EndFragment-->\n</body>\n</html>`
+ : CLIPBOARD_HTML_STRING;
+
+ assertContentAnalysisRequest(mockCA.calls[0], expectedHtml);
+ assertContentAnalysisRequest(mockCA.calls[1], CLIPBOARD_TEXT_STRING);
+ mockCA.clearCalls();
+ }
+
+ {
+ let result = await setDataAndStartTest(browser, false, "readText", true);
+ is(result, true, "Got unexpected result from page for readText()");
+
+ is(
+ mockCA.calls.length,
+ 1,
+ "Correct number of calls to Content Analysis for read()"
+ );
+ assertContentAnalysisRequest(mockCA.calls[0], CLIPBOARD_TEXT_STRING);
+ mockCA.clearCalls();
+ }
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+function setDataAndStartTest(
+ browser,
+ allowPaste,
+ testType,
+ shouldError = false
+) {
+ return SpecialPowers.spawn(
+ browser,
+ [allowPaste, testType, shouldError],
+ (allowPaste, testType, shouldError) => {
+ return new Promise(resolve => {
+ content.document.addEventListener(
+ "testresult",
+ event => {
+ resolve(event.detail.result);
+ },
+ { once: true }
+ );
+ content.document.getElementById("pasteAllowed").checked = allowPaste;
+ content.document.getElementById("contentAnalysisReturnsError").checked =
+ shouldError;
+ content.document.dispatchEvent(
+ new content.CustomEvent("teststart", {
+ detail: Cu.cloneInto({ testType }, content),
+ })
+ );
+ });
+ }
+ );
+}
+
+function assertContentAnalysisRequest(request, expectedText) {
+ is(request.url.spec, PAGE_URL, "request has correct URL");
+ is(
+ request.analysisType,
+ Ci.nsIContentAnalysisRequest.eBulkDataEntry,
+ "request has correct analysisType"
+ );
+ is(
+ request.operationTypeForDisplay,
+ Ci.nsIContentAnalysisRequest.eClipboard,
+ "request has correct operationTypeForDisplay"
+ );
+ is(request.filePath, "", "request filePath should match");
+ is(request.textContent, expectedText, "request textContent should match");
+ is(request.printDataHandle, 0, "request printDataHandle should not be 0");
+ is(request.printDataSize, 0, "request printDataSize should not be 0");
+ ok(!!request.requestToken.length, "request requestToken should not be empty");
+}
+
+function setClipboardData() {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ {
+ trans.addDataFlavor("text/plain");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = CLIPBOARD_TEXT_STRING;
+ trans.setTransferData("text/plain", str);
+ }
+ {
+ trans.addDataFlavor("text/html");
+ const str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ str.data = CLIPBOARD_HTML_STRING;
+ trans.setTransferData("text/html", str);
+ }
+
+ // Write to clipboard.
+ Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+}
+
+add_task(async function testClipboardReadAsyncWithContentAnalysisAllow() {
+ await testClipboardReadAsync(true);
+});
+
+add_task(async function testClipboardReadAsyncWithContentAnalysisBlock() {
+ await testClipboardReadAsync(false);
+});
+
+add_task(async function testClipboardReadAsyncWithError() {
+ await testClipboardReadAsyncWithErrorHelper();
+});
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 b226c0a37a..e7122508e1 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
+++ b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js
@@ -25,7 +25,8 @@ const kAgentNamePref = "agent_name";
const kClientSignaturePref = "client_signature";
const kPerUserPref = "is_per_user";
const kShowBlockedPref = "show_blocked_result";
-const kDefaultAllowPref = "default_allow";
+const kDefaultResultPref = "default_result";
+const kBypassForSameTabOperationsPref = "bypass_for_same_tab_operations";
const ca = Cc["@mozilla.org/contentanalysis;1"].getService(
Ci.nsIContentAnalysis
@@ -87,7 +88,8 @@ add_task(async function test_ca_enterprise_config() {
ClientSignature: string4,
IsPerUser: true,
ShowBlockedResult: false,
- DefaultAllow: true,
+ DefaultResult: 1,
+ BypassForSameTabOperations: true,
},
},
});
@@ -135,9 +137,16 @@ add_task(async function test_ca_enterprise_config() {
"show blocked match"
);
is(
- Services.prefs.getBoolPref("browser.contentanalysis." + kDefaultAllowPref),
+ Services.prefs.getIntPref("browser.contentanalysis." + kDefaultResultPref),
+ 1,
+ "default result match"
+ );
+ is(
+ Services.prefs.getBoolPref(
+ "browser.contentanalysis." + kBypassForSameTabOperationsPref
+ ),
true,
- "default allow match"
+ "bypass for same tab operations match"
);
PoliciesPrefTracker.stop();
});
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
index 72a7dcbb91..0f2d846627 100644
--- 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
@@ -12,69 +12,7 @@ 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);
- },
-};
+let mockCA = makeMockContentAnalysis();
add_setup(async function test_setup() {
mockCA = mockContentAnalysisService(mockCA);
diff --git a/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
index 9b4c0ffa60..05897b5ca6 100644
--- a/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
+++ b/toolkit/components/contentanalysis/tests/browser/browser_print_content_analysis.js
@@ -12,73 +12,7 @@ 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);
- },
-};
+let mockCA = makeMockContentAnalysis();
add_setup(async function test_setup() {
mockCA = mockContentAnalysisService(mockCA);
diff --git a/toolkit/components/contentanalysis/tests/browser/clipboard_paste_file.html b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_file.html
new file mode 100644
index 0000000000..9604633842
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_file.html
@@ -0,0 +1,61 @@
+<html><body>
+<script>
+async function checkPaste(event) {
+ let result = null;
+ try {
+ result = await checkPasteHelper(event);
+ } catch (e) {
+ result = e.toString();
+ }
+
+ document.dispatchEvent(new CustomEvent('testresult', {
+ detail: { result }
+ }));
+}
+
+function is(a, b, msg) {
+ if (!Object.is(a, b)) {
+ throw new Error(`FAIL: expected ${b} got ${a} - ${msg}`);
+ }
+}
+
+async function checkPasteHelper(event) {
+ let dt = event.clipboardData;
+ // Set by injected JS in the test
+ let filePasteAllowed = document.getElementById("pasteAllowed").checked;
+
+ is(dt.types.length, 2, "Correct number of types");
+
+ // TODO: Remove application/x-moz-file from content.
+ is(dt.types[0], "application/x-moz-file", "First type")
+ is(dt.types[1], "Files", "Last type must be Files");
+
+ is(dt.getData("text/plain"), "", "text/plain found with getData");
+ is(dt.getData("application/x-moz-file"), "", "application/x-moz-file found with getData");
+
+ if (!filePasteAllowed) {
+ is(dt.files.length, 0, "No files");
+ } else {
+ is(dt.files.length, 1, "Correct number of files");
+ is(dt.files[0].type, "text/plain", "Correct file type");
+ }
+ is(dt.items.length, 1, "Correct number of items");
+ is(dt.items[0].kind, "file", "Correct item kind");
+ if (!filePasteAllowed) {
+ is(dt.items[0].type, "application/x-moz-file", "Correct item type");
+ return "";
+ }
+ is(dt.items[0].type, "text/plain", "Correct item type");
+
+ let file = dt.files[0];
+ is(await file.text(), "Hello World!", "Pasted file contains right text");
+
+ return file.name;
+}
+</script>
+
+<input id="input" onpaste="checkPaste(event)">
+
+<label for="pasteAllowed">Paste allowed?</label><input id="pasteAllowed" type="checkbox">
+
+</body></html>
diff --git a/toolkit/components/contentanalysis/tests/browser/clipboard_paste_inputandtextarea.html b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_inputandtextarea.html
new file mode 100644
index 0000000000..db93e85955
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_inputandtextarea.html
@@ -0,0 +1,41 @@
+<html>
+<body>
+
+<div id="content">
+ <input
+ id="testInput"
+ type="text" onpaste="handlePaste(event)">
+ <textarea id="testTextArea" onpaste="handlePaste(event)"></textarea>
+
+ <label for="pasteAllowed">Paste allowed?</label><input id="pasteAllowed" type="checkbox">
+</div>
+<script class="testbody" type="application/javascript">
+function is(a, b, msg) {
+ if (!Object.is(a, b)) {
+ throw new Error(`FAIL: expected ${b} got ${a} - ${msg}`);
+ }
+}
+
+function checkPasteHelper(event) {
+ // Set by injected JS in the test
+ let filePasteAllowed = document.getElementById("pasteAllowed").checked;
+ is(event.clipboardData.getData('text/plain'), filePasteAllowed ? "Just some text" : "", "getData(text/plain) should return plain text");
+ is(event.clipboardData.types.length, 1, "Correct number of types");
+}
+
+function handlePaste(e) {
+ let result = null;
+ try {
+ result = checkPasteHelper(e);
+ } catch (e) {
+ result = e.toString();
+ }
+
+ document.dispatchEvent(new CustomEvent('testresult', {
+ detail: { result }
+ }));
+}
+</script>
+
+</body>
+</html>
diff --git a/toolkit/components/contentanalysis/tests/browser/clipboard_paste_noformatting.html b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_noformatting.html
new file mode 100644
index 0000000000..eefc40de85
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_noformatting.html
@@ -0,0 +1,46 @@
+<html>
+<body>
+
+<script class="testbody" type="application/javascript">
+function is(a, b, msg) {
+ if (!Object.is(a, b)) {
+ throw new Error(`FAIL: expected ${b} got ${a} - ${msg}`);
+ }
+}
+
+function checkPasteHelper(event) {
+ is(event.clipboardData.types.indexOf('text/html'), -1, "clipboardData shouldn't have text/html");
+ // Set by injected JS in the test
+ let filePasteAllowed = document.getElementById("pasteAllowed").checked;
+ is(event.clipboardData.getData('text/plain'), filePasteAllowed ? "Some text" : "", "getData(text/plain) should return plain text");
+ return true;
+}
+
+window.addEventListener("paste", e => {
+ let result = null;
+ try {
+ result = checkPasteHelper(e);
+ } catch (e) {
+ result = e.toString();
+ }
+
+ document.dispatchEvent(new CustomEvent('testresult', {
+ detail: { result }
+ }));
+});
+
+document.addEventListener("teststart", _e => {
+ let editable = document.getElementById("editable1");
+ editable.focus();
+
+ window.getSelection().selectAllChildren(editable);
+
+ SpecialPowers.doCommand(window, "cmd_pasteNoFormatting");
+});
+</script>
+
+<div contenteditable="true" id="editable1"><b>Formatted Text</b><br></div>
+
+<label for="pasteAllowed">Paste allowed?</label><input id="pasteAllowed" type="checkbox">
+</body>
+</html>
diff --git a/toolkit/components/contentanalysis/tests/browser/clipboard_paste_prompt.html b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_prompt.html
new file mode 100644
index 0000000000..8017cc87a1
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/clipboard_paste_prompt.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+
+<div id="content"></div>
+
+</body>
+</html>
diff --git a/toolkit/components/contentanalysis/tests/browser/clipboard_read_async.html b/toolkit/components/contentanalysis/tests/browser/clipboard_read_async.html
new file mode 100644
index 0000000000..14414a0f28
--- /dev/null
+++ b/toolkit/components/contentanalysis/tests/browser/clipboard_read_async.html
@@ -0,0 +1,95 @@
+<html>
+<body>
+
+<!--<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>-->
+<script class="testbody" type="application/javascript">
+function is(a, b, msg) {
+ if (!Object.is(a, b)) {
+ throw new Error(`FAIL: expected ${b} got ${a} - ${msg}`);
+ }
+}
+
+function isNotAllowedException(ex) {
+ return /NS_ERROR_CONTENT_BLOCKED/.test(ex.toString()) ||
+ /DataError/.test(ex.toString()) ||
+ /NotAllowedError/.test(ex.toString());
+}
+
+async function checkReadHelper(readResult, gotNotAllowedException, isReadTextTest) {
+ // Set by injected JS in the test
+ let filePasteAllowed = document.getElementById("pasteAllowed").checked;
+ let contentAnalysisReturnsError = document.getElementById("contentAnalysisReturnsError").checked;
+ filePasteAllowed = filePasteAllowed && !contentAnalysisReturnsError;
+ is(gotNotAllowedException, !filePasteAllowed && isReadTextTest, "Should get exception from readText() if not allowed");
+ if (isReadTextTest) {
+ is(readResult, filePasteAllowed ? "Some plain text" : null, "Should get expected text from clipboard.readText()");
+ }
+ else {
+ is(readResult.length, 1, "check number of ClipboardItems in response");
+ is(readResult[0].types.length, 2, "check number of types in ClipboardItem");
+
+ {
+ let text = null;
+ let gotNotAllowedException = false;
+ try {
+ let textBlob = await readResult[0].getType("text/plain");
+ text = await textBlob.text();
+ } catch (ex) {
+ gotNotAllowedException = isNotAllowedException(ex);
+ }
+ is(gotNotAllowedException, !filePasteAllowed, "should get exception from reading text data when blocked");
+ if (filePasteAllowed) {
+ is(text, "Some plain text", "check text/plain data");
+ }
+ }
+
+ {
+ let html = null;
+ let gotNotAllowedException = false;
+ try {
+ let htmlBlob = await readResult[0].getType("text/html");
+ html = await htmlBlob.text();
+ } catch (ex) {
+ gotNotAllowedException = isNotAllowedException(ex);
+ }
+ is(gotNotAllowedException, !filePasteAllowed, "should get exception from reading html data when blocked");
+ if (filePasteAllowed) {
+ const CLIPBOARD_HTML_STRING = "<b>Some HTML</b>";
+ let expectedHtml = navigator.platform.includes("Win")
+ ? `<html><body>\n<!--StartFragment-->${CLIPBOARD_HTML_STRING}<!--EndFragment-->\n</body>\n</html>`
+ : CLIPBOARD_HTML_STRING;
+ is(html, expectedHtml, "check text/html data");
+ }
+ }
+ }
+ return true;
+}
+
+document.addEventListener("teststart", async e => {
+ let isReadTextTest = e.detail.testType == "readText";
+ let gotNotAllowedException = false;
+ let readResult = null;
+ try {
+ let readPromise = isReadTextTest ? navigator.clipboard.readText() : navigator.clipboard.read();
+ readResult = await readPromise;
+ } catch (ex) {
+ gotNotAllowedException = isNotAllowedException(ex);
+ }
+
+ let result = null;
+ try {
+ result = checkReadHelper(readResult, gotNotAllowedException, isReadTextTest);
+ } catch (ex) {
+ result = ex.toString();
+ }
+
+ document.dispatchEvent(new CustomEvent('testresult', {
+ detail: { result }
+ }));
+});
+</script>
+
+<label for="pasteAllowed">Paste allowed?</label><input id="pasteAllowed" type="checkbox">
+<label for="contentAnalysisReturnsError">Content Analysis returns error?</label><input id="contentAnalysisReturnsError" type="checkbox">
+</body>
+</html>
diff --git a/toolkit/components/contentanalysis/tests/browser/head.js b/toolkit/components/contentanalysis/tests/browser/head.js
index e645caa2d7..9422a62ff2 100644
--- a/toolkit/components/contentanalysis/tests/browser/head.js
+++ b/toolkit/components/contentanalysis/tests/browser/head.js
@@ -112,3 +112,127 @@ async function waitForFileToAlmostMatchSize(filePath, expectedSize) {
return Math.abs(fileStat.size - expectedSize) <= maxSizeDifference;
}, "Sizes should (almost) match");
}
+
+function makeMockContentAnalysis() {
+ return {
+ 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);
+ },
+ };
+}
+
+function whenTabLoaded(aTab, aCallback) {
+ promiseTabLoadEvent(aTab).then(aCallback);
+}
+
+function promiseTabLoaded(aTab) {
+ return new Promise(resolve => {
+ whenTabLoaded(aTab, resolve);
+ });
+}
+
+/**
+ * Waits for a load (or custom) event to finish in a given tab. If provided
+ * load an uri into the tab.
+ *
+ * @param {object} tab
+ * The tab to load into.
+ * @param {string} [url]
+ * The url to load, or the current url.
+ * @returns {Promise<string>} resolved when the event is handled. Rejected if
+ * a valid load event is not received within a meaningful interval
+ */
+function promiseTabLoadEvent(tab, url) {
+ info("Wait tab event: load");
+
+ function handle(loadedUrl) {
+ if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
+ info(`Skipping spurious load event for ${loadedUrl}`);
+ return false;
+ }
+
+ info("Tab event received: load");
+ return true;
+ }
+
+ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
+
+ if (url) {
+ BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
+ }
+
+ return loaded;
+}
+
+function promisePopupShown(popup) {
+ return BrowserTestUtils.waitForPopupEvent(popup, "shown");
+}
+
+function promisePopupHidden(popup) {
+ return BrowserTestUtils.waitForPopupEvent(popup, "hidden");
+}