Bug 1402064 Refactor compatibility overrides draft
authorAndrew Swan <aswan@mozilla.com>
Mon, 05 Feb 2018 20:28:49 -0800
changeset 760254 df2a7d49f4419f57b52305c9e5f04e06b04d0871
parent 760253 3ef4331f360515578d9bb4f9054dd73e15a2fa4b
child 760255 38a774d21c4f3b4e9ff16fdbb23e03f5934f44ed
push id100589
push useraswan@mozilla.com
push dateTue, 27 Feb 2018 06:25:44 +0000
bugs1402064
milestone60.0a1
Bug 1402064 Refactor compatibility overrides Compatibility override data was previously stored in an object attached to each AddonInternal instance and awkwardly copied around in various places. In the new AMO API, compatibility overrides come from a different API endpoint -- lay the groundwork for maintaining that data separately inside AddonRepository by creating a new internal API for fetching compatibility override data. Note there is some code related to staged installs of non-restartless addons that refers to the old compatabilityOverrides property, that code should ideally get removed soon but for now it is unreachable. MozReview-Commit-ID: EUZhPsTc2q
toolkit/mozapps/extensions/internal/AddonRepository.jsm
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/internal/XPIProviderUtils.js
toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -401,16 +401,50 @@ var AddonRepository = {
   },
 
   isMetadataStale() {
     let threshold = Services.prefs.getIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC, DEFAULT_METADATA_UPDATETHRESHOLD_SEC);
     return (this.metadataAge() > threshold);
   },
 
   /**
+   * Get any compatibility override information for the given add-on id.
+   *
+   * @param aId
+   *        The id of the add-on to fetch compatibiltiy override data for.
+   * @returns Promise
+   *          A Promise which resolves with the compatibility override
+   *          data for the given add-on, if any is available.
+   */
+  async getCompatibilityOverrides(aId) {
+    let addon = await new Promise(resolve => this.getCachedAddonByID(aId, resolve));
+    return addon ? addon.compatibilityOverrides : null;
+  },
+
+  /**
+   * Synchronously get any compatibility override information for
+   * the given add-on id.
+   *
+   * @param aId
+   *        The id of the add-on to fetch compatibiltiy override data for.
+   * @returns object
+   *          Compatibility override data for the given add-on, if any is
+   *          available.  Note that this method does not do any I/O so if
+   *          the database has not been read and cacheAddons() has not been
+   *          called for the given id, this may return null even when there
+   *          is a compatibility override for the addon.
+   */
+  getCompatibilityOverridesSync(aId) {
+    if (this._addons == null || !this._addons.has(aId)) {
+      return null;
+    }
+    return this._addons.get(aId).compatibilityOverrides;
+  },
+
+  /**
    * Asynchronously get a cached add-on by id. The add-on (or null if the
    * add-on is not found) is passed to the specified callback. If caching is
    * disabled, null is passed to the specified callback.
    *
    * @param  aId
    *         The id of the add-on to get
    * @param  aCallback
    *         The callback to pass the result back to
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -1627,19 +1627,16 @@ class AddonInstall {
     // It wasn't there so try to re-download it
     if (!repoAddon) {
       await new Promise(resolve => AddonRepository.cacheAddons([this.addon.id], resolve));
       repoAddon = await new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
     }
 
     this.addon._repositoryAddon = repoAddon;
     this.name = this.name || this.addon._repositoryAddon.name;
-    this.addon.compatibilityOverrides = repoAddon ?
-      repoAddon.compatibilityOverrides :
-      null;
     this.addon.appDisabled = !isUsableAddon(this.addon);
     return undefined;
   }
 
   getIcon(desiredSize = 64) {
     if (!this.addon.icons || !this.file) {
       return null;
     }
@@ -2721,17 +2718,17 @@ UpdateChecker.prototype = {
   },
 
   /**
    * Called when AddonUpdateChecker completes the update check
    *
    * @param  updates
    *         The list of update details for the add-on
    */
