summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/plugins
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/base/content/test/plugins/.eslintrc.js5
-rw-r--r--browser/base/content/test/plugins/BlocklistTestProxy.jsm88
-rw-r--r--browser/base/content/test/plugins/browser.ini25
-rw-r--r--browser/base/content/test/plugins/browser_CTP_favorfallback.js104
-rw-r--r--browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js122
-rw-r--r--browser/base/content/test/plugins/browser_CTP_zoom.js61
-rw-r--r--browser/base/content/test/plugins/browser_bug797677.js45
-rw-r--r--browser/base/content/test/plugins/browser_enable_DRM_prompt.js228
-rw-r--r--browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js63
-rw-r--r--browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js59
-rw-r--r--browser/base/content/test/plugins/empty_file.html9
-rw-r--r--browser/base/content/test/plugins/head.js452
-rw-r--r--browser/base/content/test/plugins/plugin_bug797677.html5
-rw-r--r--browser/base/content/test/plugins/plugin_favorfallback.html96
-rw-r--r--browser/base/content/test/plugins/plugin_outsideScrollArea.html25
-rw-r--r--browser/base/content/test/plugins/plugin_simple_blank.swfbin0 -> 37 bytes
-rw-r--r--browser/base/content/test/plugins/plugin_test.html9
-rw-r--r--browser/base/content/test/plugins/plugin_zoom.html10
18 files changed, 1406 insertions, 0 deletions
diff --git a/browser/base/content/test/plugins/.eslintrc.js b/browser/base/content/test/plugins/.eslintrc.js
new file mode 100644
index 0000000000..1779fd7f1c
--- /dev/null
+++ b/browser/base/content/test/plugins/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/browser-test"],
+};
diff --git a/browser/base/content/test/plugins/BlocklistTestProxy.jsm b/browser/base/content/test/plugins/BlocklistTestProxy.jsm
new file mode 100644
index 0000000000..cd13352f4a
--- /dev/null
+++ b/browser/base/content/test/plugins/BlocklistTestProxy.jsm
@@ -0,0 +1,88 @@
+var EXPORTED_SYMBOLS = ["BlocklistTestProxyChild"];
+
+var Cm = Components.manager;
+
+const kBlocklistServiceUUID = "{66354bc9-7ed1-4692-ae1d-8da97d6b205e}";
+const kBlocklistServiceContractID = "@mozilla.org/extensions/blocklist;1";
+
+let existingBlocklistFactory = null;
+try {
+ existingBlocklistFactory = Cm.getClassObject(
+ Cc[kBlocklistServiceContractID],
+ Ci.nsIFactory
+ );
+} catch (ex) {}
+
+const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+
+/*
+ * A lightweight blocklist proxy for testing purposes.
+ */
+var BlocklistProxy = {
+ _uuid: null,
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIObserver",
+ "nsIBlocklistService",
+ "nsITimerCallback",
+ ]),
+
+ init() {
+ if (!this._uuid) {
+ this._uuid = Cc["@mozilla.org/uuid-generator;1"]
+ .getService(Ci.nsIUUIDGenerator)
+ .generateUUID();
+ Cm.nsIComponentRegistrar.registerFactory(
+ this._uuid,
+ "",
+ "@mozilla.org/extensions/blocklist;1",
+ this
+ );
+ }
+ },
+
+ uninit() {
+ if (this._uuid) {
+ Cm.nsIComponentRegistrar.unregisterFactory(this._uuid, this);
+ if (existingBlocklistFactory) {
+ Cm.nsIComponentRegistrar.registerFactory(
+ Components.ID(kBlocklistServiceUUID),
+ "Blocklist Service",
+ "@mozilla.org/extensions/blocklist;1",
+ existingBlocklistFactory
+ );
+ }
+ this._uuid = null;
+ }
+ },
+
+ notify(aTimer) {},
+
+ async getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) {
+ await new Promise(r => setTimeout(r, 150));
+ return 0; // STATE_NOT_BLOCKED
+ },
+
+ async getPluginBlocklistState(aPluginTag, aAppVersion, aToolkitVersion) {
+ await new Promise(r => setTimeout(r, 150));
+ return 0; // STATE_NOT_BLOCKED
+ },
+
+ async getPluginBlockURL(aPluginTag) {
+ await new Promise(r => setTimeout(r, 150));
+ return "";
+ },
+};
+
+class BlocklistTestProxyChild extends JSProcessActorChild {
+ constructor() {
+ super();
+ BlocklistProxy.init();
+ }
+
+ receiveMessage(message) {
+ if (message.name == "unload") {
+ BlocklistProxy.uninit();
+ }
+ }
+}
diff --git a/browser/base/content/test/plugins/browser.ini b/browser/base/content/test/plugins/browser.ini
new file mode 100644
index 0000000000..61898167f7
--- /dev/null
+++ b/browser/base/content/test/plugins/browser.ini
@@ -0,0 +1,25 @@
+[DEFAULT]
+prefs =
+ plugin.load_flash_only=false
+support-files =
+ BlocklistTestProxy.jsm
+ empty_file.html
+ head.js
+ plugin_bug797677.html
+ plugin_favorfallback.html
+ plugin_outsideScrollArea.html
+ plugin_simple_blank.swf
+ plugin_test.html
+ plugin_zoom.html
+
+[browser_bug797677.js]
+[browser_CTP_favorfallback.js]
+[browser_CTP_outsideScrollArea.js]
+tags = blocklist
+[browser_CTP_zoom.js]
+tags = blocklist
+[browser_enable_DRM_prompt.js]
+skip-if = (os == 'win' && processor == 'aarch64') # bug 1533164
+[browser_private_browsing_eme_persistent_state.js]
+[browser_globalplugin_crashinfobar.js]
+skip-if = !crashreporter
diff --git a/browser/base/content/test/plugins/browser_CTP_favorfallback.js b/browser/base/content/test/plugins/browser_CTP_favorfallback.js
new file mode 100644
index 0000000000..ba3af78f57
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_CTP_favorfallback.js
@@ -0,0 +1,104 @@
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir.replace(
+ "chrome://mochitests/content/",
+ "http://127.0.0.1:8888/"
+);
+var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+
+add_task(async function() {
+ registerCleanupFunction(function() {
+ clearAllPluginPermissions();
+ Services.prefs.clearUserPref("plugins.favorfallback.mode");
+ Services.prefs.clearUserPref("plugins.favorfallback.rules");
+ });
+});
+
+add_task(async function() {
+ Services.prefs.setCharPref("plugins.favorfallback.mode", "follow-ctp");
+});
+
+/* The expected behavior of each testcase is documented with its markup
+ * in plugin_favorfallback.html.
+ *
+ * - "name" is the name of the testcase in the test file.
+ * - "rule" is how the plugins.favorfallback.rules must be configured
+ * for this testcase.
+ */
+const testcases = [
+ {
+ name: "video",
+ rule: "video",
+ },
+
+ {
+ name: "nosrc",
+ rule: "nosrc",
+ },
+
+ {
+ name: "embed",
+ rule: "embed,true",
+ },
+
+ {
+ name: "adobelink",
+ rule: "adobelink,true",
+ },
+
+ {
+ name: "installinstructions",
+ rule: "installinstructions,true",
+ },
+];
+
+add_task(async function() {
+ for (let testcase of Object.values(testcases)) {
+ info(`Running testcase ${testcase.name}`);
+
+ Services.prefs.setCharPref("plugins.favorfallback.rules", testcase.rule);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `${gTestRoot}plugin_favorfallback.html?testcase=${testcase.name}`
+ );
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [testcase.name],
+ async function testPlugins(name) {
+ let testcaseDiv = content.document.getElementById(`testcase_${name}`);
+ let ctpPlugins = testcaseDiv.querySelectorAll(".expected_ctp");
+
+ for (let ctpPlugin of ctpPlugins) {
+ ok(
+ ctpPlugin instanceof Ci.nsIObjectLoadingContent,
+ "This is a plugin object"
+ );
+ is(
+ ctpPlugin.pluginFallbackType,
+ Ci.nsIObjectLoadingContent.PLUGIN_ALTERNATE,
+ "Plugins always use alternate content"
+ );
+ }
+
+ let fallbackPlugins = testcaseDiv.querySelectorAll(
+ ".expected_fallback"
+ );
+
+ for (let fallbackPlugin of fallbackPlugins) {
+ ok(
+ fallbackPlugin instanceof Ci.nsIObjectLoadingContent,
+ "This is a plugin object"
+ );
+ is(
+ fallbackPlugin.pluginFallbackType,
+ Ci.nsIObjectLoadingContent.PLUGIN_ALTERNATE,
+ "Plugin fallback content was used"
+ );
+ }
+ }
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js b/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
new file mode 100644
index 0000000000..ed68a27688
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
@@ -0,0 +1,122 @@
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir.replace(
+ "chrome://mochitests/content/",
+ "http://127.0.0.1:8888/"
+);
+var gTestBrowser = null;
+var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+
+add_task(async function() {
+ registerCleanupFunction(function() {
+ clearAllPluginPermissions();
+ Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
+ gBrowser.removeCurrentTab();
+ window.focus();
+ gTestBrowser = null;
+ });
+});
+
+add_task(async function() {
+ Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
+
+ let newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+ gTestBrowser = gBrowser.selectedBrowser;
+});
+
+// Test that the plugin "blockall" overlay is always present but hidden,
+// regardless of whether the overlay is fully, partially, or not in the
+// viewport.
+
+// fully in viewport
+add_task(async function() {
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_outsideScrollArea.html"
+ );
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let doc = content.document;
+ let p = doc.createElement("embed");
+
+ p.setAttribute("id", "test");
+ p.setAttribute("type", "application/x-shockwave-flash");
+ p.style.left = "0";
+ p.style.bottom = "200px";
+
+ doc.getElementById("container").appendChild(p);
+ });
+
+ // Work around for delayed PluginBindingAttached
+ await promiseUpdatePluginBindings(gTestBrowser);
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let plugin = content.document.getElementById("test");
+ let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+ Assert.ok(overlay);
+ Assert.ok(!overlay.getAttribute("visible"));
+ Assert.ok(overlay.getAttribute("blockall") == "blockall");
+ });
+});
+
+// partially in viewport
+add_task(async function() {
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_outsideScrollArea.html"
+ );
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let doc = content.document;
+ let p = doc.createElement("embed");
+
+ p.setAttribute("id", "test");
+ p.setAttribute("type", "application/x-shockwave-flash");
+ p.style.left = "0";
+ p.style.bottom = "-410px";
+
+ doc.getElementById("container").appendChild(p);
+ });
+
+ // Work around for delayed PluginBindingAttached
+ await promiseUpdatePluginBindings(gTestBrowser);
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let plugin = content.document.getElementById("test");
+ let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+ Assert.ok(overlay);
+ Assert.ok(!overlay.getAttribute("visible"));
+ Assert.ok(overlay.getAttribute("blockall") == "blockall");
+ });
+});
+
+// not in viewport
+add_task(async function() {
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_outsideScrollArea.html"
+ );
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let doc = content.document;
+ let p = doc.createElement("embed");
+
+ p.setAttribute("id", "test");
+ p.setAttribute("type", "application/x-shockwave-flash");
+ p.style.left = "-600px";
+ p.style.bottom = "0";
+
+ doc.getElementById("container").appendChild(p);
+ });
+
+ // Work around for delayed PluginBindingAttached
+ await promiseUpdatePluginBindings(gTestBrowser);
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function() {
+ let plugin = content.document.getElementById("test");
+ let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+ Assert.ok(overlay);
+ Assert.ok(!overlay.getAttribute("visible"));
+ Assert.ok(overlay.getAttribute("blockall") == "blockall");
+ });
+});
diff --git a/browser/base/content/test/plugins/browser_CTP_zoom.js b/browser/base/content/test/plugins/browser_CTP_zoom.js
new file mode 100644
index 0000000000..eee2ec1837
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_CTP_zoom.js
@@ -0,0 +1,61 @@
+"use strict";
+
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir.replace(
+ "chrome://mochitests/content/",
+ "http://127.0.0.1:8888/"
+);
+
+var gTestBrowser = null;
+
+add_task(async function() {
+ registerCleanupFunction(function() {
+ clearAllPluginPermissions();
+ Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
+ FullZoom.reset(); // must be called before closing the tab we zoomed!
+ gBrowser.removeCurrentTab();
+ window.focus();
+ gTestBrowser = null;
+ });
+});
+
+add_task(async function() {
+ Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
+
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ gTestBrowser = gBrowser.selectedBrowser;
+
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_zoom.html"
+ );
+
+ // Work around for delayed PluginBindingAttached
+ await promiseUpdatePluginBindings(gTestBrowser);
+});
+
+// Enlarges the zoom level 4 times and tests that the overlay is
+// visible after each enlargement.
+add_task(async function() {
+ for (let count = 0; count < 4; count++) {
+ FullZoom.enlarge();
+
+ // Reload the page
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_zoom.html"
+ );
+ await promiseUpdatePluginBindings(gTestBrowser);
+ await SpecialPowers.spawn(gTestBrowser, [{ count }], async function(args) {
+ let doc = content.document;
+ let plugin = doc.getElementById("test");
+ let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+ Assert.ok(
+ overlay &&
+ !overlay.classList.contains("visible") &&
+ overlay.getAttribute("blockall") == "blockall",
+ "Overlay should be present for zoom change count " + args.count
+ );
+ });
+ }
+});
diff --git a/browser/base/content/test/plugins/browser_bug797677.js b/browser/base/content/test/plugins/browser_bug797677.js
new file mode 100644
index 0000000000..4ba565a1a5
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_bug797677.js
@@ -0,0 +1,45 @@
+var gTestRoot = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://127.0.0.1:8888/"
+);
+var gTestBrowser = null;
+var gConsoleErrors = 0;
+
+add_task(async function() {
+ registerCleanupFunction(function() {
+ Services.console.unregisterListener(errorListener);
+ gBrowser.removeCurrentTab();
+ window.focus();
+ gTestBrowser = null;
+ });
+
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ gTestBrowser = gBrowser.selectedBrowser;
+
+ let errorListener = {
+ observe(aMessage) {
+ if (aMessage.message.includes("NS_ERROR_FAILURE")) {
+ gConsoleErrors++;
+ }
+ },
+ };
+ Services.console.registerListener(errorListener);
+
+ await promiseTabLoadEvent(
+ gBrowser.selectedTab,
+ gTestRoot + "plugin_bug797677.html"
+ );
+
+ let pluginInfo = await promiseForPluginInfo("plugin");
+ is(
+ pluginInfo.pluginFallbackType,
+ Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED,
+ "plugin should not have been found."
+ );
+
+ await SpecialPowers.spawn(gTestBrowser, [], function() {
+ let plugin = content.document.getElementById("plugin");
+ ok(plugin, "plugin should be in the page");
+ });
+ is(gConsoleErrors, 0, "should have no console errors");
+});
diff --git a/browser/base/content/test/plugins/browser_enable_DRM_prompt.js b/browser/base/content/test/plugins/browser_enable_DRM_prompt.js
new file mode 100644
index 0000000000..be0c779657
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_enable_DRM_prompt.js
@@ -0,0 +1,228 @@
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty_file.html";
+
+/*
+ * Register cleanup function to reset prefs after other tasks have run.
+ */
+
+add_task(async function() {
+ // Note: SpecialPowers.pushPrefEnv has problems with the "Enable DRM"
+ // button on the notification box toggling the prefs. So manually
+ // set/unset the prefs the UI we're testing toggles.
+ let emeWasEnabled = Services.prefs.getBoolPref("media.eme.enabled", false);
+ let cdmWasEnabled = Services.prefs.getBoolPref(
+ "media.gmp-widevinecdm.enabled",
+ false
+ );
+
+ // Restore the preferences to their pre-test state on test finish.
+ registerCleanupFunction(function() {
+ // Unlock incase lock test threw and didn't unlock.
+ Services.prefs.unlockPref("media.eme.enabled");
+ Services.prefs.setBoolPref("media.eme.enabled", emeWasEnabled);
+ Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", cdmWasEnabled);
+ });
+});
+
+/*
+ * Bug 1366167 - Tests that the "Enable DRM" prompt shows if EME is requested while EME is disabled.
+ */
+
+add_task(async function test_drm_prompt_shows_for_toplevel() {
+ await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
+ // Turn off EME and Widevine CDM.
+ Services.prefs.setBoolPref("media.eme.enabled", false);
+ Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", false);
+
+ // Have content request access to Widevine, UI should drop down to
+ // prompt user to enable DRM.
+ let result = await SpecialPowers.spawn(browser, [], async function() {
+ try {
+ let config = [
+ {
+ initDataTypes: ["webm"],
+ videoCapabilities: [{ contentType: 'video/webm; codecs="vp9"' }],
+ },
+ ];
+ await content.navigator.requestMediaKeySystemAccess(
+ "com.widevine.alpha",
+ config
+ );
+ } catch (ex) {
+ return { rejected: true };
+ }
+ return { rejected: false };
+ });
+ is(
+ result.rejected,
+ true,
+ "EME request should be denied because EME disabled."
+ );
+
+ // Verify the UI prompt showed.
+ let box = gBrowser.getNotificationBox(browser);
+ let notification = box.currentNotification;
+
+ ok(notification, "Notification should be visible");
+ is(
+ notification.getAttribute("value"),
+ "drmContentDisabled",
+ "Should be showing the right notification"
+ );
+
+ // Verify the "Enable DRM" button is there.
+ let buttons = notification.querySelectorAll(".notification-button");
+ is(buttons.length, 1, "Should have one button.");
+
+ // Prepare a Promise that should resolve when the "Enable DRM" button's
+ // page reload completes.
+ let refreshPromise = BrowserTestUtils.browserLoaded(browser);
+ buttons[0].click();
+
+ // Wait for the reload to complete.
+ await refreshPromise;
+
+ // Verify clicking the "Enable DRM" button enabled DRM.
+ let enabled = Services.prefs.getBoolPref("media.eme.enabled", true);
+ is(
+ enabled,
+ true,
+ "EME should be enabled after click on 'Enable DRM' button"
+ );
+ });
+});
+
+add_task(async function test_eme_locked() {
+ await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
+ // Turn off EME and Widevine CDM.
+ Services.prefs.setBoolPref("media.eme.enabled", false);
+ Services.prefs.lockPref("media.eme.enabled");
+
+ // Have content request access to Widevine, UI should drop down to
+ // prompt user to enable DRM.
+ let result = await SpecialPowers.spawn(browser, [], async function() {
+ try {
+ let config = [
+ {
+ initDataTypes: ["webm"],
+ videoCapabilities: [{ contentType: 'video/webm; codecs="vp9"' }],
+ },
+ ];
+ await content.navigator.requestMediaKeySystemAccess(
+ "com.widevine.alpha",
+ config
+ );
+ } catch (ex) {
+ return { rejected: true };
+ }
+ return { rejected: false };
+ });
+ is(
+ result.rejected,
+ true,
+ "EME request should be denied because EME disabled."
+ );
+
+ // Verify the UI prompt did not show.
+ let box = gBrowser.getNotificationBox(browser);
+ let notification = box.currentNotification;
+
+ is(
+ notification,
+ null,
+ "Notification should not be displayed since pref is locked"
+ );
+
+ // Unlock the pref for any tests that follow.
+ Services.prefs.unlockPref("media.eme.enabled");
+ });
+});
+
+/*
+ * Bug 1642465 - Ensure cross origin frames requesting access prompt in the same way as same origin.
+ */
+
+add_task(async function test_drm_prompt_shows_for_cross_origin_iframe() {
+ await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
+ // Turn off EME and Widevine CDM.
+ Services.prefs.setBoolPref("media.eme.enabled", false);
+ Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", false);
+
+ // Have content request access to Widevine, UI should drop down to
+ // prompt user to enable DRM.
+ const CROSS_ORIGIN_URL = TEST_URL.replace("example.com", "example.org");
+ let result = await SpecialPowers.spawn(
+ browser,
+ [CROSS_ORIGIN_URL],
+ async function(crossOriginUrl) {
+ let frame = content.document.createElement("iframe");
+ frame.src = crossOriginUrl;
+ await new Promise(resolve => {
+ frame.addEventListener("load", () => {
+ resolve();
+ });
+ content.document.body.appendChild(frame);
+ });
+
+ return content.SpecialPowers.spawn(frame, [], async function() {
+ try {
+ let config = [
+ {
+ initDataTypes: ["webm"],
+ videoCapabilities: [
+ { contentType: 'video/webm; codecs="vp9"' },
+ ],
+ },
+ ];
+ await content.navigator.requestMediaKeySystemAccess(
+ "com.widevine.alpha",
+ config
+ );
+ } catch (ex) {
+ return { rejected: true };
+ }
+ return { rejected: false };
+ });
+ }
+ );
+ is(
+ result.rejected,
+ true,
+ "EME request should be denied because EME disabled."
+ );
+
+ // Verify the UI prompt showed.
+ let box = gBrowser.getNotificationBox(browser);
+ let notification = box.currentNotification;
+
+ ok(notification, "Notification should be visible");
+ is(
+ notification.getAttribute("value"),
+ "drmContentDisabled",
+ "Should be showing the right notification"
+ );
+
+ // Verify the "Enable DRM" button is there.
+ let buttons = notification.querySelectorAll(".notification-button");
+ is(buttons.length, 1, "Should have one button.");
+
+ // Prepare a Promise that should resolve when the "Enable DRM" button's
+ // page reload completes.
+ let refreshPromise = BrowserTestUtils.browserLoaded(browser);
+ buttons[0].click();
+
+ // Wait for the reload to complete.
+ await refreshPromise;
+
+ // Verify clicking the "Enable DRM" button enabled DRM.
+ let enabled = Services.prefs.getBoolPref("media.eme.enabled", true);
+ is(
+ enabled,
+ true,
+ "EME should be enabled after click on 'Enable DRM' button"
+ );
+ });
+});
diff --git a/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
new file mode 100644
index 0000000000..97cb9db618
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
@@ -0,0 +1,63 @@
+"use strict";
+
+let { PluginManager } = ChromeUtils.import(
+ "resource:///actors/PluginParent.jsm"
+);
+
+/**
+ * Test that the notification bar for crashed GMPs works.
+ */
+add_task(async function() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async function(browser) {
+ // Ensure the parent has heard before the client.
+ // In practice, this is always true for GMP crashes (but not for NPAPI ones!)
+ let props = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ props.setPropertyAsUint32("pluginID", 1);
+ props.setPropertyAsACString("pluginName", "GlobalTestPlugin");
+ props.setPropertyAsACString("pluginDumpID", "1234");
+ Services.obs.notifyObservers(props, "gmp-plugin-crash");
+
+ await SpecialPowers.spawn(browser, [], async function() {
+ const GMP_CRASH_EVENT = {
+ pluginID: 1,
+ pluginName: "GlobalTestPlugin",
+ submittedCrashReport: false,
+ bubbles: true,
+ cancelable: true,
+ gmpPlugin: true,
+ };
+
+ let crashEvent = new content.PluginCrashedEvent(
+ "PluginCrashed",
+ GMP_CRASH_EVENT
+ );
+ content.dispatchEvent(crashEvent);
+ });
+
+ let notification = await waitForNotificationBar(
+ "plugin-crashed",
+ browser
+ );
+
+ let notificationBox = gBrowser.getNotificationBox(browser);
+ ok(notification, "Infobar was shown.");
+ is(
+ notification.priority,
+ notificationBox.PRIORITY_WARNING_MEDIUM,
+ "Correct priority."
+ );
+ is(
+ notification.messageText.textContent,
+ "The GlobalTestPlugin plugin has crashed.",
+ "Correct message."
+ );
+ }
+ );
+});
diff --git a/browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js b/browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js
new file mode 100644
index 0000000000..9a0b91119b
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/*
+ * This test ensures that navigator.requestMediaKeySystemAccess() requests
+ * to run EME with persistent state are rejected in private browsing windows.
+ * Bug 1334111.
+ */
+
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "empty_file.html";
+
+async function isEmePersistentStateSupported(mode) {
+ let win = await BrowserTestUtils.openNewBrowserWindow(mode);
+ let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+ let persistentStateSupported = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async function() {
+ try {
+ let config = [
+ {
+ initDataTypes: ["webm"],
+ videoCapabilities: [{ contentType: 'video/webm; codecs="vp9"' }],
+ persistentState: "required",
+ },
+ ];
+ await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ config
+ );
+ } catch (ex) {
+ return false;
+ }
+ return true;
+ }
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+
+ return persistentStateSupported;
+}
+
+add_task(async function test() {
+ is(
+ await isEmePersistentStateSupported({ private: true }),
+ false,
+ "EME persistentState should *NOT* be supported in private browsing window."
+ );
+ is(
+ await isEmePersistentStateSupported({ private: false }),
+ true,
+ "EME persistentState *SHOULD* be supported in non private browsing window."
+ );
+});
diff --git a/browser/base/content/test/plugins/empty_file.html b/browser/base/content/test/plugins/empty_file.html
new file mode 100644
index 0000000000..af8440ac16
--- /dev/null
+++ b/browser/base/content/test/plugins/empty_file.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ This page is intentionally left blank.
+ </body>
+</html>
diff --git a/browser/base/content/test/plugins/head.js b/browser/base/content/test/plugins/head.js
new file mode 100644
index 0000000000..d00bd4a446
--- /dev/null
+++ b/browser/base/content/test/plugins/head.js
@@ -0,0 +1,452 @@
+var { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "PromiseUtils",
+ "resource://gre/modules/PromiseUtils.jsm"
+);
+
+XPCOMUtils.defineLazyServiceGetters(this, {
+ uuidGen: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
+});
+
+// Various tests in this directory may define gTestBrowser, to use as the
+// default browser under test in some of the functions below.
+/* global gTestBrowser:true */
+
+/**
+ * Waits a specified number of miliseconds.
+ *
+ * Usage:
+ * let wait = yield waitForMs(2000);
+ * ok(wait, "2 seconds should now have elapsed");
+ *
+ * @param aMs the number of miliseconds to wait for
+ * @returns a Promise that resolves to true after the time has elapsed
+ */
+function waitForMs(aMs) {
+ return new Promise(resolve => {
+ setTimeout(done, aMs);
+ function done() {
+ resolve(true);
+ }
+ });
+}
+
+/**
+ * Waits for a load (or custom) event to finish in a given tab. If provided
+ * load an uri into the tab.
+ *
+ * @param tab
+ * The tab to load into.
+ * @param [optional] url
+ * The url to load, or the current url.
+ * @return {Promise} resolved when the event is handled.
+ * @resolves to the received event
+ * @rejects 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.loadURI(tab.linkedBrowser, url);
+ }
+
+ return loaded;
+}
+
+function waitForCondition(condition, nextTest, errorMsg, aTries, aWait) {
+ let tries = 0;
+ let maxTries = aTries || 100; // 100 tries
+ let maxWait = aWait || 100; // 100 msec x 100 tries = ten seconds
+ let interval = setInterval(function() {
+ if (tries >= maxTries) {
+ ok(false, errorMsg);
+ moveOn();
+ }
+ let conditionPassed;
+ try {
+ conditionPassed = condition();
+ } catch (e) {
+ ok(false, e + "\n" + e.stack);
+ conditionPassed = false;
+ }
+ if (conditionPassed) {
+ moveOn();
+ }
+ tries++;
+ }, maxWait);
+ let moveOn = function() {
+ clearInterval(interval);
+ nextTest();
+ };
+}
+
+// Waits for a conditional function defined by the caller to return true.
+function promiseForCondition(aConditionFn, aMessage, aTries, aWait) {
+ return new Promise(resolve => {
+ waitForCondition(
+ aConditionFn,
+ resolve,
+ aMessage || "Condition didn't pass.",
+ aTries,
+ aWait
+ );
+ });
+}
+
+// Returns the chrome side nsIPluginTag for this plugin
+function getTestPlugin(aName) {
+ let pluginName = aName || "Test Plug-in";
+ let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ let tags = ph.getPluginTags();
+
+ // Find the test plugin
+ for (let i = 0; i < tags.length; i++) {
+ if (tags[i].name == pluginName) {
+ return tags[i];
+ }
+ }
+ ok(false, "Unable to find plugin");
+ return null;
+}
+
+// Set the 'enabledState' on the nsIPluginTag stored in the main or chrome
+// process.
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+ let name = pluginName || "Test Plug-in";
+ let plugin = getTestPlugin(name);
+ plugin.enabledState = newEnabledState;
+}
+
+// Get the 'enabledState' on the nsIPluginTag stored in the main or chrome
+// process.
+function getTestPluginEnabledState(pluginName) {
+ let name = pluginName || "Test Plug-in";
+ let plugin = getTestPlugin(name);
+ return plugin.enabledState;
+}
+
+// Returns a promise for nsIObjectLoadingContent props data.
+function promiseForPluginInfo(aId, aBrowser) {
+ let browser = aBrowser || gTestBrowser;
+ return SpecialPowers.spawn(browser, [aId], async function(contentId) {
+ let plugin = content.document.getElementById(contentId);
+ if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
+ throw new Error("no plugin found");
+ }
+ return {
+ pluginFallbackType: plugin.pluginFallbackType,
+ activated: plugin.activated,
+ hasRunningPlugin: plugin.hasRunningPlugin,
+ displayedType: plugin.displayedType,
+ };
+ });
+}
+
+// Return a promise and call the plugin's playPlugin() method.
+function promisePlayObject(aId, aBrowser) {
+ let browser = aBrowser || gTestBrowser;
+ return SpecialPowers.spawn(browser, [aId], async function(contentId) {
+ content.document.getElementById(contentId).playPlugin();
+ });
+}
+
+function promiseCrashObject(aId, aBrowser) {
+ let browser = aBrowser || gTestBrowser;
+ return SpecialPowers.spawn(browser, [aId], async function(contentId) {
+ let plugin = content.document.getElementById(contentId);
+ Cu.waiveXrays(plugin).crash();
+ });
+}
+
+// Return a promise and call the plugin's getObjectValue() method.
+function promiseObjectValueResult(aId, aBrowser) {
+ let browser = aBrowser || gTestBrowser;
+ return SpecialPowers.spawn(browser, [aId], async function(contentId) {
+ let plugin = content.document.getElementById(contentId);
+ return Cu.waiveXrays(plugin).getObjectValue();
+ });
+}
+
+// Return a promise and reload the target plugin in the page
+function promiseReloadPlugin(aId, aBrowser) {
+ let browser = aBrowser || gTestBrowser;
+ return SpecialPowers.spawn(browser, [aId], async function(contentId) {
+ let plugin = content.document.getElementById(contentId);
+ // eslint-disable-next-line no-self-assign
+ plugin.src = plugin.src;
+ });
+}
+
+// after a test is done using the plugin doorhanger, we should just clear
+// any permissions that may have crept in
+function clearAllPluginPermissions() {
+ for (let perm of Services.perms.all) {
+ if (perm.type.startsWith("plugin")) {
+ info(
+ "removing permission:" + perm.principal.origin + " " + perm.type + "\n"
+ );
+ Services.perms.removePermission(perm);
+ }
+ }
+}
+
+// Ported from AddonTestUtils.jsm
+let JSONBlocklistWrapper = {
+ /**
+ * Load the data from the specified files into the *real* blocklist providers.
+ * Loads using loadBlocklistRawData, which will treat this as an update.
+ *
+ * @param {nsIFile} dir
+ * The directory in which the files live.
+ * @param {string} prefix
+ * a prefix for the files which ought to be loaded.
+ * This method will suffix -extensions.json and -plugins.json
+ * to the prefix it is given, and attempt to load both.
+ * Insofar as either exists, their data will be dumped into
+ * the respective store, and the respective update handlers
+ * will be called.
+ */
+ async loadBlocklistData(url) {
+ const fullURL = `${url}-plugins.json`;
+ let jsonObj;
+ try {
+ jsonObj = await (await fetch(fullURL)).json();
+ } catch (ex) {
+ ok(false, ex);
+ }
+ info(`Loaded ${fullURL}`);
+
+ return this.loadBlocklistRawData({ plugins: jsonObj });
+ },
+
+ /**
+ * Load the following data into the *real* blocklist providers.
+ * While `overrideBlocklist` replaces the blocklist entirely with a mock
+ * that returns dummy data, this method instead loads data into the actual
+ * blocklist, fires update methods as would happen if this data came from
+ * an actual blocklist update, etc.
+ *
+ * @param {object} data
+ * An object that can optionally have `extensions` and/or `plugins`
+ * properties, each being an array of blocklist items.
+ * This code only uses plugin blocks, that can look something like:
+ *
+ * {
+ * "matchFilename": "libnptest\\.so|nptest\\.dll|Test\\.plugin",
+ * "versionRange": [
+ * {
+ * "severity": "0",
+ * "vulnerabilityStatus": "1"
+ * }
+ * ],
+ * "blockID": "p9999"
+ * }
+ *
+ */
+ async loadBlocklistRawData(data) {
+ const bsPass = ChromeUtils.import(
+ "resource://gre/modules/Blocklist.jsm",
+ null
+ );
+ const blocklistMapping = {
+ extensions: bsPass.ExtensionBlocklistRS,
+ plugins: bsPass.PluginBlocklistRS,
+ };
+
+ for (const [dataProp, blocklistObj] of Object.entries(blocklistMapping)) {
+ let newData = data[dataProp];
+ if (!newData) {
+ continue;
+ }
+ if (!Array.isArray(newData)) {
+ throw new Error(
+ "Expected an array of new items to put in the " +
+ dataProp +
+ " blocklist!"
+ );
+ }
+ for (let item of newData) {
+ if (!item.id) {
+ item.id = uuidGen.generateUUID().number.slice(1, -1);
+ }
+ if (!item.last_modified) {
+ item.last_modified = Date.now();
+ }
+ }
+ await blocklistObj.ensureInitialized();
+ let db = await blocklistObj._client.db;
+ await db.importChanges({}, 42, newData, { clear: true });
+ // We manually call _onUpdate... which is evil, but at the moment kinto doesn't have
+ // a better abstraction unless you want to mock your own http server to do the update.
+ await blocklistObj._onUpdate();
+ }
+ },
+};
+
+// An async helper that insures a new blocklist is loaded (in both
+// processes if applicable).
+async function asyncSetAndUpdateBlocklist(aURL, aBrowser) {
+ let doTestRemote = aBrowser ? aBrowser.isRemoteBrowser : false;
+ let localPromise = TestUtils.topicObserved("plugin-blocklist-updated");
+ info("*** loading blocklist: " + aURL);
+ await JSONBlocklistWrapper.loadBlocklistData(aURL);
+ info("*** waiting on local load");
+ await localPromise;
+ if (doTestRemote) {
+ info("*** waiting on remote load");
+ // Ensure content has been updated with the blocklist
+ await SpecialPowers.spawn(aBrowser, [], () => {});
+ }
+ info("*** blocklist loaded.");
+}
+
+// Insure there's a popup notification present. This test does not indicate
+// open state. aBrowser can be undefined.
+function promisePopupNotification(aName, aBrowser) {
+ return new Promise(resolve => {
+ waitForCondition(
+ () => PopupNotifications.getNotification(aName, aBrowser),
+ () => {
+ ok(
+ !!PopupNotifications.getNotification(aName, aBrowser),
+ aName + " notification appeared"
+ );
+
+ resolve();
+ },
+ "timeout waiting for popup notification " + aName
+ );
+ });
+}
+
+/**
+ * Allows setting focus on a window, and waiting for that window to achieve
+ * focus.
+ *
+ * @param aWindow
+ * The window to focus and wait for.
+ *
+ * @return {Promise}
+ * @resolves When the window is focused.
+ * @rejects Never.
+ */
+function promiseWaitForFocus(aWindow) {
+ return new Promise(resolve => {
+ waitForFocus(resolve, aWindow);
+ });
+}
+
+/**
+ * Returns a Promise that resolves when a notification bar
+ * for a browser is shown. Alternatively, for old-style callers,
+ * can automatically call a callback before it resolves.
+ *
+ * @param notificationID
+ * The ID of the notification to look for.
+ * @param browser
+ * The browser to check for the notification bar.
+ * @param callback (optional)
+ * A function to be called just before the Promise resolves.
+ *
+ * @return Promise
+ */
+function waitForNotificationBar(notificationID, browser, callback) {
+ return new Promise((resolve, reject) => {
+ let notification;
+ let notificationBox = gBrowser.getNotificationBox(browser);
+ waitForCondition(
+ () =>
+ (notification = notificationBox.getNotificationWithValue(
+ notificationID
+ )),
+ () => {
+ ok(
+ notification,
+ `Successfully got the ${notificationID} notification bar`
+ );
+ if (callback) {
+ callback(notification);
+ }
+ resolve(notification);
+ },
+ `Waited too long for the ${notificationID} notification bar`
+ );
+ });
+}
+
+function promiseForNotificationBar(notificationID, browser) {
+ return new Promise(resolve => {
+ waitForNotificationBar(notificationID, browser, resolve);
+ });
+}
+
+/**
+ * Reshow a notification and call a callback when it is reshown.
+ * @param notification
+ * The notification to reshow
+ * @param callback
+ * A function to be called when the notification has been reshown
+ */
+function waitForNotificationShown(notification, callback) {
+ if (PopupNotifications.panel.state == "open") {
+ executeSoon(callback);
+ return;
+ }
+ PopupNotifications.panel.addEventListener(
+ "popupshown",
+ function(e) {
+ callback();
+ },
+ { once: true }
+ );
+ notification.reshow();
+}
+
+function promiseForNotificationShown(notification) {
+ return new Promise(resolve => {
+ waitForNotificationShown(notification, resolve);
+ });
+}
+
+/**
+ * Due to layout being async, "PluginBindAttached" may trigger later. This
+ * returns a Promise that resolves once we've forced a layout flush, which
+ * triggers the PluginBindAttached event to fire. This trick only works if
+ * there is some sort of plugin in the page.
+ * @param browser
+ * The browser to force plugin bindings in.
+ * @return Promise
+ */
+function promiseUpdatePluginBindings(browser) {
+ return SpecialPowers.spawn(browser, [], async function() {
+ let doc = content.document;
+ let elems = doc.getElementsByTagName("embed");
+ if (!elems || elems.length < 1) {
+ elems = doc.getElementsByTagName("object");
+ }
+ if (elems && elems.length) {
+ elems[0].clientTop;
+ }
+ });
+}
diff --git a/browser/base/content/test/plugins/plugin_bug797677.html b/browser/base/content/test/plugins/plugin_bug797677.html
new file mode 100644
index 0000000000..1545f36475
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_bug797677.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"/></head>
+<body><embed id="plugin" type="9000"></embed></body>
+</html>
diff --git a/browser/base/content/test/plugins/plugin_favorfallback.html b/browser/base/content/test/plugins/plugin_favorfallback.html
new file mode 100644
index 0000000000..6eaf154994
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_favorfallback.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"/></head>
+<body>
+<style>
+.testcase {
+ display: none;
+}
+object {
+ width: 200px;
+ height: 200px;
+}
+</style>
+
+<!-- Tests that a <video> tag in the fallback content favors the fallback content -->
+<div id="testcase_video" class="testcase">
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ Unexpected fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ <video></video>
+ Expected fallback
+ </object>
+</div>
+
+<!-- Tests that an object with no src specified (no data="") favors the fallback content -->
+<div id="testcase_nosrc" class="testcase">
+ <!-- We must use an existing and valid file here because otherwise the failed load
+ triggers the plugin's alternate content, indepedent of the favor-fallback code path -->
+ <object class="expected_ctp" type="application/x-shockwave-flash-test" data="plugin_simple_blank.swf">
+ Unexpected fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ Expected fallback
+ </object>
+</div>
+
+<!-- Tests that an <embed> tag in the fallback content forces the plugin content,
+ when fallback is defaulting to true -->
+<div id="testcase_embed" class="testcase">
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ <embed></embed>
+ Unexpected fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ Expected fallback
+ </object>
+</div>
+
+<!-- Tests that links to adobe.com inside the fallback content forces the plugin content,
+ when fallback is defaulting to true -->
+<div id="testcase_adobelink" class="testcase">
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ <a href="https://www.adobe.com">Go to adobe.com</a>
+ Unexpected fallback
+ </object>
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ <a href="https://adobe.com">Go to adobe.com</a>
+ Unexpected fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ Expected fallback
+ </object>
+</div>
+
+<!-- Tests that instructions to download or install flash inside the fallback content
+ forces the plugin content, when fallback is defaulting to true -->
+<div id="testcase_installinstructions" class="testcase">
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ Install -- Unexpected fallback
+ </object>
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ Flash -- Unexpected fallback
+ </object>
+ <object class="expected_ctp" type="application/x-shockwave-flash-test">
+ Download -- Unexpected fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ <!-- Tests that the words Install, Flash or Download do not trigger
+ this behavior if it's just inside a comment, and not part of
+ the text content -->
+ Expected Fallback
+ </object>
+ <object class="expected_fallback" type="application/x-shockwave-flash-test">
+ Expected fallback
+ </object>
+</div>
+
+<script>
+ let queryString = location.search;
+ let match = /^\?testcase=([a-z]+)$/.exec(queryString);
+ let testcase = match[1];
+ document.getElementById(`testcase_${testcase}`).style.display = "block";
+</script>
+</body>
+</html>
diff --git a/browser/base/content/test/plugins/plugin_outsideScrollArea.html b/browser/base/content/test/plugins/plugin_outsideScrollArea.html
new file mode 100644
index 0000000000..c6ef50d5db
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_outsideScrollArea.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style type="text/css">
+#container {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+ background: blue;
+}
+
+#test {
+ width: 400px;
+ height: 400px;
+ position: absolute;
+}
+</style>
+</head>
+<body>
+ <div id="container"></div>
+</body>
+</html>
diff --git a/browser/base/content/test/plugins/plugin_simple_blank.swf b/browser/base/content/test/plugins/plugin_simple_blank.swf
new file mode 100644
index 0000000000..b846387eb8
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_simple_blank.swf
Binary files differ
diff --git a/browser/base/content/test/plugins/plugin_test.html b/browser/base/content/test/plugins/plugin_test.html
new file mode 100644
index 0000000000..3d4f43e6a5
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_test.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<embed id="test" style="width: 300px; height: 300px" type="application/x-test">
+</body>
+</html>
diff --git a/browser/base/content/test/plugins/plugin_zoom.html b/browser/base/content/test/plugins/plugin_zoom.html
new file mode 100644
index 0000000000..f9e5986581
--- /dev/null
+++ b/browser/base/content/test/plugins/plugin_zoom.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<!-- The odd width and height are here to trigger bug 972237. -->
+<embed id="test" style="width: 99.789%; height: 99.123%" type="application/x-test">
+</body>
+</html>