Commit 4329bf0e authored by Robert Helmer's avatar Robert Helmer
Browse files

Bug 1249689 - generate and provide a Symbol for each add-on on startup r=mossop

MozReview-Commit-ID: LoPGSrJtlkr

--HG--
extra : rebase_source : e2ce9f017213bce7b78567df7f086d36fe14229a
parent b0f85da2
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -2246,6 +2246,28 @@ var AddonManagerInternal = {
                               .installTemporaryAddon(aFile);
  },

  /**
   * Returns an Addon corresponding to an instance ID.
   * @param aInstanceID
   *        An Addon Instance ID symbol
   * @return {Promise}
   * @resolves The found Addon or null if no such add-on exists.
   * @rejects  Never
   * @throws if the aInstanceID argument is not specified
   *         or the AddonManager is not initialized
   */
   getAddonByInstanceID: function(aInstanceID) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);

     if (!aInstanceID || typeof aInstanceID != "symbol")
       throw Components.Exception("aInstanceID must be a Symbol()",
                                  Cr.NS_ERROR_INVALID_ARG);

     return AddonManagerInternal._getProviderByName("XPIProvider")
                                .getAddonByInstanceID(aInstanceID);
   },

  /**
   * Gets an icon from the icon set provided by the add-on
@@ -3198,6 +3220,10 @@ this.AddonManager = {
    return AddonManagerInternal.installTemporaryAddon(aDirectory);
  },

  getAddonByInstanceID: function(aInstanceID) {
    return AddonManagerInternal.getAddonByInstanceID(aInstanceID);
  },

  addManagerListener: function(aListener) {
    AddonManagerInternal.addManagerListener(aListener);
  },
+34 −0
Original line number Diff line number Diff line
@@ -3881,6 +3881,29 @@ this.XPIProvider = {
    AddonManagerPrivate.callAddonListeners("onInstalled", addon.wrapper);
  }),

  /**
   * Returns an Addon corresponding to an instance ID.
   * @param aInstanceID
   *        An Addon Instance ID
   * @return {Promise}
   * @resolves The found Addon or null if no such add-on exists.
   * @rejects  Never
   * @throws if the aInstanceID argument is not specified
   */
   getAddonByInstanceID: function(aInstanceID) {
     if (!aInstanceID || typeof aInstanceID != "symbol")
       throw Components.Exception("aInstanceID must be a Symbol()",
                                  Cr.NS_ERROR_INVALID_ARG);

     for (let [id, val] of this.activeAddons) {
       if (aInstanceID == val.instanceID) {
         return new Promise(resolve => this.getAddonByID(id, resolve));
       }
     }

     return Promise.resolve(null);
   },

  /**
   * Removes an AddonInstall from the list of active installs.
   *
@@ -4451,6 +4474,8 @@ this.XPIProvider = {

    this.activeAddons.set(aId, {
      bootstrapScope: null,
      // a Symbol passed to this add-on, which it can use to identify itself
      instanceID: Symbol(aId),
    });
    let activeAddon = this.activeAddons.get(aId);

@@ -4598,6 +4623,15 @@ this.XPIProvider = {
        activeAddon = this.activeAddons.get(aAddon.id);
      }

      if (aAddon.bootstrap) {
        if (aMethod == "startup" || aMethod == "shutdown") {
          if (!aExtraParams) {
            aExtraParams = {};
          }
          aExtraParams["instanceID"] = this.activeAddons.get(aAddon.id).instanceID;
        }
      }

      // Nothing to call for locales
      if (aAddon.type == "locale")
        return;
+62 −0
Original line number Diff line number Diff line
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/AddonManager.jsm");

const PASS_PREF = "symboltest.instanceid.pass";
const FAIL_BOGUS_PREF = "symboltest.instanceid.fail_bogus";
const FAIL_ID_PREF = "symboltest.instanceid.fail_bogus";
const ADDON_ID = "test_symbol@tests.mozilla.org";

function install(data, reason) {}

// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside `XPIProvider.jsm`.
function startup(data, reason) {
  Services.prefs.setBoolPref(PASS_PREF, false);
  Services.prefs.setBoolPref(FAIL_BOGUS_PREF, false);
  Services.prefs.setBoolPref(FAIL_ID_PREF, false);

  // test with the correct symbol
  if (data.hasOwnProperty("instanceID") && data.instanceID) {
    AddonManager.getAddonByInstanceID(data.instanceID)
      .then(addon => {
        if (addon.id == ADDON_ID) {
          Services.prefs.setBoolPref(PASS_PREF, true);
        }
      }).catch(err => {
        throw Error("no addon found for symbol");
      });

  }

  // test with a totally bogus symbol
  AddonManager.getAddonByInstanceID(Symbol("bad symbol"))
    .then(addon => {
      // there is no symbol by this name, so null should be returned
      if (addon == null) {
        Services.prefs.setBoolPref(FAIL_BOGUS_PREF, true);
      } else {
        throw Error("bad symbol should not match:", addon);
      }
    }).catch(err => {
      throw Error("promise should not have rejected: " + err);
    });

  // try to make a matching symbol - this should fail because it's not a
  // reference to the same symbol stored inside the addons manager.
  AddonManager.getAddonByInstanceID(Symbol(ADDON_ID))
    .then(addon => {
      // there is no symbol by this name, so null should be returned
      if (addon == null) {
        Services.prefs.setBoolPref(FAIL_ID_PREF, true);
      } else {
        throw Error("bad symbol should not match:", addon);
      }
    }).catch(err => {
      throw Error("promise should not have rejected: " + err);
    });

}

function shutdown(data, reason) {}

function uninstall(data, reason) {}
+28 −0
Original line number Diff line number Diff line
<?xml version="1.0"?>

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">

  <Description about="urn:mozilla:install-manifest">
    <em:id>test_symbol@tests.mozilla.org</em:id>
    <em:version>1.0</em:version>
    <em:bootstrap>true</em:bootstrap>

    <!-- Front End MetaData -->
    <em:name>Test Symbol</em:name>
    <em:description>Test Description</em:description>

    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>

    <em:targetApplication>
      <Description>
        <em:id>xpcshell@tests.mozilla.org</em:id>
        <em:minVersion>1</em:minVersion>
        <em:maxVersion>1</em:maxVersion>
      </Description>
    </em:targetApplication>

  </Description>
</RDF>
+43 −0
Original line number Diff line number Diff line
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

const PASS_PREF = "symboltest.instanceid.pass";
const FAIL_BOGUS_PREF = "symboltest.instanceid.fail_bogus";
const FAIL_ID_PREF = "symboltest.instanceid.fail_bogus";
const ADDON_ID = "test_symbol@tests.mozilla.org";

createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
startupManager();

BootstrapMonitor.init();

// symbol is passed when add-on is installed
add_task(function*() {
  for (let pref of [PASS_PREF, FAIL_BOGUS_PREF, FAIL_ID_PREF])
    Services.prefs.clearUserPref(pref);

  yield promiseInstallAllFiles([do_get_addon("test_symbol")], true);

  let addon = yield promiseAddonByID(ADDON_ID);

  do_check_neq(addon, null);
  do_check_eq(addon.version, "1.0");
  do_check_eq(addon.name, "Test Symbol");
  do_check_true(addon.isCompatible);
  do_check_false(addon.appDisabled);
  do_check_true(addon.isActive);
  do_check_eq(addon.type, "extension");

  // most of the test is in bootstrap.js in the addon because BootstrapMonitor
  // currently requires the objects in `data` to be serializable, and we
  // need a real reference to the symbol to test this.
  do_execute_soon(function() {
    // give the startup time to run
    do_check_true(Services.prefs.getBoolPref(PASS_PREF));
    do_check_true(Services.prefs.getBoolPref(FAIL_BOGUS_PREF));
    do_check_true(Services.prefs.getBoolPref(FAIL_ID_PREF));
  });

  yield promiseRestartManager();
});
Loading