-  onUpdateCheckComplete(aUpdates) {
+  async onUpdateCheckComplete(aUpdates) {
     XPIProvider.done(this.addon._updateCheck);
     this.addon._updateCheck = null;
     let AUC = AddonUpdateChecker;
 
     let ignoreMaxVersion = false;
     let ignoreStrictCompat = false;
     if (!AddonManager.checkCompatibility) {
       ignoreMaxVersion = true;
@@ -2779,18 +2776,18 @@ UpdateChecker.prototype = {
       } else {
         aSelf.callListener("onNoUpdateAvailable", aSelf.addon.wrapper);
       }
       aSelf.callListener("onUpdateFinished", aSelf.addon.wrapper,
                          AddonManager.UPDATE_STATUS_NO_ERROR);
     }
 
     let compatOverrides = AddonManager.strictCompatibility ?
-                            null :
-                            this.addon.compatibilityOverrides;
+                          null :
+                          await AddonRepository.getCompatibilityOverrides(this.addon.id);
 
     let update = AUC.getNewestCompatibleUpdate(aUpdates,
                                            this.appVersion,
                                            this.platformVersion,
                                            ignoreMaxVersion,
                                            ignoreStrictCompat,
                                            compatOverrides);
 
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -3933,17 +3933,16 @@ var XPIProvider = {
           aCallback();
       }
 
       for (let addon of aAddons) {
         AddonRepository.getCachedAddonByID(addon.id, aRepoAddon => {
           if (aRepoAddon) {
             logger.debug("updateAddonRepositoryData got info for " + addon.id);
             addon._repositoryAddon = aRepoAddon;
-            addon.compatibilityOverrides = aRepoAddon.compatibilityOverrides;
             this.updateAddonDisabledState(addon);
           }
 
           notifyComplete();
         });
       }
     });
   },
