Bug 915879 - Wait for _writeFile to finish before continuing. r=fabrice
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Fri, 25 Apr 2014 11:49:00 -0400
changeset 198814 27207b8dc546ce9621c1c70d2c895a4079e45036
parent 198813 c00e3be62b888eb20266b6d5266b41b47bfdb174
child 198815 1676c8fa75462153b00aafbe31fbc1e161ee1a60
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs915879
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 915879 - Wait for _writeFile to finish before continuing. r=fabrice
dom/apps/src/Webapps.jsm
dom/apps/tests/mochitest.ini
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1300,18 +1300,19 @@ this.DOMApplicationRegistry = {
       this.broadcastMessage("Webapps:FireEvent", {
         eventType: "downloaderror",
         manifestURL: app.manifestURL
       });
     });
     AppDownloadManager.remove(aManifestURL);
   },
 
-  startDownload: function startDownload(aManifestURL) {
+  startDownload: Task.async(function*(aManifestURL) {
     debug("startDownload for " + aManifestURL);
+
     let id = this._appIdForManifestURL(aManifestURL);
     let app = this.webapps[id];
     if (!app) {
       debug("startDownload: No app found for " + aManifestURL);
       return;
     }
 
     if (app.downloading) {
@@ -1348,89 +1349,90 @@ this.DOMApplicationRegistry = {
                                  ["webapps", id,
                                   isUpdate ? "staged-update.webapp"
                                            : "update.webapp"],
                                  true);
 
     if (!file.exists()) {
       // This is a hosted app, let's check if it has an appcache
       // and download it.
-      this._readManifests([{ id: id }]).then((aResults) => {
-        let jsonManifest = aResults[0].manifest;
-        let manifest = new ManifestHelper(jsonManifest, app.origin);
-
-        if (manifest.appcache_path) {
-          debug("appcache found");
-          this.startOfflineCacheDownload(manifest, app, null, isUpdate);
-        } else {
-          // Hosted app with no appcache, nothing to do, but we fire a
-          // downloaded event.
-          debug("No appcache found, sending 'downloaded' for " + aManifestURL);
-          app.downloadAvailable = false;
-          this._saveApps().then(() => {
-            this.broadcastMessage("Webapps:UpdateState", {
-              app: app,
-              manifest: jsonManifest,
-              manifestURL: aManifestURL
-            });
-            this.broadcastMessage("Webapps:FireEvent", {
-              eventType: "downloadsuccess",
-              manifestURL: aManifestURL
-            });
-          });
-        }
-      });
+      let results = yield this._readManifests([{ id: id }]);
+
+      let jsonManifest = results[0].manifest;
+      let manifest = new ManifestHelper(jsonManifest, app.origin);
+
+      if (manifest.appcache_path) {
+        debug("appcache found");
+        this.startOfflineCacheDownload(manifest, app, null, isUpdate);
+      } else {
+        // Hosted app with no appcache, nothing to do, but we fire a
+        // downloaded event.
+        debug("No appcache found, sending 'downloaded' for " + aManifestURL);
+        app.downloadAvailable = false;
+
+        yield this._saveApps();
+
+        this.broadcastMessage("Webapps:UpdateState", {
+          app: app,
+          manifest: jsonManifest,
+          manifestURL: aManifestURL
+        });
+        this.broadcastMessage("Webapps:FireEvent", {
+          eventType: "downloadsuccess",
+          manifestURL: aManifestURL
+        });
+      }
 
       return;
     }
 
-    AppsUtils.loadJSONAsync(file.path).then((aJSON) => {
-      if (!aJSON) {
-        debug("startDownload: No update manifest found at " + file.path + " " +
-              aManifestURL);
-        return;
-      }
-
-      let manifest = new ManifestHelper(aJSON, app.installOrigin);
-      this.downloadPackage(manifest, {
-          manifestURL: aManifestURL,
-          origin: app.origin,
-          installOrigin: app.installOrigin,
-          downloadSize: app.downloadSize
-        }, isUpdate).then(function([aId, aManifest]) {
-          // Success! Keep the zip in of TmpD, we'll move it out when
-          // applyDownload() will be called.
-          // Save the manifest in TmpD also
-          let manFile = OS.Path.join(OS.Constants.Path.tmpDir, "webapps", aId,
-                                     "manifest.webapp");
-          DOMApplicationRegistry._writeFile(manFile, JSON.stringify(aManifest));
-
-          app = DOMApplicationRegistry.webapps[aId];
-          // Set state and fire events.
-          app.downloading = false;
-          app.downloadAvailable = false;
-          app.readyToApplyDownload = true;
-          app.updateTime = Date.now();
-          DOMApplicationRegistry._saveApps().then(() => {
-            DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
-              app: app,
-              manifestURL: aManifestURL
-            });
-            DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
-              eventType: "downloadsuccess",
-              manifestURL: aManifestURL
-            });
-            if (app.installState == "pending") {
-              // We restarted a failed download, apply it automatically.
-              DOMApplicationRegistry.applyDownload(aManifestURL);
-            }
-          });
-        });
+    let json = yield AppsUtils.loadJSONAsync(file.path);
+    if (!json) {
+      debug("startDownload: No update manifest found at " + file.path + " " +
+            aManifestURL);
+      return;
+    }
+
+    let manifest = new ManifestHelper(json, app.installOrigin);
+    let [aId, aManifest] = yield this.downloadPackage(manifest, {
+        manifestURL: aManifestURL,
+        origin: app.origin,
+        installOrigin: app.installOrigin,
+        downloadSize: app.downloadSize
+      }, isUpdate);
+
+    // Success! Keep the zip in of TmpD, we'll move it out when
+    // applyDownload() will be called.
+    // Save the manifest in TmpD also
+    let manFile = OS.Path.join(OS.Constants.Path.tmpDir, "webapps", aId,
+                               "manifest.webapp");
+    yield this._writeFile(manFile, JSON.stringify(aManifest));
+
+    app = this.webapps[aId];
+    // Set state and fire events.
+    app.downloading = false;
+    app.downloadAvailable = false;
+    app.readyToApplyDownload = true;
+    app.updateTime = Date.now();
+
+    yield this._saveApps();
+
+    this.broadcastMessage("Webapps:UpdateState", {
+      app: app,
+      manifestURL: aManifestURL
     });
-  },
+    this.broadcastMessage("Webapps:FireEvent", {
+      eventType: "downloadsuccess",
+      manifestURL: aManifestURL
+    });
+    if (app.installState == "pending") {
+      // We restarted a failed download, apply it automatically.
+      this.applyDownload(aManifestURL);
+    }
+  }),
 
   applyDownload: function applyDownload(aManifestURL) {
     debug("applyDownload for " + aManifestURL);
     let id = this._appIdForManifestURL(aManifestURL);
     let app = this.webapps[id];
     if (!app || (app && !app.readyToApplyDownload)) {
       return;
     }
@@ -1861,50 +1863,50 @@ this.DOMApplicationRegistry = {
        getInterface: function(iid) {
          if (iid.equals(Ci.nsILoadContext))
            return this;
          throw Cr.NS_ERROR_NO_INTERFACE;
        }
      }
   },
 
