diff options
Diffstat (limited to 'browser/base/content/test/plugins')
10 files changed, 724 insertions, 0 deletions
diff --git a/browser/base/content/test/plugins/BlocklistTestProxy.jsm b/browser/base/content/test/plugins/BlocklistTestProxy.jsm new file mode 100644 index 0000000000..80e02e1e37 --- /dev/null +++ b/browser/base/content/test/plugins/BlocklistTestProxy.jsm @@ -0,0 +1,78 @@ +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.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +/* + * A lightweight blocklist proxy for testing purposes. + */ +var BlocklistProxy = { + _uuid: null, + + QueryInterface: ChromeUtils.generateQI([ + "nsIObserver", + "nsIBlocklistService", + "nsITimerCallback", + ]), + + init() { + if (!this._uuid) { + this._uuid = Services.uuid.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 + }, +}; + +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..df3c5b4d6b --- /dev/null +++ b/browser/base/content/test/plugins/browser.ini @@ -0,0 +1,14 @@ +[DEFAULT] +support-files = + BlocklistTestProxy.jsm + empty_file.html + head.js + plugin_bug797677.html + plugin_test.html + +[browser_bug797677.js] +[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_bug797677.js b/browser/base/content/test/plugins/browser_bug797677.js new file mode 100644 index 0000000000..bcb35d161e --- /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.displayedType, + Ci.nsIObjectLoadingContent.TYPE_NULL, + "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..8698c07a6c --- /dev/null +++ b/browser/base/content/test/plugins/browser_enable_DRM_prompt.js @@ -0,0 +1,232 @@ +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.buttonContainer.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.buttonContainer.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..c02af8b281 --- /dev/null +++ b/browser/base/content/test/plugins/head.js @@ -0,0 +1,210 @@ +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", +}); + +// 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 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 { + activated: plugin.activated, + hasRunningPlugin: plugin.hasRunningPlugin, + displayedType: plugin.displayedType, + }; + }); +} + +/** + * 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); + }); +} 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_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> |