@@ -4994,23 +4993,23 @@ AddonInternal.prototype = {
     // Only extensions and dictionaries can be compatible by default; themes
     // and language packs always use strict compatibility checking.
     if (this.type in COMPATIBLE_BY_DEFAULT_TYPES &&
         !AddonManager.strictCompatibility && !this.strictCompatibility &&
         !this.hasBinaryComponents) {
 
       // The repository can specify compatibility overrides.
       // Note: For now, only blacklisting is supported by overrides.
-      if (this._repositoryAddon &&
-          this._repositoryAddon.compatibilityOverrides) {
-        let overrides = this._repositoryAddon.compatibilityOverrides;
+      let overrides = AddonRepository.getCompatibilityOverridesSync(this.id);
+      if (overrides) {
         let override = AddonRepository.findMatchingCompatOverride(this.version,
                                                                   overrides);
-        if (override && override.type == "incompatible")
+        if (override) {
           return false;
+        }
       }
 
       // Extremely old extensions should not be compatible by default.
       let minCompatVersion;
       if (app.id == Services.appinfo.ID)
         minCompatVersion = XPIProvider.minCompatibleAppVersion;
       else if (app.id == TOOLKIT_ID)
         minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
@@ -5741,17 +5740,17 @@ function defineAddonWrapperProperty(name
     get: getter,
     enumerable: true,
   });
 }
 
 ["id", "syncGUID", "version", "isCompatible", "isPlatformCompatible",
  "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
  "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
- "strictCompatibility", "compatibilityOverrides", "updateURL", "dependencies",
+ "strictCompatibility", "updateURL", "dependencies",
  "getDataDirectory", "multiprocessCompatible", "signedState", "mpcOptedOut",
  "isCorrectlySigned"].forEach(function(aProp) {
    defineAddonWrapperProperty(aProp, function() {
      let addon = addonFor(this);
      return (aProp in addon) ? addon[aProp] : undefined;
    });
 });
 
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -73,19 +73,16 @@ const ASYNC_SAVE_DELAY_MS = 20;
  */
 function getRepositoryAddon(aAddon, aCallback) {
   if (!aAddon) {
     aCallback(aAddon);
     return;
   }
   function completeAddon(aRepositoryAddon) {
     aAddon._repositoryAddon = aRepositoryAddon;
-    aAddon.compatibilityOverrides = aRepositoryAddon ?
-                                      aRepositoryAddon.compatibilityOverrides :
-                                      null;
     aCallback(aAddon);
   }
   AddonRepository.getCachedAddonByID(aAddon.id, completeAddon);
 }
 
 /**
  * Wrap an API-supplied function in an exception handler to make it safe to call
  */
--- a/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
@@ -182,68 +182,74 @@ function check_compat_status(aCallback) 
                                "addon6@tests.mozilla.org",
                                "addon7@tests.mozilla.org",
                                "addon8@tests.mozilla.org",
                                "addon9@tests.mozilla.org",
                                "addon10@tests.mozilla.org"],
                               function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]) {
 
     Assert.notEqual(a1, null);
-    Assert.equal(a1.compatibilityOverrides, null);
+    Assert.equal(AddonRepository.getCompatibilityOverridesSync(a1.id), null);
     Assert.ok(a1.isCompatible);
     Assert.ok(!a1.appDisabled);
 
     Assert.notEqual(a2, null);
-    Assert.equal(a2.compatibilityOverrides, null);
+    Assert.equal(AddonRepository.getCompatibilityOverridesSync(a2.id), null);
     Assert.ok(a2.isCompatible);
     Assert.ok(!a2.appDisabled);
 
     Assert.notEqual(a3, null);
-    Assert.notEqual(a3.compatibilityOverrides, null);
-    Assert.equal(a3.compatibilityOverrides.length, 1);
+    let overrides = AddonRepository.getCompatibilityOverridesSync(a3.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 1);
     Assert.ok(!a3.isCompatible);
     Assert.ok(a3.appDisabled);
 
     Assert.notEqual(a4, null);
-    Assert.notEqual(a4.compatibilityOverrides, null);
-    Assert.equal(a4.compatibilityOverrides.length, 1);
+    overrides = AddonRepository.getCompatibilityOverridesSync(a4.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 1);
     Assert.ok(!a4.isCompatible);
     Assert.ok(a4.appDisabled);
 
     Assert.notEqual(a5, null);
-    Assert.equal(a5.compatibilityOverrides, null);
+    Assert.equal(AddonRepository.getCompatibilityOverridesSync(a5.id), null);
     Assert.ok(a5.isCompatible);
     Assert.ok(!a5.appDisabled);
 
     Assert.notEqual(a6, null);
-    Assert.notEqual(a6.compatibilityOverrides, null);
-    Assert.equal(a6.compatibilityOverrides.length, 1);
+    overrides = AddonRepository.getCompatibilityOverridesSync(a6.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 1);
     Assert.ok(a6.isCompatible);
     Assert.ok(!a6.appDisabled);
 
     Assert.notEqual(a7, null);
-    Assert.notEqual(a7.compatibilityOverrides, null);
-    Assert.equal(a7.compatibilityOverrides.length, 1);
+    overrides = AddonRepository.getCompatibilityOverridesSync(a7.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 1);
     Assert.ok(a7.isCompatible);
     Assert.ok(!a7.appDisabled);
 
     Assert.notEqual(a8, null);
-    Assert.notEqual(a8.compatibilityOverrides, null);
-    Assert.equal(a8.compatibilityOverrides.length, 3);
+    overrides = AddonRepository.getCompatibilityOverridesSync(a8.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 3);
     Assert.ok(!a8.isCompatible);
     Assert.ok(a8.appDisabled);
 
     Assert.notEqual(a9, null);
-    Assert.notEqual(a9.compatibilityOverrides, null);
-    Assert.equal(a9.compatibilityOverrides.length, 1);
+    overrides = AddonRepository.getCompatibilityOverridesSync(a9.id);
+    Assert.notEqual(overrides, null);
+    Assert.equal(overrides.length, 1);
     Assert.ok(!a9.isCompatible);
     Assert.ok(a9.appDisabled);
 
     Assert.notEqual(a10, null);
-    Assert.equal(a10.compatibilityOverrides, null);
+    Assert.equal(AddonRepository.getCompatibilityOverridesSync(a10.id), null);
     Assert.ok(a10.isCompatible);
     Assert.ok(!a10.appDisabled);
 
     executeSoon(aCallback);
   });
 }
 
 function run_test_1() {