summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_GMPInstallManager.js')
-rw-r--r--toolkit/modules/tests/xpcshell/test_GMPInstallManager.js1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
new file mode 100644
index 0000000000..b39b19f0ae
--- /dev/null
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -0,0 +1,1030 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+var Cm = Components.manager;
+const URL_HOST = "http://localhost";
+const PR_USEC_PER_MSEC = 1000;
+
+var GMPScope = ChromeUtils.import(
+ "resource://gre/modules/GMPInstallManager.jsm",
+ null
+);
+var GMPInstallManager = GMPScope.GMPInstallManager;
+
+const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+const { FileUtils } = ChromeUtils.import(
+ "resource://gre/modules/FileUtils.jsm"
+);
+const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+const { Preferences } = ChromeUtils.import(
+ "resource://gre/modules/Preferences.jsm"
+);
+const { UpdateUtils } = ChromeUtils.import(
+ "resource://gre/modules/UpdateUtils.jsm"
+);
+const GMPUtils = ChromeUtils.import("resource://gre/modules/GMPUtils.jsm");
+
+var ProductAddonCheckerScope = ChromeUtils.import(
+ "resource://gre/modules/addons/ProductAddonChecker.jsm",
+ null
+);
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+Services.prefs.setBoolPref("media.gmp-manager.updateEnabled", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+ Services.prefs.clearUserPref("media.gmp-manager.updateEnabled");
+});
+
+do_get_profile();
+
+function run_test() {
+ Preferences.set("media.gmp.log.dump", true);
+ Preferences.set("media.gmp.log.level", 0);
+ run_next_test();
+}
+
+/**
+ * Tests that the helper used for preferences works correctly
+ */
+add_task(async function test_prefs() {
+ let addon1 = "addon1",
+ addon2 = "addon2";
+
+ GMPScope.GMPPrefs.setString(
+ GMPScope.GMPPrefs.KEY_URL,
+ "http://not-really-used"
+ );
+ GMPScope.GMPPrefs.setString(
+ GMPScope.GMPPrefs.KEY_URL_OVERRIDE,
+ "http://not-really-used-2"
+ );
+ GMPScope.GMPPrefs.setInt(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 1, addon1);
+ GMPScope.GMPPrefs.setString(
+ GMPScope.GMPPrefs.KEY_PLUGIN_VERSION,
+ "2",
+ addon1
+ );
+ GMPScope.GMPPrefs.setInt(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 3, addon2);
+ GMPScope.GMPPrefs.setInt(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, 4, addon2);
+ GMPScope.GMPPrefs.setBool(
+ GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
+ false,
+ addon2
+ );
+ GMPScope.GMPPrefs.setBool(GMPScope.GMPPrefs.KEY_CERT_CHECKATTRS, true);
+
+ Assert.equal(
+ GMPScope.GMPPrefs.getString(GMPScope.GMPPrefs.KEY_URL),
+ "http://not-really-used"
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getString(GMPScope.GMPPrefs.KEY_URL_OVERRIDE),
+ "http://not-really-used-2"
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getInt(
+ GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE,
+ "",
+ addon1
+ ),
+ 1
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getString(
+ GMPScope.GMPPrefs.KEY_PLUGIN_VERSION,
+ "",
+ addon1
+ ),
+ "2"
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getInt(
+ GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE,
+ "",
+ addon2
+ ),
+ 3
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getInt(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "", addon2),
+ 4
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getBool(
+ GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
+ undefined,
+ addon2
+ ),
+ false
+ );
+ Assert.ok(GMPScope.GMPPrefs.getBool(GMPScope.GMPPrefs.KEY_CERT_CHECKATTRS));
+ GMPScope.GMPPrefs.setBool(
+ GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
+ true,
+ addon2
+ );
+});
+
+/**
+ * Tests that an uninit without a check works fine
+ */
+add_task(async function test_checkForAddons_uninitWithoutCheck() {
+ let installManager = new GMPInstallManager();
+ installManager.uninit();
+});
+
+/**
+ * Tests that an uninit without an install works fine
+ */
+add_test(function test_checkForAddons_uninitWithoutInstall() {
+ overrideXHR(200, "");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that no response returned rejects
+ */
+add_test(function test_checkForAddons_noResponse() {
+ overrideXHR(200, "");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that no addons element returned resolves with no addons
+ */
+add_task(async function test_checkForAddons_noAddonsElement() {
+ overrideXHR(200, "<updates></updates>");
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 0);
+ installManager.uninit();
+});
+
+/**
+ * Tests that empty addons element returned resolves with no addons
+ */
+add_task(async function test_checkForAddons_emptyAddonsElement() {
+ overrideXHR(200, "<updates><addons/></updates>");
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 0);
+ installManager.uninit();
+});
+
+/**
+ * Tests that a response with the wrong root element rejects
+ */
+add_test(function test_checkForAddons_wrongResponseXML() {
+ overrideXHR(200, "<digits_of_pi>3.141592653589793....</digits_of_pi>");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that a 404 error works as expected
+ */
+add_test(function test_checkForAddons_404Error() {
+ overrideXHR(404, "");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that a xhr abort() works as expected
+ */
+add_test(function test_checkForAddons_abort() {
+ let overriddenXhr = overrideXHR(200, "", { dropRequest: true });
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+
+ // Since the XHR is created in checkForAddons asynchronously,
+ // we need to delay aborting till the XHR is running.
+ // Since checkForAddons returns a Promise already only after
+ // the abort is triggered, we can't use that, and instead
+ // we'll use a fake timer.
+ setTimeout(() => {
+ overriddenXhr.abort();
+ }, 100);
+
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that a defensive timeout works as expected
+ */
+add_test(function test_checkForAddons_timeout() {
+ overrideXHR(200, "", { dropRequest: true, timeout: true });
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that we throw correctly in case of ssl certification error.
+ */
+add_test(function test_checkForAddons_bad_ssl() {
+ //
+ // Add random stuff that cause CertUtil to require https.
+ //
+ let PREF_KEY_URL_OVERRIDE_BACKUP = Preferences.get(
+ GMPScope.GMPPrefs.KEY_URL_OVERRIDE,
+ ""
+ );
+ Preferences.reset(GMPScope.GMPPrefs.KEY_URL_OVERRIDE);
+
+ let CERTS_BRANCH_DOT_ONE = GMPScope.GMPPrefs.KEY_CERTS_BRANCH + ".1";
+ let PREF_CERTS_BRANCH_DOT_ONE_BACKUP = Preferences.get(
+ CERTS_BRANCH_DOT_ONE,
+ ""
+ );
+ Services.prefs.setCharPref(CERTS_BRANCH_DOT_ONE, "funky value");
+
+ overrideXHR(200, "");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ if (PREF_KEY_URL_OVERRIDE_BACKUP) {
+ Preferences.set(
+ GMPScope.GMPPrefs.KEY_URL_OVERRIDE,
+ PREF_KEY_URL_OVERRIDE_BACKUP
+ );
+ }
+ if (PREF_CERTS_BRANCH_DOT_ONE_BACKUP) {
+ Preferences.set(CERTS_BRANCH_DOT_ONE, PREF_CERTS_BRANCH_DOT_ONE_BACKUP);
+ }
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that gettinga a funky non XML response works as expected
+ */
+add_test(function test_checkForAddons_notXML() {
+ overrideXHR(200, "3.141592653589793....");
+ let installManager = new GMPInstallManager();
+ let promise = installManager.checkForAddons();
+
+ promise.then(res => {
+ Assert.ok(res.usedFallback);
+ installManager.uninit();
+ run_next_test();
+ });
+});
+
+/**
+ * Tests that getting a response with a single addon works as expected
+ */
+add_task(async function test_checkForAddons_singleAddon() {
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ ' <addon id="gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha256"' +
+ ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 1);
+ let gmpAddon = res.addons[0];
+ Assert.equal(gmpAddon.id, "gmp-gmpopenh264");
+ Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+ Assert.equal(gmpAddon.hashFunction, "sha256");
+ Assert.equal(
+ gmpAddon.hashValue,
+ "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"
+ );
+ Assert.equal(gmpAddon.version, "1.1");
+ Assert.ok(gmpAddon.isValid);
+ Assert.ok(!gmpAddon.isInstalled);
+ installManager.uninit();
+});
+
+/**
+ * Tests that getting a response with a single addon with the optional size
+ * attribute parses as expected.
+ */
+add_task(async function test_checkForAddons_singleAddonWithSize() {
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ ' <addon id="openh264-plugin-no-at-symbol"' +
+ ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha256"' +
+ ' size="42"' +
+ ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 1);
+ let gmpAddon = res.addons[0];
+ Assert.equal(gmpAddon.id, "openh264-plugin-no-at-symbol");
+ Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+ Assert.equal(gmpAddon.hashFunction, "sha256");
+ Assert.equal(
+ gmpAddon.hashValue,
+ "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"
+ );
+ Assert.equal(gmpAddon.size, 42);
+ Assert.equal(gmpAddon.version, "1.1");
+ Assert.ok(gmpAddon.isValid);
+ Assert.ok(!gmpAddon.isInstalled);
+ installManager.uninit();
+});
+
+/**
+ * Tests that checking for multiple addons work correctly.
+ * Also tests that invalid addons work correctly.
+ */
+add_task(
+ async function test_checkForAddons_multipleAddonNoUpdatesSomeInvalid() {
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ // valid openh264
+ ' <addon id="gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha256"' +
+ ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="1.1"/>' +
+ // valid not openh264
+ ' <addon id="NOT-gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha512"' +
+ ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="9.1"/>' +
+ // noid
+ ' <addon notid="NOT-gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha512"' +
+ ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="9.1"/>' +
+ // no URL
+ ' <addon id="NOT-gmp-gmpopenh264"' +
+ ' notURL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha512"' +
+ ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="9.1"/>' +
+ // no hash function
+ ' <addon id="NOT-gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' nothashFunction="sha512"' +
+ ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="9.1"/>' +
+ // no hash function
+ ' <addon id="NOT-gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha512"' +
+ ' nothashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="9.1"/>' +
+ // not version
+ ' <addon id="NOT-gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha512"' +
+ ' hashValue="141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' notversion="9.1"/>' +
+ " </addons>" +
+ "</updates>";
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 7);
+ let gmpAddon = res.addons[0];
+ Assert.equal(gmpAddon.id, "gmp-gmpopenh264");
+ Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+ Assert.equal(gmpAddon.hashFunction, "sha256");
+ Assert.equal(
+ gmpAddon.hashValue,
+ "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"
+ );
+ Assert.equal(gmpAddon.version, "1.1");
+ Assert.ok(gmpAddon.isValid);
+ Assert.ok(!gmpAddon.isInstalled);
+
+ gmpAddon = res.addons[1];
+ Assert.equal(gmpAddon.id, "NOT-gmp-gmpopenh264");
+ Assert.equal(
+ gmpAddon.URL,
+ "http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip"
+ );
+ Assert.equal(gmpAddon.hashFunction, "sha512");
+ Assert.equal(
+ gmpAddon.hashValue,
+ "141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"
+ );
+ Assert.equal(gmpAddon.version, "9.1");
+ Assert.ok(gmpAddon.isValid);
+ Assert.ok(!gmpAddon.isInstalled);
+
+ for (let i = 2; i < res.addons.length; i++) {
+ Assert.ok(!res.addons[i].isValid);
+ Assert.ok(!res.addons[i].isInstalled);
+ }
+ installManager.uninit();
+ }
+);
+
+/**
+ * Tests that checking for addons when there are also updates available
+ * works as expected.
+ */
+add_task(async function test_checkForAddons_updatesWithAddons() {
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ " <updates>" +
+ ' <update type="minor" displayVersion="33.0a1" appVersion="33.0a1" platformVersion="33.0a1" buildID="20140628030201">' +
+ ' <patch type="complete" URL="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2014/06/2014-06-28-03-02-01-mozilla-central/firefox-33.0a1.en-US.mac.complete.mar" hashFunction="sha512" hashValue="f3f90d71dff03ae81def80e64bba3e4569da99c9e15269f731c2b167c4fc30b3aed9f5fee81c19614120230ca333e73a5e7def1b8e45d03135b2069c26736219" size="85249896"/>' +
+ " </update>" +
+ " <addons>" +
+ ' <addon id="gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha256"' +
+ ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 1);
+ let gmpAddon = res.addons[0];
+ Assert.equal(gmpAddon.id, "gmp-gmpopenh264");
+ Assert.equal(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
+ Assert.equal(gmpAddon.hashFunction, "sha256");
+ Assert.equal(
+ gmpAddon.hashValue,
+ "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"
+ );
+ Assert.equal(gmpAddon.version, "1.1");
+ Assert.ok(gmpAddon.isValid);
+ Assert.ok(!gmpAddon.isInstalled);
+ installManager.uninit();
+});
+
+/**
+ * Tests that installing found addons works as expected
+ */
+async function test_checkForAddons_installAddon(
+ id,
+ includeSize,
+ wantInstallReject
+) {
+ info(
+ "Running installAddon for id: " +
+ id +
+ ", includeSize: " +
+ includeSize +
+ " and wantInstallReject: " +
+ wantInstallReject
+ );
+ let httpServer = new HttpServer();
+ let dir = FileUtils.getDir("TmpD", [], true);
+ httpServer.registerDirectory("/", dir);
+ httpServer.start(-1);
+ let testserverPort = httpServer.identity.primaryPort;
+ let zipFileName = "test_" + id + "_GMP.zip";
+
+ let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName;
+ info("zipURL: " + zipURL);
+
+ let data = "e~=0.5772156649";
+ let zipFile = createNewZipFile(zipFileName, data);
+ let hashFunc = "sha256";
+ let expectedDigest = await ProductAddonCheckerScope.computeHash(
+ hashFunc,
+ zipFile.path
+ );
+ let fileSize = zipFile.fileSize;
+ if (wantInstallReject) {
+ fileSize = 1;
+ }
+
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ ' <addon id="' +
+ id +
+ '-gmp-gmpopenh264"' +
+ ' URL="' +
+ zipURL +
+ '"' +
+ ' hashFunction="' +
+ hashFunc +
+ '"' +
+ ' hashValue="' +
+ expectedDigest +
+ '"' +
+ (includeSize ? ' size="' + fileSize + '"' : "") +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let res = await installManager.checkForAddons();
+ Assert.equal(res.addons.length, 1);
+ let gmpAddon = res.addons[0];
+ Assert.ok(!gmpAddon.isInstalled);
+
+ try {
+ let extractedPaths = await installManager.installAddon(gmpAddon);
+ if (wantInstallReject) {
+ Assert.ok(false); // installAddon() should have thrown.
+ }
+ Assert.equal(extractedPaths.length, 1);
+ let extractedPath = extractedPaths[0];
+
+ info("Extracted path: " + extractedPath);
+
+ let extractedFile = Cc["@mozilla.org/file/local;1"].createInstance(
+ Ci.nsIFile
+ );
+ extractedFile.initWithPath(extractedPath);
+ Assert.ok(extractedFile.exists());
+ let readData = readStringFromFile(extractedFile);
+ Assert.equal(readData, data);
+
+ // Make sure the prefs are set correctly
+ Assert.ok(
+ !!GMPScope.GMPPrefs.getInt(
+ GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE,
+ "",
+ gmpAddon.id
+ )
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getString(
+ GMPScope.GMPPrefs.KEY_PLUGIN_VERSION,
+ "",
+ gmpAddon.id
+ ),
+ "1.1"
+ );
+ Assert.equal(
+ GMPScope.GMPPrefs.getString(
+ GMPScope.GMPPrefs.KEY_PLUGIN_ABI,
+ "",
+ gmpAddon.id
+ ),
+ UpdateUtils.ABI
+ );
+ // Make sure it reports as being installed
+ Assert.ok(gmpAddon.isInstalled);
+
+ // Cleanup
+ extractedFile.parent.remove(true);
+ zipFile.remove(false);
+ httpServer.stop(function() {});
+ installManager.uninit();
+ } catch (ex) {
+ zipFile.remove(false);
+ if (!wantInstallReject) {
+ do_throw("install update should not reject " + ex.message);
+ }
+ }
+}
+
+add_task(test_checkForAddons_installAddon.bind(null, "1", true, false));
+add_task(test_checkForAddons_installAddon.bind(null, "2", false, false));
+add_task(test_checkForAddons_installAddon.bind(null, "3", true, true));
+
+/**
+ * Tests simpleCheckAndInstall when autoupdate is disabled for a GMP
+ */
+add_task(async function test_simpleCheckAndInstall_autoUpdateDisabled() {
+ GMPScope.GMPPrefs.setBool(
+ GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
+ false,
+ GMPUtils.OPEN_H264_ID
+ );
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ // valid openh264
+ ' <addon id="gmp-gmpopenh264"' +
+ ' URL="http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip"' +
+ ' hashFunction="sha256"' +
+ ' hashValue="1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee"' +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let result = await installManager.simpleCheckAndInstall();
+ Assert.equal(result.status, "nothing-new-to-install");
+ Preferences.reset(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK);
+ GMPScope.GMPPrefs.setBool(
+ GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
+ true,
+ GMPUtils.OPEN_H264_ID
+ );
+});
+
+/**
+ * Tests simpleCheckAndInstall nothing to install
+ */
+add_task(async function test_simpleCheckAndInstall_nothingToInstall() {
+ let responseXML = '<?xml version="1.0"?><updates></updates>';
+
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let result = await installManager.simpleCheckAndInstall();
+ Assert.equal(result.status, "nothing-new-to-install");
+});
+
+/**
+ * Tests simpleCheckAndInstall too frequent
+ */
+add_task(async function test_simpleCheckAndInstall_tooFrequent() {
+ let responseXML = '<?xml version="1.0"?><updates></updates>';
+
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let result = await installManager.simpleCheckAndInstall();
+ Assert.equal(result.status, "too-frequent-no-check");
+});
+
+/**
+ * Tests that installing addons when there is no server works as expected
+ */
+add_test(function test_installAddon_noServer() {
+ let zipFileName = "test_GMP.zip";
+ let zipURL = URL_HOST + ":0/" + zipFileName;
+
+ let responseXML =
+ '<?xml version="1.0"?>' +
+ "<updates>" +
+ " <addons>" +
+ ' <addon id="gmp-gmpopenh264"' +
+ ' URL="' +
+ zipURL +
+ '"' +
+ ' hashFunction="sha256"' +
+ ' hashValue="11221cbda000347b054028b527a60e578f919cb10f322ef8077d3491c6fcb474"' +
+ ' version="1.1"/>' +
+ " </addons>" +
+ "</updates>";
+
+ overrideXHR(200, responseXML);
+ let installManager = new GMPInstallManager();
+ let checkPromise = installManager.checkForAddons();
+ checkPromise.then(
+ res => {
+ Assert.equal(res.addons.length, 1);
+ let gmpAddon = res.addons[0];
+
+ GMPInstallManager.overrideLeaveDownloadedZip = true;
+ let installPromise = installManager.installAddon(gmpAddon);
+ installPromise.then(
+ extractedPaths => {
+ do_throw("No server for install should reject");
+ },
+ err => {
+ Assert.ok(!!err);
+ installManager.uninit();
+ run_next_test();
+ }
+ );
+ },
+ () => {
+ do_throw("check should not reject for install no server");
+ }
+ );
+});
+
+/***
+ * Tests GMPExtractor (an internal component of GMPInstallManager) to ensure
+ * it handles paths with certain characters.
+ */
+
+add_task(async function test_GMPExtractor_paths() {
+ let GMPExtractor = GMPScope.GMPExtractor;
+ registerCleanupFunction(function() {
+ // Must stop holding on to the zip file using the JAR cache:
+ let zipFile = new FileUtils.File(
+ OS.Path.join(tempDir.path, "dummy_gmp.zip")
+ );
+ Services.obs.notifyObservers(zipFile, "flush-cache-entry");
+ extractedDir.remove(/* recursive */ true);
+ tempDir.remove(/* recursive */ true);
+ });
+ // Create a dir with the following in the name
+ // - # -- this is used to delimit URI fragments and tests that
+ // we escape any internal URIs appropriately.
+ // - 猫 -- ensure we handle non-ascii characters appropriately.
+ let tempDirName = "TmpDir#猫";
+ let tempDir = FileUtils.getDir("TmpD", [tempDirName], true);
+ let zipPath = OS.Path.join(tempDir.path, "dummy_gmp.zip");
+ await OS.File.copy("zips/dummy_gmp.zip", zipPath, { noOverwrite: false });
+ // The path inside the profile dir we'll extract to. Make sure we handle
+ // the characters there too.
+ let relativeExtractPath = "extracted#猫";
+ let extractor = new GMPExtractor(zipPath, relativeExtractPath);
+ let extractedPaths = await extractor.install();
+ // extractedPaths should contain the files extracted. In this case we
+ // should have a single file extracted to our profile dir -- the zip
+ // contains two files, but one should be skipped by the extraction logic.
+ Assert.equal(extractedPaths.length, 1, "One file should be extracted");
+ Assert.ok(
+ extractedPaths[0].includes("dummy_file.txt"),
+ "dummy_file.txt should be on extracted path"
+ );
+ Assert.ok(
+ !extractedPaths[0].includes("verified_contents.json"),
+ "verified_contents.json should not be on extracted path"
+ );
+ let extractedDir = FileUtils.getDir("ProfD", [relativeExtractPath], false);
+ Assert.ok(
+ extractedDir.exists(),
+ "Extraction should have created a directory"
+ );
+ let extractedFile = FileUtils.getDir(
+ "ProfD",
+ [relativeExtractPath, "dummy_file.txt"],
+ false
+ );
+ Assert.ok(
+ extractedFile.exists(),
+ "Extraction should have created dummy_file.txt"
+ );
+ let unextractedFile = FileUtils.getDir(
+ "ProfD",
+ [relativeExtractPath, "verified_contents.json"],
+ false
+ );
+ Assert.ok(
+ !unextractedFile.exists(),
+ "Extraction should not have created verified_contents.json"
+ );
+});
+
+/**
+ * Returns the read stream into a string
+ */
+function readStringFromInputStream(inputStream) {
+ let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ sis.init(inputStream);
+ let text = sis.read(sis.available());
+ sis.close();
+ return text;
+}
+
+/**
+ * Reads a string of text from a file.
+ * This function only works with ASCII text.
+ */
+function readStringFromFile(file) {
+ if (!file.exists()) {
+ info("readStringFromFile - file doesn't exist: " + file.path);
+ return null;
+ }
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ fis.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+ return readStringFromInputStream(fis);
+}
+
+/**
+ * Bare bones XMLHttpRequest implementation for testing onprogress, onerror,
+ * and onload nsIDomEventListener handleEvent.
+ */
+function makeHandler(aVal) {
+ if (typeof aVal == "function") {
+ return { handleEvent: aVal };
+ }
+ return aVal;
+}
+/**
+ * Constructs a mock xhr which is used for testing different aspects
+ * of responses.
+ */
+function xhr(inputStatus, inputResponse, options) {
+ this.inputStatus = inputStatus;
+ this.inputResponse = inputResponse;
+ this.status = 0;
+ this.responseXML = null;
+ this._aborted = false;
+ this._onabort = null;
+ this._onprogress = null;
+ this._onerror = null;
+ this._onload = null;
+ this._onloadend = null;
+ this._ontimeout = null;
+ this._url = null;
+ this._method = null;
+ this._timeout = 0;
+ this._notified = false;
+ this._options = options || {};
+}
+xhr.prototype = {
+ overrideMimeType(aMimetype) {},
+ setRequestHeader(aHeader, aValue) {},
+ status: null,
+ channel: { set notificationCallbacks(aVal) {} },
+ open(aMethod, aUrl) {
+ this.channel.originalURI = Services.io.newURI(aUrl);
+ this._method = aMethod;
+ this._url = aUrl;
+ },
+ abort() {
+ this._dropRequest = true;
+ this._notify(["abort", "loadend"]);
+ },
+ responseXML: null,
+ responseText: null,
+ send(aBody) {
+ executeSoon(() => {
+ try {
+ if (this._options.dropRequest) {
+ if (this._timeout > 0 && this._options.timeout) {
+ this._notify(["timeout", "loadend"]);
+ }
+ return;
+ }
+ this.status = this.inputStatus;
+ this.responseText = this.inputResponse;
+ try {
+ let parser = new DOMParser();
+ this.responseXML = parser.parseFromString(
+ this.inputResponse,
+ "application/xml"
+ );
+ } catch (e) {
+ this.responseXML = null;
+ }
+ if (this.inputStatus === 200) {
+ this._notify(["load", "loadend"]);
+ } else {
+ this._notify(["error", "loadend"]);
+ }
+ } catch (ex) {
+ do_throw(ex);
+ }
+ });
+ },
+ set onabort(aValue) {
+ this._onabort = makeHandler(aValue);
+ },
+ get onabort() {
+ return this._onabort;
+ },
+ set onprogress(aValue) {
+ this._onprogress = makeHandler(aValue);
+ },
+ get onprogress() {
+ return this._onprogress;
+ },
+ set onerror(aValue) {
+ this._onerror = makeHandler(aValue);
+ },
+ get onerror() {
+ return this._onerror;
+ },
+ set onload(aValue) {
+ this._onload = makeHandler(aValue);
+ },
+ get onload() {
+ return this._onload;
+ },
+ set onloadend(aValue) {
+ this._onloadend = makeHandler(aValue);
+ },
+ get onloadend() {
+ return this._onloadend;
+ },
+ set ontimeout(aValue) {
+ this._ontimeout = makeHandler(aValue);
+ },
+ get ontimeout() {
+ return this._ontimeout;
+ },
+ set timeout(aValue) {
+ this._timeout = aValue;
+ },
+ _notify(events) {
+ if (this._notified) {
+ return;
+ }
+ this._notified = true;
+ for (let item of events) {
+ let k = "on" + item;
+ if (this[k]) {
+ info("Notifying " + item);
+ let e = {
+ target: this,
+ type: item,
+ };
+ this[k](e);
+ } else {
+ info("Notifying " + item + ", but there are no listeners");
+ }
+ }
+ },
+ addEventListener(aEvent, aValue, aCapturing) {
+ // eslint-disable-next-line no-eval
+ eval("this._on" + aEvent + " = aValue");
+ },
+ get wrappedJSObject() {
+ return this;
+ },
+};
+
+/**
+ * Helper used to overrideXHR requests (no matter to what URL) with the
+ * specified status and response.
+ * @param status The status you want to get back when an XHR request is made
+ * @param response The response you want to get back when an XHR request is made
+ */
+function overrideXHR(status, response, options) {
+ overrideXHR.myxhr = new xhr(status, response, options);
+ ProductAddonCheckerScope.CreateXHR = function() {
+ return overrideXHR.myxhr;
+ };
+ return overrideXHR.myxhr;
+}
+
+/**
+ * Creates a new zip file containing a file with the specified data
+ * @param zipName The name of the zip file
+ * @param data The data to go inside the zip for the filename entry1.info
+ */
+function createNewZipFile(zipName, data) {
+ // Create a zip file which will be used for extracting
+ let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ stream.setData(data, data.length);
+ let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(
+ Ci.nsIZipWriter
+ );
+ let zipFile = FileUtils.getFile("TmpD", [zipName]);
+ if (zipFile.exists()) {
+ zipFile.remove(false);
+ }
+ // From prio.h
+ const PR_RDWR = 0x04;
+ const PR_CREATE_FILE = 0x08;
+ const PR_TRUNCATE = 0x20;
+ zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+ zipWriter.addEntryStream(
+ "entry1.info",
+ Date.now() * PR_USEC_PER_MSEC,
+ Ci.nsIZipWriter.COMPRESSION_BEST,
+ stream,
+ false
+ );
+ zipWriter.close();
+ stream.close();
+ info("zip file created on disk at: " + zipFile.path);
+ return zipFile;
+}