Bug 1446833: Part 4 - Make sure directory service overrides actually take effect. r?rhelmer draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 18 Mar 2018 15:55:08 -0700
changeset 769192 484948a133f9402850ee9d4ea40877bf8acdd7ed
parent 769191 a65cfa697c4537872b50d6cbbe0f49dae4c16b63
push id103063
push usermaglione.k@gmail.com
push dateSun, 18 Mar 2018 23:28:42 +0000
reviewersrhelmer
bugs1446833
milestone61.0a1
Bug 1446833: Part 4 - Make sure directory service overrides actually take effect. r?rhelmer The directory service caches certain directory entries, or has them set by other callers using `.set()`. When we try to override those directories with custom providers, the cached value still takes precedence. It might make more sense to just store the directory entry values directly in the directory service's hash, but this patch just takes the less obtrusive path of clearing cached values for keys that we override. This patch also fixes the few instances where add-on manager tests leave files in the global temporary directory which are now caught by the shutdown assertions. MozReview-Commit-ID: Jq92TngLO1L
testing/xpcshell/head.js
toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_harness.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_install_syntax_error.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -1102,16 +1102,26 @@ function do_get_profile(notifyProfileAft
       }
       return null;
     },
     QueryInterface: _XPCOMUtils.generateQI(["nsIDirectoryServiceProvider"]),
   };
   _Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
            .registerProvider(provider);
 
