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 180596 27207b8dc546ce9621c1c70d2c895a4079e45036
parent 180595 c00e3be62b888eb20266b6d5266b41b47bfdb174
child 180597 1676c8fa75462153b00aafbe31fbc1e161ee1a60
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersfabrice
bugs915879
milestone31.0a1
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]