summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutthirdparty/tests/browser
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/aboutthirdparty/tests/browser')
-rw-r--r--toolkit/components/aboutthirdparty/tests/browser/browser.toml6
-rw-r--r--toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js317
-rw-r--r--toolkit/components/aboutthirdparty/tests/browser/head.js144
-rw-r--r--toolkit/components/aboutthirdparty/tests/browser/hello.zzz1
4 files changed, 468 insertions, 0 deletions
diff --git a/toolkit/components/aboutthirdparty/tests/browser/browser.toml b/toolkit/components/aboutthirdparty/tests/browser/browser.toml
new file mode 100644
index 0000000000..8a2d9db429
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/browser/browser.toml
@@ -0,0 +1,6 @@
+[DEFAULT]
+head = "head.js"
+
+["browser_aboutthirdparty.js"]
+support-files = ["hello.zzz"]
+skip-if = ["os == 'win'"] # Bug 1776048
diff --git a/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js b/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js
new file mode 100644
index 0000000000..0a53823a35
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/browser/browser_aboutthirdparty.js
@@ -0,0 +1,317 @@
+/* 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/. */
+
+// Return card containers matching a given name
+function getCardsByName(aContainer, aLeafName) {
+ const matchedCards = [];
+ const allCards = aContainer.querySelectorAll(".card");
+ for (const card of allCards) {
+ const nameLabel = card.querySelector(".module-name");
+ if (nameLabel.textContent == aLeafName) {
+ matchedCards.push(card);
+ }
+ }
+ return matchedCards;
+}
+
+function getDetailRow(aContainer, aLabel) {
+ return aContainer.querySelector(`[data-l10n-id=${aLabel}]`).parentElement;
+}
+
+function verifyClipboardData(aModuleJson) {
+ Assert.ok(
+ aModuleJson.hasOwnProperty("blocked"),
+ "Clipboard data should have blocked property."
+ );
+ const blocked = aModuleJson.blocked.filter(
+ x => x.name == kUserBlockedModuleName
+ );
+ Assert.equal(
+ blocked.length,
+ 1,
+ "Blocked array should contain the blocked module one time."
+ );
+ Assert.ok(
+ aModuleJson.hasOwnProperty("modules"),
+ "Clipboard data should have modules property"
+ );
+ let aModuleArray = aModuleJson.modules;
+ const filtered = aModuleArray.filter(x => x.name == kExtensionModuleName);
+ Assert.equal(filtered.length, 1, "No duplicate data for the module.");
+
+ const kDeletedPropertiesOfModule = [
+ "application",
+ "dllFile",
+ "loadingOnMain",
+ "trustFlags",
+ ];
+ const kDeletedPropertiesOfLoadingEvent = [
+ "baseAddress",
+ "isDependent",
+ "mainThread",
+ "moduleIndex",
+ "processUptimeMS",
+ ];
+
+ for (const module of aModuleArray) {
+ for (const deletedProperty of kDeletedPropertiesOfModule) {
+ Assert.ok(
+ !module.hasOwnProperty(deletedProperty),
+ `The property \`${deletedProperty}\` is deleted.`
+ );
+ }
+
+ Assert.ok(
+ !module.hasOwnProperty("typeFlags") || module.typeFlags != 0,
+ "typeFlags does not exist or is non-zero."
+ );
+
+ for (const event of module.events) {
+ for (const deletedProperty of kDeletedPropertiesOfLoadingEvent) {
+ Assert.ok(
+ !event.hasOwnProperty(deletedProperty),
+ `The property \`${deletedProperty}\` is deleted.`
+ );
+ }
+ }
+ }
+}
+
+function verifyModuleSorting(compareFunc) {
+ const uninteresting = {
+ typeFlags: 0,
+ isCrasher: false,
+ loadingOnMain: 0,
+ };
+ const crasherNotBlocked = { ...uninteresting, isCrasher: true };
+ const crasherBlocked = {
+ ...uninteresting,
+ isCrasher: true,
+ typeFlags: Ci.nsIAboutThirdParty.ModuleType_BlockedByUserAtLaunch,
+ };
+ const justBlocked = {
+ ...uninteresting,
+ typeFlags: Ci.nsIAboutThirdParty.ModuleType_BlockedByUserAtLaunch,
+ };
+ const uninterestingButSlow = {
+ ...uninteresting,
+ loadingOnMain: 10,
+ };
+ let modules = [
+ uninteresting,
+ uninterestingButSlow,
+ crasherNotBlocked,
+ justBlocked,
+ crasherBlocked,
+ ];
+ modules.sort(compareFunc);
+ Assert.equal(
+ JSON.stringify([
+ crasherBlocked,
+ justBlocked,
+ crasherNotBlocked,
+ uninterestingButSlow,
+ uninteresting,
+ ]),
+ JSON.stringify(modules),
+ "Modules sort in expected order"
+ );
+}
+
+add_task(async () => {
+ registerCleanupFunction(() => {
+ unregisterAll();
+ });
+ await registerObject();
+ registerExtensions();
+ loadShellExtension();
+
+ await kATP.collectSystemInfo();
+ Assert.equal(
+ kATP.lookupModuleType(kExtensionModuleName),
+ Ci.nsIAboutThirdParty.ModuleType_ShellExtension,
+ "lookupModuleType() returns a correct type " +
+ "after system info was collected."
+ );
+
+ await BrowserTestUtils.withNewTab("about:third-party", async browser => {
+ if (!content.fetchDataDone) {
+ const mainDiv = content.document.getElementById("main");
+ await BrowserTestUtils.waitForMutationCondition(
+ mainDiv,
+ { childList: true },
+ () => mainDiv.childElementCount > 0
+ );
+ Assert.ok(content.fetchDataDone, "onLoad() is completed.");
+ }
+
+ const reload = content.document.getElementById("button-reload");
+ if (!reload.hidden) {
+ reload.click();
+ await BrowserTestUtils.waitForMutationCondition(
+ reload,
+ { attributes: true, attributeFilter: ["hidden"] },
+ () => reload.hidden
+ );
+ }
+
+ Assert.ok(
+ content.document.getElementById("no-data").hidden,
+ "The no-data message is hidden."
+ );
+ const blockedCards = getCardsByName(
+ content.document,
+ kUserBlockedModuleName
+ );
+ Assert.equal(
+ blockedCards.length,
+ 1,
+ "Only one card matching the blocked module exists."
+ );
+ const blockedCard = blockedCards[0];
+ Assert.equal(
+ blockedCard.querySelectorAll(".button-block.module-blocked").length,
+ 1,
+ "The blocked module has a button indicating it is blocked"
+ );
+ let blockedBlockButton = blockedCard.querySelector(
+ ".button-block.module-blocked"
+ );
+ Assert.equal(
+ blockedBlockButton.getAttribute("data-l10n-id"),
+ "third-party-button-to-unblock-module",
+ "Button to block the module has correct title"
+ );
+ blockedBlockButton.click();
+ await BrowserTestUtils.promiseAlertDialogOpen("cancel");
+ Assert.ok(
+ !blockedBlockButton.classList.contains("module-blocked"),
+ "After clicking to unblock a module, button should not have module-blocked class."
+ );
+ Assert.equal(
+ blockedBlockButton.getAttribute("data-l10n-id"),
+ "third-party-button-to-block-module",
+ "After clicking to unblock a module, button should have correct title."
+ );
+ // Restore this to blocked for later tests
+ blockedBlockButton.click();
+ await BrowserTestUtils.promiseAlertDialogOpen("cancel");
+ Assert.ok(
+ blockedBlockButton.classList.contains("module-blocked"),
+ "After clicking to block a module, button should have module-blocked class."
+ );
+ Assert.equal(
+ blockedBlockButton.getAttribute("data-l10n-id"),
+ "third-party-button-to-unblock-module",
+ "After clicking to block a module, button should have correct title."
+ );
+
+ const cards = getCardsByName(content.document, kExtensionModuleName);
+ Assert.equal(cards.length, 1, "Only one card matching the module exists.");
+ const card = cards[0];
+
+ const blockButton = card.querySelector(".button-block");
+ Assert.ok(
+ !blockButton.classList.contains("blocklist-disabled"),
+ "Button to block the module does not indicate the blocklist is disabled."
+ );
+ Assert.ok(
+ !blockButton.classList.contains("module-blocked"),
+ "Button to block the module does not indicate the module is blocked."
+ );
+
+ Assert.ok(
+ card.querySelector(".image-warning").hidden,
+ "No warning sign for the module."
+ );
+ Assert.equal(
+ card.querySelector(".image-unsigned").hidden,
+ false,
+ "The module is labeled as unsigned."
+ );
+ Assert.equal(
+ card.querySelector(".tag-shellex").hidden,
+ false,
+ "The module is labeled as a shell extension."
+ );
+ Assert.equal(
+ card.querySelector(".tag-ime").hidden,
+ true,
+ "The module is not labeled as an IME."
+ );
+
+ const versionRow = getDetailRow(card, "third-party-detail-version");
+ Assert.equal(
+ versionRow.childNodes[1].textContent,
+ "1.2.3.4",
+ "The version matches a value in TestShellEx.rc."
+ );
+ const vendorRow = getDetailRow(card, "third-party-detail-vendor");
+ Assert.equal(
+ vendorRow.childNodes[1].textContent,
+ "Mozilla Corporation",
+ "The vendor name matches a value in TestShellEx.rc."
+ );
+ const occurrencesRow = getDetailRow(card, "third-party-detail-occurrences");
+ Assert.equal(
+ Number(occurrencesRow.childNodes[1].textContent),
+ 1,
+ "The module was loaded once."
+ );
+ const durationRow = getDetailRow(card, "third-party-detail-duration");
+ Assert.ok(
+ Number(durationRow.childNodes[1].textContent),
+ "The duration row shows a valid number."
+ );
+
+ const eventTable = card.querySelector(".event-table");
+ const tableCells = eventTable.querySelectorAll("td");
+ Assert.equal(
+ tableCells.length,
+ 3,
+ "The table has three cells as there is only one event."
+ );
+ Assert.equal(
+ tableCells[0].querySelector(".process-type").getAttribute("data-l10n-id"),
+ "process-type-default",
+ "The module was loaded into the main process."
+ );
+ Assert.ok(
+ Number(tableCells[0].querySelector(".process-id").textContent),
+ "A valid process ID is displayed."
+ );
+ Assert.equal(
+ tableCells[1].querySelector(".event-duration").textContent,
+ durationRow.childNodes[1].textContent,
+ "The event's duration is the same as the average " +
+ "as there is only one event."
+ );
+ Assert.equal(
+ tableCells[1].querySelector(".tag-background").hidden,
+ true,
+ "The icon handler is loaded in the main thread."
+ );
+ Assert.equal(
+ tableCells[2].getAttribute("data-l10n-id"),
+ "third-party-status-loaded",
+ "The module was really loaded without being blocked."
+ );
+
+ const button = content.document.getElementById("button-copy-to-clipboard");
+ button.click();
+
+ // Wait until copying is done and the button becomes clickable.
+ await BrowserTestUtils.waitForMutationCondition(
+ button,
+ { attributes: true },
+ () => !button.disabled
+ );
+
+ const copiedJSON = JSON.parse(await navigator.clipboard.readText());
+ Assert.ok(copiedJSON instanceof Object, "Data is an object.");
+ verifyClipboardData(copiedJSON);
+
+ verifyModuleSorting(content.moduleCompareForDisplay);
+ });
+});
diff --git a/toolkit/components/aboutthirdparty/tests/browser/head.js b/toolkit/components/aboutthirdparty/tests/browser/head.js
new file mode 100644
index 0000000000..eba3948046
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/browser/head.js
@@ -0,0 +1,144 @@
+/* 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/. */
+
+"use strict";
+
+const kClsidTestShellEx = "{10a9521e-0205-4cc7-93a1-62f30a9a54b3}";
+const kFriendlyName = "Minimum Shell Extension for Firefox testing";
+const kExtensionSubkeys = [".zzz\\shellex\\IconHandler"];
+const kExtensionModuleName = "TestShellEx.dll";
+const kUserBlockedModuleName = "TestDllBlocklist_UserBlocked.dll";
+const kFileFilterInDialog = "*.zzz";
+const kATP = Cc["@mozilla.org/about-thirdparty;1"].getService(
+ Ci.nsIAboutThirdParty
+);
+
+function loadShellExtension() {
+ // This method call opens the file dialog and shows the support file
+ // "hello.zzz" in it, which loads TestShellEx.dll to show an icon
+ // for files with the .zzz extension.
+ kATP.openAndCloseFileDialogForTesting(
+ kExtensionModuleName,
+ getTestFilePath(""),
+ kFileFilterInDialog
+ );
+}
+
+async function registerObject() {
+ const reg = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+
+ reg.create(
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Classes\\CLSID\\" + kClsidTestShellEx,
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ reg.writeStringValue("", kFriendlyName);
+
+ const inprocServer = reg.createChild(
+ "InprocServer32",
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ const moduleFullPath = getTestFilePath(kExtensionModuleName);
+ Assert.ok(await IOUtils.exists(moduleFullPath), "The module file exists.");
+
+ inprocServer.writeStringValue("", moduleFullPath);
+ inprocServer.writeStringValue("ThreadingModel", "Apartment");
+ reg.close();
+
+ info("registerObject() done - " + moduleFullPath);
+}
+
+function registerExtensions() {
+ for (const subkey of kExtensionSubkeys) {
+ const reg = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+ reg.create(
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Classes\\" + subkey,
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ let currentExtension = "";
+ try {
+ // If the key was just created above, the default value does not exist,
+ // so readStringValue will throw NS_ERROR_FAILURE.
+ currentExtension = reg.readStringValue("");
+ } catch (e) {}
+
+ try {
+ if (!currentExtension) {
+ reg.writeStringValue("", kClsidTestShellEx);
+ } else if (currentExtension != kClsidTestShellEx) {
+ throw new Error(
+ `Another extension \`${currentExtension}\` has been registered.`
+ );
+ }
+ } catch (e) {
+ throw new Error("Failed to register TestShellEx.dll: " + e);
+ } finally {
+ reg.close();
+ }
+ }
+}
+
+function unregisterAll() {
+ for (const subkey of kExtensionSubkeys) {
+ const reg = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+
+ try {
+ reg.open(
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Classes\\" + subkey,
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ if (reg.readStringValue("") != kClsidTestShellEx) {
+ // If another extension is registered, don't overwrite it.
+ continue;
+ }
+
+ // Set an empty string instead of deleting the key
+ // not to touch non-default values.
+ reg.writeStringValue("", "");
+ } catch (e) {
+ info(`Failed to unregister \`${subkey}\`: ` + e);
+ } finally {
+ reg.close();
+ }
+ }
+
+ const reg = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+ reg.open(
+ Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "Software\\Classes\\CLSID",
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+
+ try {
+ const child = reg.openChild(
+ kClsidTestShellEx,
+ Ci.nsIWindowsRegKey.ACCESS_ALL
+ );
+ try {
+ child.removeChild("InprocServer32");
+ } catch (e) {
+ } finally {
+ child.close();
+ }
+
+ reg.removeChild(kClsidTestShellEx);
+ } catch (e) {
+ } finally {
+ reg.close();
+ }
+}
diff --git a/toolkit/components/aboutthirdparty/tests/browser/hello.zzz b/toolkit/components/aboutthirdparty/tests/browser/hello.zzz
new file mode 100644
index 0000000000..e69ab604b3
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/browser/hello.zzz
@@ -0,0 +1 @@
+Showing this file on Windows Shell loads TestShellEx.dll.