Commit c5eae7ab authored by John Bieling's avatar John Bieling
Browse files

Bug 1735812 - Copy metadata of staged add-on before checking compatibility. r=robwu

parent dfca79f9
Loading
Loading
Loading
Loading
+9 −8
Original line number Diff line number Diff line
@@ -3941,14 +3941,6 @@ var XPIInstall = {

    let addon = await loadManifestFromFile(source, location);

    // Ensure a staged addon is compatible with the current running version of
    // Firefox.  If a prior version of the addon is installed, it will remain.
    if (!addon.isCompatible) {
      throw new Error(
        `Add-on ${addon.id} is not compatible with application version.`
      );
    }

    if (
      XPIDatabase.mustSign(addon.type) &&
      addon.signedState <= AddonManager.SIGNEDSTATE_MISSING
@@ -3958,8 +3950,17 @@ var XPIInstall = {
      );
    }

    // Import saved metadata before checking for compatibility.
    addon.importMetadata(metadata);

    // Ensure a staged addon is compatible with the current running version of
    // Firefox.  If a prior version of the addon is installed, it will remain.
    if (!addon.isCompatible) {
      throw new Error(
        `Add-on ${addon.id} is not compatible with application version.`
      );
    }

    logger.debug(`Processing install of ${id} in ${location.name}`);
    let existingAddon = XPIStates.findAddon(id);
    // This part of the startup file changes is called from
+32 −0
Original line number Diff line number Diff line
{
  "addons": {
    "test_delay_update_staged_webext@tests.mozilla.org": {
      "updates": [
        {
          "version": "2.0",
          "update_link": "http://example.com/addons/test_delay_update_staged_webextension_v2.xpi",
          "applications": {
            "gecko": {
              "strict_min_version": "1",
              "strict_max_version": "43"
            }
          }
        }
      ]
    },
    "test_delay_update_staged_webext_no_update_url@tests.mozilla.org": {
      "updates": [
        {
          "version": "2.0",
          "update_link": "http://example.com/addons/test_delay_update_staged_webextension_no_update_url_v2.xpi",
          "applications": {
            "gecko": {
              "strict_min_version": "1",
              "strict_max_version": "43"
            }
          }
        }
      ]
    }
  }
}
+176 −0
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ stageDir.append("staged");
const IGNORE_ID = "test_delay_update_ignore_webext@tests.mozilla.org";
const COMPLETE_ID = "test_delay_update_complete_webext@tests.mozilla.org";
const DEFER_ID = "test_delay_update_defer_webext@tests.mozilla.org";
const STAGED_ID = "test_delay_update_staged_webext@tests.mozilla.org";
const STAGED_NO_UPDATE_URL_ID =
  "test_delay_update_staged_webext_no_update_url@tests.mozilla.org";
const NOUPDATE_ID = "test_no_update_webext@tests.mozilla.org";

// Create and configure the HTTP server.
@@ -58,6 +61,35 @@ const ADDONS = {
      },
    },
  },
  test_delay_update_staged_webextension_v2: {
    "manifest.json": {
      manifest_version: 2,
      name: "Delay Upgrade",
      version: "2.0",
      applications: {
        gecko: {
          id: STAGED_ID,
          update_url: `http://example.com/data/test_delay_updates_staged.json`,
          strict_min_version: "1",
          strict_max_version: "41",
        },
      },
    },
  },
  test_delay_update_staged_webextension_no_update_url_v2: {
    "manifest.json": {
      manifest_version: 2,
      name: "Delay Upgrade",
      version: "2.0",
      applications: {
        gecko: {
          id: STAGED_NO_UPDATE_URL_ID,
          strict_min_version: "1",
          strict_max_version: "41",
        },
      },
    },
  },
  test_delay_update_ignore_webextension_v2: {
    "manifest.json": {
      manifest_version: 2,
@@ -330,6 +362,150 @@ add_task(async function delay_updates_defer() {
  await promiseShutdownManager();
});

// add-on registers upgrade listener to deny update, completes after restart,
// even though the updated XPI is incompatible - the information returned
// by the update server defined in its manifest returns a compatible range
add_task(async function delay_updates_staged() {
  await promiseStartupManager();

  let extension = ExtensionTestUtils.loadExtension({
    useAddonManager: "permanent",
    manifest: {
      version: "1.0",
      applications: {
        gecko: {
          id: STAGED_ID,
          update_url: `http://example.com/data/test_delay_updates_staged.json`,
        },
      },
    },
    background() {
      browser.runtime.onUpdateAvailable.addListener(details => {
        browser.test.sendMessage("denied");
      });
      browser.test.sendMessage("ready");
    },
  });

  await Promise.all([extension.startup(), extension.awaitMessage("ready")]);

  let addon = await promiseAddonByID(STAGED_ID);
  Assert.notEqual(addon, null);
  Assert.equal(addon.version, "1.0");
  Assert.equal(addon.name, "Generated extension");
  Assert.ok(addon.isCompatible);
  Assert.ok(!addon.appDisabled);
  Assert.ok(addon.isActive);
  Assert.equal(addon.type, "extension");

  let update = await promiseFindAddonUpdates(addon);
  let install = update.updateAvailable;
  await promiseCompleteAllInstalls([install]);

  Assert.equal(install.state, AddonManager.STATE_POSTPONED);

  // upgrade is initially postponed
  let addon_postponed = await promiseAddonByID(STAGED_ID);
  Assert.notEqual(addon_postponed, null);
  Assert.equal(addon_postponed.version, "1.0");
  Assert.equal(addon_postponed.name, "Generated extension");
  Assert.ok(addon_postponed.isCompatible);
  Assert.ok(!addon_postponed.appDisabled);
  Assert.ok(addon_postponed.isActive);
  Assert.equal(addon_postponed.type, "extension");

  // add-on reports an available upgrade, but denied it till next restart
  await extension.awaitMessage("denied");

  await promiseRestartManager();
  await extension.awaitStartup();

  // add-on should have been updated during restart
  let addon_upgraded = await promiseAddonByID(STAGED_ID);
  Assert.notEqual(addon_upgraded, null);
  Assert.equal(addon_upgraded.version, "2.0");
  Assert.equal(addon_upgraded.name, "Delay Upgrade");
  Assert.ok(addon_upgraded.isCompatible);
  Assert.ok(!addon_upgraded.appDisabled);
  Assert.ok(addon_upgraded.isActive);
  Assert.equal(addon_upgraded.type, "extension");

  await extension.unload();
  await promiseShutdownManager();
});

// add-on registers upgrade listener to deny update, does not complete after
// restart, because the updated XPI is incompatible - there is no update server
// defined in its manifest, which could return a compatible range
add_task(async function delay_updates_staged_no_update_url() {
  await promiseStartupManager();

  let extension = ExtensionTestUtils.loadExtension({
    useAddonManager: "permanent",
    manifest: {
      version: "1.0",
      applications: {
        gecko: {
          id: STAGED_NO_UPDATE_URL_ID,
          update_url: `http://example.com/data/test_delay_updates_staged.json`,
        },
      },
    },
    background() {
      browser.runtime.onUpdateAvailable.addListener(details => {
        browser.test.sendMessage("denied");
      });
      browser.test.sendMessage("ready");
    },
  });

  await Promise.all([extension.startup(), extension.awaitMessage("ready")]);

  let addon = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID);
  Assert.notEqual(addon, null);
  Assert.equal(addon.version, "1.0");
  Assert.equal(addon.name, "Generated extension");
  Assert.ok(addon.isCompatible);
  Assert.ok(!addon.appDisabled);
  Assert.ok(addon.isActive);
  Assert.equal(addon.type, "extension");

  let update = await promiseFindAddonUpdates(addon);
  let install = update.updateAvailable;
  await promiseCompleteAllInstalls([install]);

  Assert.equal(install.state, AddonManager.STATE_POSTPONED);

  // upgrade is initially postponed
  let addon_postponed = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID);
  Assert.notEqual(addon_postponed, null);
  Assert.equal(addon_postponed.version, "1.0");
  Assert.equal(addon_postponed.name, "Generated extension");
  Assert.ok(addon_postponed.isCompatible);
  Assert.ok(!addon_postponed.appDisabled);
  Assert.ok(addon_postponed.isActive);
  Assert.equal(addon_postponed.type, "extension");

  // add-on reports an available upgrade, but denied it till next restart
  await extension.awaitMessage("denied");

  await promiseRestartManager();
  await extension.awaitStartup();

  // add-on should not have been updated during restart
  let addon_upgraded = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID);
  Assert.notEqual(addon_upgraded, null);
  Assert.equal(addon_upgraded.version, "1.0");
  Assert.equal(addon_upgraded.name, "Generated extension");
  Assert.ok(addon_upgraded.isCompatible);
  Assert.ok(!addon_upgraded.appDisabled);
  Assert.ok(addon_upgraded.isActive);
  Assert.equal(addon_upgraded.type, "extension");

  await extension.unload();
  await promiseShutdownManager();
});

// browser.runtime.reload() without a pending upgrade should just reload.
add_task(async function runtime_reload() {
  await promiseStartupManager();