+  try {
+    _Services.dirsvc.undefine("TmpD");
+  } catch (e) {
+    // This throws if the key is not already registers, but that
+    // doesn't matter.
+    if (e.result != Cr.NS_ERROR_FAILURE) {
+      throw e;
+    }
+  }
+
   // We need to update the crash events directory when the profile changes.
   if (runningInParent &&
       "@mozilla.org/toolkit/crash-reporter;1" in Cc) {
     let crashReporter =
         Cc["@mozilla.org/toolkit/crash-reporter;1"]
           .getService(Ci.nsICrashReporter);
     crashReporter.UpdateCrashEventsDir();
   }
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -26,16 +26,19 @@ const {OS} = ChromeUtils.import("resourc
 
 ChromeUtils.defineModuleGetter(this, "Extension",
                                "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyGetter(this, "Management", () => {
   let {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
   return Management;
 });
 
+ChromeUtils.defineModuleGetter(this, "FileTestUtils",
+                               "resource://testing-common/FileTestUtils.jsm");
+
 XPCOMUtils.defineLazyServiceGetter(this, "aomStartup",
                                    "@mozilla.org/addons/addon-manager-startup;1",
                                    "amIAddonManagerStartup");
 XPCOMUtils.defineLazyServiceGetter(this, "rdfService",
                                    "@mozilla.org/rdf/rdf-service;1", "nsIRDFService");
 XPCOMUtils.defineLazyServiceGetter(this, "uuidGen",
                                    "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
 
@@ -290,22 +293,37 @@ var AddonTestUtils = {
       if (file.exists()) {
         throw new Error(`Test cleanup: path ${file.path} exists when it should not`);
       }
     }
 
     testScope.registerCleanupFunction(() => {
       this.cleanupTempXPIs();
 
+      let ignoreEntries = new Set();
+      {
+        // FileTestUtils lazily creates a directory to hold the temporary files
+        // it creates. If that directory exists, ignore it.
+        let {value} = Object.getOwnPropertyDescriptor(FileTestUtils,
+                                                      "_globalTemporaryDirectory");
+        if (value) {
+          ignoreEntries.add(value.leafName);
+        }
+      }
+
       // Check that the temporary directory is empty
       var dirEntries = this.tempDir.directoryEntries
                            .QueryInterface(Ci.nsIDirectoryEnumerator);
       var entries = [];
-      while (dirEntries.hasMoreElements())
-        entries.push(dirEntries.nextFile.leafName);
+      while (dirEntries.hasMoreElements()) {
+        let {leafName} = dirEntries.nextFile;
+        if (!ignoreEntries.has(leafName)) {
+          entries.push(leafName);
+        }
+      }
       if (entries.length)
         throw new Error(`Found unexpected files in temporary directory: ${entries.join(", ")}`);
 
       dirEntries.close();
 
       try {
         appDirForAddons.remove(true);
       } catch (ex) {
@@ -1049,16 +1067,26 @@ var AddonTestUtils = {
         if (prop == key)
           return dir.clone();
         return null;
       },
 
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
     };
     Services.dirsvc.registerProvider(dirProvider);
+
+    try {
+      Services.dirsvc.undefine(key);
+    } catch (e) {
+      // This throws if the key is not already registers, but that
+      // doesn't matter.
+      if (e.result != Cr.NS_ERROR_FAILURE) {
+        throw e;
+      }
+    }
   },
 
   /**
    * Returns a promise that resolves when the given add-on event is fired. The
    * resolved value is an array of arguments passed for the event.
    *
    * @param {string} event
    *        The name of the AddonListener event handler method for which
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -1388,16 +1388,28 @@ class AddonInstall {
       // Installation is already running
       return;
     default:
       throw new Error("Cannot start installing from this state");
     }
   }
 
   /**
+   * Called during XPIProvider shutdown so that we can do any necessary
+   * pre-shutdown cleanup.
+   */
+  onShutdown() {
+    switch (this.state) {
+    case AddonManager.STATE_POSTPONED:
+      this.removeTemporaryFile();
+      break;
+    }
+  }
+
+  /**
    * Cancels installation of this add-on.
    *
    * Note this method is overridden to handle additional state in
    * the subclass DownloadAddonInstall.
    *
    * @throws if installation cannot be cancelled from the current state
    */
   cancel() {
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2321,16 +2321,22 @@ var XPIProvider = {
     logger.debug("shutdown");
 
     // Stop anything we were doing asynchronously
     this.cancelAll();
 
     this.activeAddons.clear();
     this.allAppGlobal = true;
 
+    for (let install of this.installs) {
+      if (install.onShutdown()) {
+        install.onShutdown();
+      }
+    }
+
     // If there are pending operations then we must update the list of active
     // add-ons
     if (Services.prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) {
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_pending_ops", 1);
       XPIDatabase.updateActiveAddons();
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
     }
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_harness.js
@@ -0,0 +1,11 @@
+"use strict";
+
+// Test that the test harness is sane.
+
+// Test that the temporary directory is actually overridden in the
+// directory service.
+add_task(async function test_TmpD_override() {
+  equal(FileUtils.getDir("TmpD", []).path,
+        AddonTestUtils.tempDir.path,
+        "Should get the correct temporary directory from the directory service");
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install_syntax_error.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install_syntax_error.js
@@ -33,16 +33,17 @@ add_task(async function install_xpi() {
   Assert.equal(install1.error, AddonManager.ERROR_CORRUPT_FILE);
 
   // Replace xpi1 with xpi2 to have the same filename to reproduce install error
   xpi2.moveTo(xpi1.parent, xpi1.leafName);
 
   let install2 = await AddonManager.getInstallForFile(xpi2);
   Assert.notEqual(install2.error, AddonManager.ERROR_CORRUPT_FILE);
 
+  xpi1.remove(false);
 });
 
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
   startupManager();
 
   run_next_test();
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -12,16 +12,17 @@ support-files =
 tags = blocklist
 [test_blocklist_gfx.js]
 tags = blocklist
 [test_cache_certdb.js]
 run-if = addon_signing
 [test_cacheflush.js]
 [test_gmpProvider.js]
 skip-if = appname != "firefox"
+[test_harness.js]
 [test_invalid_install_rdf.js]
 [test_isReady.js]
 [test_pluginInfoURL.js]
 tags = blocklist
 [test_provider_markSafe.js]
 [test_provider_shutdown.js]
 [test_provider_unsafe_access_shutdown.js]
 [test_provider_unsafe_access_startup.js]