Bug 981249 - Test app launch r= myk
☠☠ backed out by ffaf9cf6616c ☠ ☠
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Mon, 24 Mar 2014 15:14:00 +0100
changeset 175493 6d2ffe8dc04699778a59491472d1532d59d5de9a
parent 175492 6a72a2510405173f1ca3b4667c44295e35b06139
child 175494 cafd9175112f6bff12705dc822c7f5f7d37914a9
push idunknown
push userunknown
push dateunknown
bugs981249
milestone31.0a1
Bug 981249 - Test app launch r= myk
testing/profiles/webapps_mochitest.json
toolkit/webapps/tests/app.sjs
toolkit/webapps/tests/chrome.ini
toolkit/webapps/tests/data/app/index.html
toolkit/webapps/tests/data/app/manifest.webapp
toolkit/webapps/tests/head.js
toolkit/webapps/tests/test_hosted.xul
toolkit/webapps/tests/test_hosted_launch.xul
toolkit/webapps/tests/test_packaged.xul
toolkit/webapps/tests/test_packaged_launch.xul
webapprt/CommandLineHandler.js
webapprt/Startup.jsm
webapprt/WebappRT.jsm
--- a/testing/profiles/webapps_mochitest.json
+++ b/testing/profiles/webapps_mochitest.json
@@ -89,10 +89,26 @@
   },
   {
     "name": "https_a_domain_privileged",
     "csp": "",
     "origin": "https://aprivileged.com",
     "manifestURL": "https://aprivileged.com/manifest.webapp",
     "description": "https://aprivileged.com Privileged App ",
     "appStatus": 2
+  },
+  {
+    "name": "test_desktop_hosted_launch",
+    "csp": "",
+    "origin": "http://127.0.0.1:8888/",
+    "manifestURL": "http://127.0.0.1:8888/sample.manifest",
+    "description": "Hosted app",
+    "appStatus": 1
+  },
+  {
+    "name": "test_desktop_packaged_launch",
+    "csp": "",
+    "origin": "app://test_desktop_packaged_launch",
+    "manifestURL": "http://127.0.0.1:8888/sample.manifest",
+    "description": "Packaged App",
+    "appStatus": 2
   }
 ]
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/app.sjs
@@ -0,0 +1,33 @@
+function getQuery(request) {
+  let query = {};
+
+  request.queryString.split('&').forEach(function(val) {
+    let [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+
+  return query;
+}
+
+function handleRequest(request, response) {
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  let query = getQuery(request);
+
+  if ("appreq" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write("Hello world!");
+
+    setState("appreq", "done");
+
+    return;
+  }
+
+  if ("testreq" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+
+    response.write(getState("appreq"));
+
+    return;
+  }
+}
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -1,8 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
+  app.sjs
+  data/app/index.html
+  data/app/manifest.webapp
 
 [test_hosted.xul]
 skip-if = os == "mac"
 [test_packaged.xul]
 skip-if = os == "mac"
+[test_hosted_launch.xul]
+skip-if = os == "mac"
+[test_packaged_launch.xul]
+skip-if = os == "mac"
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test app</title>
+</head>
+<body>
+Test app:
+<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/manifest.webapp
@@ -0,0 +1,5 @@
+{
+  "name": "Test app",
+  "description": "A test application",
+  "launch_path": "/index.html"
+}
--- a/toolkit/webapps/tests/head.js
+++ b/toolkit/webapps/tests/head.js
@@ -43,8 +43,15 @@ function wait(time) {
   let deferred = Promise.defer();
 
   setTimeout(function() {
     deferred.resolve();
   }, time);
 
   return deferred.promise;
 }
+
+// Helper to create a nsIFile from a set of path components
+function getFile() {
+  let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+  file.initWithPath(OS.Path.join.apply(OS.Path, arguments));
+  return file;
+}
--- a/toolkit/webapps/tests/test_hosted.xul
+++ b/toolkit/webapps/tests/test_hosted.xul
@@ -244,16 +244,17 @@ Task.spawn(function() {
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(installedFiles)), "Files correctly written");
+  is(WebappOSUtils.getInstallPath(app), installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(installPath);
   let installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Reinstall application
copy from toolkit/webapps/tests/test_hosted.xul
copy to toolkit/webapps/tests/test_hosted_launch.xul
--- a/toolkit/webapps/tests/test_hosted.xul
+++ b/toolkit/webapps/tests/test_hosted_launch.xul
@@ -1,148 +1,106 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=898647
+https://bugzilla.mozilla.org/show_bug.cgi?id=981249
 -->
-<window title="Mozilla Bug 898647"
+<window title="Mozilla Bug 981249"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=898647"
-     target="_blank">Mozilla Bug 898647</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=981249"
+     target="_blank">Mozilla Bug 981249</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 898647 **/
+/** Test for Bug 981249 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 let manifest = {
-  name: "Sample hosted app",
+  name: "test_desktop_hosted_launch",
+  launch_path: "/chrome/toolkit/webapps/tests/app.sjs?appreq",
 };
 
 let app = {
-  name: "Sample hosted app",
-  manifestURL: "http://example.com/sample.manifest",
+  name: "test_desktop_hosted_launch",
+  manifestURL: "http://127.0.0.1:8888/sample.manifest",
   manifest: manifest,
-  origin: "http://example.com/",
+  origin: "http://127.0.0.1:8888/",
   categories: [],
-  installOrigin: "http://example.com/",
+  installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
 let profileDir;
-let profilesIni;
 let installPath;
-
-let installedFiles;
-let tempUpdatedFiles;
-let updatedFiles;
+let exePath;
+let appProcess = Cc["@mozilla.org/process/util;1"].
+                 createInstance(Ci.nsIProcess);
 
 let cleanup;
 
 if (navigator.platform.startsWith("Linux")) {
   installPath = OS.Path.join(OS.Constants.Path.homeDir, "." + WebappOSUtils.getUniqueName(app));
+  exePath = OS.Path.join(installPath, "webapprt-stub");
 
   let xdg_data_home = Cc["@mozilla.org/process/environment;1"].
                       getService(Ci.nsIEnvironment).
                       get("XDG_DATA_HOME");
   if (!xdg_data_home) {
     xdg_data_home = OS.Path.join(OS.Constants.Path.homeDir, ".local", "share");
   }
 
   let desktopINI = OS.Path.join(xdg_data_home, "applications",
                                 "owa-" + WebappOSUtils.getUniqueName(app) + ".desktop");
 
-  installedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapprt-stub"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    desktopINI,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "icon.png"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    desktopINI,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopINI, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Win")) {
   installPath = OS.Path.join(OS.Constants.Path.winAppDataDir, WebappOSUtils.getUniqueName(app));
-
-  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "Sample hosted app.lnk");
-  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "Sample hosted app.lnk");
+  exePath = OS.Path.join(installPath, "test_desktop_hosted_launch.exe");
 
-  installedFiles = [
-    OS.Path.join(installPath, "Sample hosted app.exe"),
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "uninstall", "webapp-uninstaller.exe"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "update", "uninstall", "webapp-uninstaller.exe"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
+  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "test_desktop_hosted_launch.lnk");
+  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "test_desktop_hosted_launch.lnk");
 
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       let uninstallKey;
       try {
         uninstallKey = Cc["@mozilla.org/windows-registry-key;1"].
                        createInstance(Ci.nsIWindowsRegKey);
         uninstallKey.open(uninstallKey.ROOT_KEY_CURRENT_USER,
                           "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
                           uninstallKey.ACCESS_WRITE);
         if (uninstallKey.hasChild(WebappOSUtils.getUniqueName(app))) {
@@ -161,44 +119,28 @@ if (navigator.platform.startsWith("Linux
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
       yield OS.File.remove(startMenuShortcut, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Mac")) {
-  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "Sample hosted app.app");
+  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "test_desktop_hosted_launch.app");
+  exePath = OS.Path.join(installPath, "Contents", "MacOS", "webapprt");
+
   let appProfileDir = OS.Path.join(OS.Constants.Path.macUserLibDir, "Application Support",
                                    WebappOSUtils.getUniqueName(app));
 
-  installedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapprt"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "Contents", "Info.plist"),
-    OS.Path.join(installPath, "update", "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "update", "webapp.json")
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-
-  profilesIni = OS.Path.join(appProfileDir, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.removeDir(appProfileDir, { ignoreAbsent: true });
     });
@@ -217,93 +159,66 @@ SimpleTest.registerCleanupFunction(funct
     Services.prefs.clearUserPref("browser.mozApps.installer.dry_run");
   } else {
     Services.prefs.setBoolPref("browser.mozApps.installer.dry_run", old_dry_run);
   }
 
   cleanup();
 });
 
-Task.spawn(function() {
+function wasAppSJSAccessed() {
+  let deferred = Promise.defer();
+
+  var xhr = new XMLHttpRequest();
+
+  xhr.addEventListener("load", function() {
+    let ret = (xhr.responseText == "done") ? true : false;
+    deferred.resolve(ret);
+  });
+
+  xhr.addEventListener("error", aError => deferred.reject(aError));
+  xhr.addEventListener("abort", aError => deferred.reject(aError));
+
+  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs?testreq', true);
+  xhr.send();
+
+  return deferred.promise;
+}
+
+Task.spawn(function*() {
   // Get to a clean state before the test
   yield cleanup();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
-  info("Test update for an uninstalled application");
-  try {
-    yield nativeApp.prepareUpdate(manifest);
-    ok(false, "Didn't thrown");
-  } catch (ex) {
-    is(ex, "The application isn't installed", "Exception thrown");
-  }
-
   profileDir = nativeApp.createProfile();
   ok(profileDir && profileDir.exists(), "Profile directory created");
-  ok((yield OS.File.exists(profilesIni)), "profiles.ini file created");
 
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Files correctly written");
 
-  let stat = yield OS.File.stat(installPath);
-  let installTime = stat.lastModificationDate;
+  let exeFile = getFile(exePath);
 
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
+  ok(exeFile.isExecutable(), "webapprt executable is executable");
 
-  // Reinstall application
-  info("Test reinstallation");
-  yield nativeApp.install(manifest);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+  let appClosed = false;
 
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
+  appProcess.init(exeFile)
+  appProcess.runAsync([], 0, () => appClosed = true);
+
+  while (!(yield wasAppSJSAccessed()) && !appClosed) {
     yield wait(1000);
   }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
-
-  stat = yield OS.File.stat(installPath);
-  installTime = stat.lastModificationDate;
-
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
-
-  // Update application
-  info("Test update");
-  yield nativeApp.prepareUpdate(manifest);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
-
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
+  ok(!appClosed, "App was launched and is still running");
 
   SimpleTest.finish();
 }).then(null, function(e) {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
 
 ]]>
--- a/toolkit/webapps/tests/test_packaged.xul
+++ b/toolkit/webapps/tests/test_packaged.xul
@@ -262,16 +262,17 @@ Task.spawn(function() {
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(installedFiles)), "Files correctly written");
+  is(WebappOSUtils.getInstallPath(app), installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(installPath);
   let installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Reinstall application
copy from toolkit/webapps/tests/test_packaged.xul
copy to toolkit/webapps/tests/test_packaged_launch.xul
--- a/toolkit/webapps/tests/test_packaged.xul
+++ b/toolkit/webapps/tests/test_packaged_launch.xul
@@ -1,160 +1,116 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=898647
+https://bugzilla.mozilla.org/show_bug.cgi?id=981249
 -->
-<window title="Mozilla Bug 898647"
+<window title="Mozilla Bug 981249"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome-harness.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=898647"
-     target="_blank">Mozilla Bug 898647</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=981249"
+     target="_blank">Mozilla Bug 981249</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 898647 **/
+/** Test for Bug 981249 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
-let zipPath = OS.Path.join(OS.Constants.Path.profileDir, "sample.zip");
+const PR_RDWR        = 0x04;
+const PR_CREATE_FILE = 0x08;
+const PR_TRUNCATE    = 0x20;
 
 let manifest = {
-  name: "Sample packaged app",
+  name: "test_desktop_packaged_launch",
   version: "0.1a",
   size: 777,
-  package_path: "/sample.zip",
+  package_path: "/data/app.zip",
+  launch_path: "/index.html",
 };
 
 let app = {
-  name: "Sample packaged app",
-  manifestURL: "http://example.com/sample.manifest",
+  name: "test_desktop_packaged_launch",
+  manifestURL: "http://127.0.0.1:8888/sample.manifest",
   manifest: manifest,
   updateManifest: manifest,
-  origin: "http://example.com/",
+  origin: "app://test_desktop_packaged_launch/",
   categories: [],
-  installOrigin: "http://example.com/",
+  installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
 let profileDir;
-let profilesIni;
 let installPath;
-
-let installedFiles;
-let tempUpdatedFiles;
-let updatedFiles;
+let exePath;
+let appProcess = Cc["@mozilla.org/process/util;1"].
+                 createInstance(Ci.nsIProcess);
 
 let cleanup;
 
 if (navigator.platform.startsWith("Linux")) {
   installPath = OS.Path.join(OS.Constants.Path.homeDir, "." + WebappOSUtils.getUniqueName(app));
+  exePath = OS.Path.join(installPath, "webapprt-stub");
 
   let xdg_data_home = Cc["@mozilla.org/process/environment;1"].
                       getService(Ci.nsIEnvironment).
                       get("XDG_DATA_HOME");
   if (!xdg_data_home) {
     xdg_data_home = OS.Path.join(OS.Constants.Path.homeDir, ".local", "share");
   }
 
   let desktopINI = OS.Path.join(xdg_data_home, "applications",
                                 "owa-" + WebappOSUtils.getUniqueName(app) + ".desktop");
 
-  installedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapprt-stub"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    desktopINI,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "icon.png"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "application.zip"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    desktopINI,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopINI, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Win")) {
   installPath = OS.Path.join(OS.Constants.Path.winAppDataDir, WebappOSUtils.getUniqueName(app));
-
-  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "Sample packaged app.lnk");
-  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "Sample packaged app.lnk");
+  exePath = OS.Path.join(installPath, "test_desktop_packaged_launch.exe");
 
-  installedFiles = [
-    OS.Path.join(installPath, "Sample packaged app.exe"),
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "uninstall", "webapp-uninstaller.exe"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "application.zip"),
-    OS.Path.join(installPath, "update", "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "update", "uninstall", "webapp-uninstaller.exe"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
+  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "test_desktop_packaged_launch.lnk");
+  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "test_desktop_packaged_launch.lnk");
 
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       let uninstallKey;
       try {
         uninstallKey = Cc["@mozilla.org/windows-registry-key;1"].
                        createInstance(Ci.nsIWindowsRegKey);
         uninstallKey.open(uninstallKey.ROOT_KEY_CURRENT_USER,
                           "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
                           uninstallKey.ACCESS_WRITE);
         if (uninstallKey.hasChild(WebappOSUtils.getUniqueName(app))) {
@@ -173,47 +129,28 @@ if (navigator.platform.startsWith("Linux
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
       yield OS.File.remove(startMenuShortcut, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Mac")) {
-  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "Sample packaged app.app");
+  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "test_desktop_packaged_launch.app");
+  exePath = OS.Path.join(installPath, "Contents", "MacOS", "webapprt");
+
   let appProfileDir = OS.Path.join(OS.Constants.Path.macUserLibDir, "Application Support",
                                    WebappOSUtils.getUniqueName(app));
 
-  installedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapprt"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "Contents", "Resources", "application.zip"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "Contents", "Info.plist"),
-    OS.Path.join(installPath, "update", "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "application.zip"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "Contents", "Resources", "application.zip"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-
-  profilesIni = OS.Path.join(appProfileDir, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.removeDir(appProfileDir, { ignoreAbsent: true });
     });
@@ -232,104 +169,86 @@ SimpleTest.registerCleanupFunction(funct
     Services.prefs.clearUserPref("browser.mozApps.installer.dry_run");
   } else {
     Services.prefs.setBoolPref("browser.mozApps.installer.dry_run", old_dry_run);
   }
 
   cleanup();
 });
 
+function buildAppPackage() {
+  let zipFile = getFile(OS.Constants.Path.profileDir, "sample.zip");
+
+  let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
+  zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+  zipWriter.addEntryFile("index.html",
+                         Ci.nsIZipWriter.COMPRESSION_NONE,
+                         getFile(getTestFilePath("data/app/index.html")),
+                         false);
+  zipWriter.addEntryFile("manifest.webapp",
+                         Ci.nsIZipWriter.COMPRESSION_NONE,
+                         getFile(getTestFilePath("data/app/manifest.webapp")),
+                         false);
+  zipWriter.close();
+
+  return zipFile.path;
+}
+
+function wasAppSJSAccessed() {
+  let deferred = Promise.defer();
+
+  var xhr = new XMLHttpRequest();
+
+  xhr.addEventListener("load", function() {
+    let ret = (xhr.responseText == "done") ? true : false;
+    deferred.resolve(ret);
+  });
+
+  xhr.addEventListener("error", aError => deferred.reject(aError));
+  xhr.addEventListener("abort", aError => deferred.reject(aError));
+
+  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs?testreq', true);
+  xhr.send();
+
+  return deferred.promise;
+}
+
 Task.spawn(function() {
   // Get to a clean state before the test
   yield cleanup();
 
-  let zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
+  let zipPath = buildAppPackage();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
-  info("Test update for an application that isn't installed");
-  try {
-    yield nativeApp.prepareUpdate(manifest, zipPath);
-    ok(false, "Didn't thrown");
-  } catch (ex) {
-    is(ex, "The application isn't installed", "Exception thrown");
-  }
-
   profileDir = nativeApp.createProfile();
   ok(profileDir && profileDir.exists(), "Profile directory created");
-  ok((yield OS.File.exists(profilesIni)), "profiles.ini file created");
 
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Files correctly written");
-
-  let stat = yield OS.File.stat(installPath);
-  let installTime = stat.lastModificationDate;
 
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
+  let exeFile = getFile(exePath);
 
-  // Reinstall application
-  info("Test reinstallation");
+  ok(exeFile.isExecutable(), "webapprt executable is executable");
 
-  zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
+  let appClosed = false;
 
-  yield nativeApp.install(manifest, zipPath);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+  appProcess.init(exeFile)
+  appProcess.runAsync([], 0, () => appClosed = true);
 
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
+  while (!(yield wasAppSJSAccessed()) && !appClosed) {
     yield wait(1000);
   }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
-
-  stat = yield OS.File.stat(installPath);
-  installTime = stat.lastModificationDate;
-
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
-
-  // Update application
-  info("Test update");
-
-  zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
-
-  yield nativeApp.prepareUpdate(manifest, zipPath);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
-
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
+  ok(!appClosed, "App was launched and is still running");
 
   SimpleTest.finish();
 }).then(null, function(e) {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
 
 ]]>
--- a/webapprt/CommandLineHandler.js
+++ b/webapprt/CommandLineHandler.js
@@ -38,17 +38,20 @@ CommandLineHandler.prototype = {
       // We're opening the window here in order to show it as soon as possible.
       let window = Services.ww.openWindow(null,
                                           "chrome://webapprt/content/webapp.xul",
                                           "_blank",
                                           "chrome,dialog=no,resizable,scrollbars,centerscreen",
                                           null);
       // Load the module to start up the app
       Cu.import("resource://webapprt/modules/Startup.jsm");
-      startup(window);
+      startup(window).then(null, function (aError) {
+        dump("Error: " + aError + "\n");
+        Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
+      });
     }
   },
 
   /**
    * Handle debug command line option.
    *
    * @param cmdLine A nsICommandLine object.
    *
--- a/webapprt/Startup.jsm
+++ b/webapprt/Startup.jsm
@@ -141,10 +141,10 @@ this.startup = function(window) {
       appBrowser.addEventListener("load", function onLoad() {
         appBrowser.removeEventListener("load", onLoad, true);
         appBrowser.contentDocument.
           documentElement.mozRequestFullScreen();
       }, true);
     }
 
     WebappRT.startUpdateService();
-  }).then(null, Cu.reportError.bind(Cu));
+  });
 }
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -25,18 +25,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   'resource://gre/modules/NativeApp.jsm');
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                   "@mozilla.org/AppsService;1",
                                   "nsIAppsService");
 
 this.WebappRT = {
   get launchURI() {
-    let manifest = this.localeManifest;
-    return manifest.fullLaunchPath();
+    return this.localeManifest.fullLaunchPath();
   },
 
   get localeManifest() {
     return new ManifestHelper(this.config.app.manifest,
                               this.config.app.origin);
   },
 
   get appID() {