-  updatePackagedApp: function(aData, aId, aApp, aNewManifest) {
+  updatePackagedApp: Task.async(function*(aData, aId, aApp, aNewManifest) {
     debug("updatePackagedApp");
 
     // Store the new update manifest.
     let dir = this._getAppDir(aId).path;
     let manFile = OS.Path.join(dir, "staged-update.webapp");
-    this._writeFile(manFile, JSON.stringify(aNewManifest));
+    yield this._writeFile(manFile, JSON.stringify(aNewManifest));
 
     let manifest = new ManifestHelper(aNewManifest, aApp.manifestURL);
     // A package is available: set downloadAvailable to fire the matching
     // event.
     aApp.downloadAvailable = true;
     aApp.downloadSize = manifest.size;
     aApp.updateManifest = aNewManifest;
-    this._saveApps().then(() => {
-      this.broadcastMessage("Webapps:UpdateState", {
-        app: aApp,
-        manifestURL: aApp.manifestURL
-      });
-      this.broadcastMessage("Webapps:FireEvent", {
-        eventType: "downloadavailable",
-        manifestURL: aApp.manifestURL,
-        requestID: aData.requestID
-      });
+    yield this._saveApps();
+
+    this.broadcastMessage("Webapps:UpdateState", {
+      app: aApp,
+      manifestURL: aApp.manifestURL
     });
-  },
+    this.broadcastMessage("Webapps:FireEvent", {
+      eventType: "downloadavailable",
+      manifestURL: aApp.manifestURL,
+      requestID: aData.requestID
+    });
+  }),
 
   // A hosted app is updated if the app manifest or the appcache needs
   // updating. Even if the app manifest has not changed, we still check
   // for changes in the app cache.
   // 'aNewManifest' would contain the updated app manifest if
   // it has actually been updated, while 'aOldManifest' contains the
   // stored app manifest.
-  updateHostedApp: function(aData, aId, aApp, aOldManifest, aNewManifest) {
+  updateHostedApp: Task.async(function*(aData, aId, aApp, aOldManifest, aNewManifest) {
     debug("updateHostedApp " + aData.manifestURL);
 
     // Clean up the deprecated manifest cache if needed.
     if (aId in this._manifestCache) {
       delete this._manifestCache[aId];
     }
 
     aApp.manifest = aNewManifest || aOldManifest;
@@ -1913,17 +1915,18 @@ this.DOMApplicationRegistry = {
     if (aNewManifest) {
       this.updateAppHandlers(aOldManifest, aNewManifest, aApp);
 
       this.notifyUpdateHandlers(AppsUtils.cloneAppObject(aApp), aNewManifest);
 
       // Store the new manifest.
       let dir = this._getAppDir(aId).path;
       let manFile = OS.Path.join(dir, "manifest.webapp");
-      this._writeFile(manFile, JSON.stringify(aNewManifest));
+      yield this._writeFile(manFile, JSON.stringify(aNewManifest));
+
       manifest = new ManifestHelper(aNewManifest, aApp.origin);
 
       if (supportUseCurrentProfile()) {
         // Update the permissions for this app.
         PermissionsInstaller.installPermissions({
           manifest: aApp.manifest,
           origin: aApp.origin,
           manifestURL: aData.manifestURL
@@ -1938,62 +1941,67 @@ this.DOMApplicationRegistry = {
       aApp.role = manifest.role || "";
       aApp.updateTime = Date.now();
     } else {
       manifest = new ManifestHelper(aOldManifest, aApp.origin);
     }
 
     // Update the registry.
     this.webapps[aId] = aApp;
-    this._saveApps().then(() => {
-      let reg = DOMApplicationRegistry;
-      if (!manifest.appcache_path) {
-        reg.broadcastMessage("Webapps:UpdateState", {
-          app: aApp,
-          manifest: aApp.manifest,
-          manifestURL: aApp.manifestURL
-        });
-        reg.broadcastMessage("Webapps:FireEvent", {
-          eventType: "downloadapplied",
-          manifestURL: aApp.manifestURL,
-          requestID: aData.requestID
-        });
-      } else {
-        // Check if the appcache is updatable, and send "downloadavailable" or
-        // "downloadapplied".
-        let updateObserver = {
-          observe: function(aSubject, aTopic, aObsData) {
-            debug("updateHostedApp: updateSvc.checkForUpdate return for " +
-                  aApp.manifestURL + " - event is " + aTopic);
-            let eventType =
-              aTopic == "offline-cache-update-available" ? "downloadavailable"
-                                                         : "downloadapplied";
-            aApp.downloadAvailable = (eventType == "downloadavailable");
-            reg._saveApps().then(() => {
-              reg.broadcastMessage("Webapps:UpdateState", {
-                app: aApp,
-                manifest: aApp.manifest,
-                manifestURL: aApp.manifestURL
-              });
-              reg.broadcastMessage("Webapps:FireEvent", {
-                eventType: eventType,
-                manifestURL: aApp.manifestURL,
-                requestID: aData.requestID
-              });
-            });
-          }
-        };
-        debug("updateHostedApp: updateSvc.checkForUpdate for " +
-              manifest.fullAppcachePath());
-        updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
-                                 aApp.localId, false, updateObserver);
-      }
-      delete aApp.manifest;
-    });
-  },
+    yield this._saveApps();
+
+    if (!manifest.appcache_path) {
+      this.broadcastMessage("Webapps:UpdateState", {
+        app: aApp,
+        manifest: aApp.manifest,
+        manifestURL: aApp.manifestURL
+      });
+      this.broadcastMessage("Webapps:FireEvent", {
+        eventType: "downloadapplied",
+        manifestURL: aApp.manifestURL,
+        requestID: aData.requestID
+      });
+    } else {
+      // Check if the appcache is updatable, and send "downloadavailable" or
+      // "downloadapplied".
+      debug("updateHostedApp: updateSvc.checkForUpdate for " +
+            manifest.fullAppcachePath());
+
+      let updateDeferred = Promise.defer();
+
+      updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
+                               aApp.localId, false,
+                               (aSubject, aTopic, aData) => updateDeferred.resolve(aTopic));
+
+      let topic = yield updateDeferred.promise;
+
+      debug("updateHostedApp: updateSvc.checkForUpdate return for " +
+            aApp.manifestURL + " - event is " + topic);
+
+      let eventType =
+        topic == "offline-cache-update-available" ? "downloadavailable"
+                                                  : "downloadapplied";
+
+      aApp.downloadAvailable = (eventType == "downloadavailable");
+      yield this._saveApps();
+
+      this.broadcastMessage("Webapps:UpdateState", {
+        app: aApp,
+        manifest: aApp.manifest,
+        manifestURL: aApp.manifestURL
+      });
+      this.broadcastMessage("Webapps:FireEvent", {
+        eventType: eventType,
+        manifestURL: aApp.manifestURL,
+        requestID: aData.requestID
+      });
+    }
+
+    delete aApp.manifest;
+  }),
 
   // Downloads the manifest and run checks, then eventually triggers the
   // installation UI.
   doInstall: function doInstall(aData, aMm) {
     let app = aData.app;
 
     let sendError = function sendError(aError) {
       aData.error = aError;
@@ -2545,80 +2553,80 @@ onInstallSuccessAck: function onInstallS
    * @param aInstallSuccessCallback {Function}
    *        the callback to call on install success
    *
    * Passed params:
    *
    * @param aId {Integer} the unique ID of the application
    * @param aManifest {Object} The manifest of the application
    */
-  _onDownloadPackage: function(aNewApp, aInstallSuccessCallback,
+  _onDownloadPackage: Task.async(function*(aNewApp, aInstallSuccessCallback,
                                [aId, aManifest]) {
     debug("_onDownloadPackage");
     // Success! Move the zip out of TmpD.
     let app = this.webapps[aId];
     let zipFile =
       FileUtils.getFile("TmpD", ["webapps", aId, "application.zip"], true);
     let dir = this._getAppDir(aId);
     zipFile.moveTo(dir, "application.zip");
     let tmpDir = FileUtils.getDir("TmpD", ["webapps", aId], true, true);
     try {
       tmpDir.remove(true);
     } catch(e) { }
 
     // Save the manifest
     let manFile = OS.Path.join(dir.path, "manifest.webapp");
-    this._writeFile(manFile, JSON.stringify(aManifest));
+    yield this._writeFile(manFile, JSON.stringify(aManifest));
     // Set state and fire events.
     app.installState = "installed";
     app.downloading = false;
     app.downloadAvailable = false;
-    this._saveApps().then(() => {
-      this.updateAppHandlers(null, aManifest, aNewApp);
-      // Clear the manifest cache in case it holds the update manifest.
-      if (aId in this._manifestCache) {
-        delete this._manifestCache[aId];
-      }
-
-      this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
-      Services.obs.notifyObservers(null, "webapps-installed",
-        JSON.stringify({ manifestURL: aNewApp.manifestURL }));
-
-      if (supportUseCurrentProfile()) {
-        // Update the permissions for this app.
-        PermissionsInstaller.installPermissions({
-          manifest: aManifest,
-          origin: aNewApp.origin,
-          manifestURL: aNewApp.manifestURL
-        }, true);
-      }
-
-      this.updateDataStore(this.webapps[aId].localId, aNewApp.origin,
-                           aNewApp.manifestURL, aManifest, aNewApp.appStatus);
-
-      this.broadcastMessage("Webapps:UpdateState", {
-        app: app,
+
+    yield this._saveApps();
+
+    this.updateAppHandlers(null, aManifest, aNewApp);
+    // Clear the manifest cache in case it holds the update manifest.
+    if (aId in this._manifestCache) {
+      delete this._manifestCache[aId];
+    }
+
+    this.broadcastMessage("Webapps:AddApp", { id: aId, app: aNewApp });
+    Services.obs.notifyObservers(null, "webapps-installed",
+      JSON.stringify({ manifestURL: aNewApp.manifestURL }));
+
+    if (supportUseCurrentProfile()) {
+      // Update the permissions for this app.
+      PermissionsInstaller.installPermissions({
         manifest: aManifest,
+        origin: aNewApp.origin,
         manifestURL: aNewApp.manifestURL
-      });
-
-      // Check if we have asm.js code to preload for this application.
-      ScriptPreloader.preload(aNewApp, aManifest)
-                     .then(() => {
-          this.broadcastMessage("Webapps:FireEvent", {
-            eventType: ["downloadsuccess", "downloadapplied"],
-            manifestURL: aNewApp.manifestURL
-          });
-          if (aInstallSuccessCallback) {
-            aInstallSuccessCallback(aManifest, zipFile.path);
-          }
-        }
-      );
+      }, true);
+    }
+
+    this.updateDataStore(this.webapps[aId].localId, aNewApp.origin,
+                         aNewApp.manifestURL, aManifest, aNewApp.appStatus);
+
+    this.broadcastMessage("Webapps:UpdateState", {
+      app: app,
+      manifest: aManifest,
+      manifestURL: aNewApp.manifestURL
     });
-  },
+
+    // Check if we have asm.js code to preload for this application.
+    yield ScriptPreloader.preload(aNewApp, aManifest);
+
+    this.broadcastMessage("Webapps:FireEvent", {
+      eventType: ["downloadsuccess", "downloadapplied"],
+      manifestURL: aNewApp.manifestURL
+    });
+
+    if (aInstallSuccessCallback) {
+      aInstallSuccessCallback(aManifest, zipFile.path);
+    }
+  }),
 
   _nextLocalId: function() {
     let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
 
     while (this.getManifestURLByLocalId(id)) {
       id++;
     }
 
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -8,18 +8,18 @@ support-files =
   file_cached_app.template.webapp
   file_hosted_app.template.webapp
   file_packaged_app.sjs
   file_packaged_app.template.html
   file_packaged_app.template.webapp
   signed_app.sjs
   signed_app_template.webapp
   signed/*
+  test_packaged_app_common.js
 
 [test_app_update.html]
 [test_bug_795164.html]
 [test_install_receipts.html]
-[test_packaged_app_common.js]
 [test_packaged_app_install.html]
 [test_packaged_app_update.html]
 [test_receipt_operations.html]
 [test_signed_pkg_install.html]
 [test_uninstall_errors.html]