Bug 919496 - [app manager] Once an app has been installed, it doesn't show up in the 'installed app' tab. r=ochameau
authorPaul Rouget <paul@mozilla.com>
Sat, 05 Oct 2013 10:43:06 -0400
changeset 149964 daf000728e1ee467c9eadfb52a83f6981f521ead
parent 149963 9e6755e523d67d09effb369804c15402c224c4d1
child 149974 87fd173f56f185c01a652d984ada2accc296f8a4
push id2944
push userryanvm@gmail.com
push dateSat, 05 Oct 2013 14:43:19 +0000
treeherderfx-team@daf000728e1e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs919496
milestone27.0a1
Bug 919496 - [app manager] Once an app has been installed, it doesn't show up in the 'installed app' tab. r=ochameau
browser/devtools/app-manager/webapps-store.js
toolkit/devtools/apps/tests/unit/head_apps.js
toolkit/devtools/apps/tests/unit/test_webappsActor.js
toolkit/devtools/server/actors/webapps.js
--- a/browser/devtools/app-manager/webapps-store.js
+++ b/browser/devtools/app-manager/webapps-store.js
@@ -3,19 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const ObservableObject = require("devtools/shared/observable-object");
 const promise = require("sdk/core/promise");
 const {Connection} = require("devtools/client/connection-manager");
 
 const {Cu} = require("chrome");
 const dbgClient = Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
-dbgClient.UnsolicitedNotifications.appOpen = "appOpen";
-dbgClient.UnsolicitedNotifications.appClose = "appClose"
-
 const _knownWebappsStores = new WeakMap();
 
 let WebappsStore;
 
 module.exports = WebappsStore = function(connection) {
   // If we already know about this connection,
   // let's re-use the existing store.
   if (_knownWebappsStores.has(connection)) {
@@ -98,16 +95,24 @@ WebappsStore.prototype = {
       client.addListener("appOpen", (type, { manifestURL }) => {
         this._onAppOpen(manifestURL);
       });
 
       client.addListener("appClose", (type, { manifestURL }) => {
         this._onAppClose(manifestURL);
       });
 
+      client.addListener("appInstall", (type, { manifestURL }) => {
+        this._onAppInstall(manifestURL);
+      });
+
+      client.addListener("appUninstall", (type, { manifestURL }) => {
+        this._onAppUninstall(manifestURL);
+      });
+
       return deferred.resolve();
     })
     return deferred.promise;
   },
 
   _getAllApps: function() {
     let deferred = promise.defer();
     let request = {
@@ -172,16 +177,20 @@ WebappsStore.prototype = {
     let idx = 0;
     (function getIcon() {
       if (idx == allApps.length) {
         return deferred.resolve();
       }
       let a = allApps[idx++];
       request.manifestURL = a.manifestURL;
       return client.request(request, (res) => {
+        if (res.error) {
+          Cu.reportError(res.message || res.error);
+        }
+
         if (res.url) {
           a.iconURL = res.url;
         }
         getIcon();
       });
     })();
 
     return deferred.promise;
@@ -199,9 +208,62 @@ WebappsStore.prototype = {
   _onAppClose: function(manifest) {
     let a = this._getAppFromManifest(manifest);
     a.running = false;
     let running = this.object.running;
     this.object.running = running.filter((m) => {
       return m != manifest;
     });
   },
+
+  _onAppInstall: function(manifest) {
+    let client = this._connection.client;
+    let request = {
+      to: this._webAppsActor,
+      type: "getApp",
+      manifestURL: manifest
+    };
+
+    client.request(request, (res) => {
+      if (res.error) {
+        if (res.error == "forbidden") {
+          // We got a notification for an app we don't have access to.
+          // Ignore.
+          return;
+        }
+        Cu.reportError(res.message || res.error);
+        return;
+      }
+
+      let app = res.app;
+      app.running = false;
+
+      let notFound = true;
+      let proxifiedApp;
+      for (let i = 0; i < this.object.all.length; i++) {
+        let storedApp = this.object.all[i];
+        if (storedApp.manifestURL == app.manifestURL) {
+          this.object.all[i] = app;
+          proxifiedApp = this.object.all[i];
+          notFound = false;
+          break;
+        }
+      }
+      if (notFound) {
+        this.object.all.push(app);
+        proxifiedApp = this.object.all[this.object.all.length - 1];
+      }
+
+      request.type = "getIconAsDataURL";
+      client.request(request, (res) => {
+        if (res.url) {
+          proxifiedApp.iconURL = res.url;
+        }
+      });
+    });
+  },
+
+  _onAppUninstall: function(manifest) {
+    this.object.all = this.object.all.filter((app) => {
+      return (app.manifestURL != manifest);
+    });
+  },
 }
--- a/toolkit/devtools/apps/tests/unit/head_apps.js
+++ b/toolkit/devtools/apps/tests/unit/head_apps.js
@@ -93,30 +93,28 @@ function do_get_webappsdir() {
   webappsDir.append("test_webapps");
   if (!webappsDir.exists())
     webappsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
 
   var coreAppsDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
   coreAppsDir.append("test_coreapps");
   if (!coreAppsDir.exists())
     coreAppsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
-  var tmpDir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
 
   // Register our own provider for the profile directory.
   // It will return our special docshell profile directory.
   var provider = {
     getFile: function(prop, persistent) {
       persistent.value = true;
       if (prop == "webappsDir") {
         return webappsDir.clone();
       }
       else if (prop == "coreAppsDir") {
         return coreAppsDir.clone();
       }
-      return tmpDir.clone();
       throw Cr.NS_ERROR_FAILURE;
     },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
           iid.equals(Ci.nsISupports)) {
         return this;
       }
       throw Cr.NS_ERROR_NO_INTERFACE;
--- a/toolkit/devtools/apps/tests/unit/test_webappsActor.js
+++ b/toolkit/devtools/apps/tests/unit/test_webappsActor.js
@@ -52,16 +52,33 @@ add_test(function testGetAll() {
         run_next_test();
         return;
       }
     }
     do_throw("Unable to find the test app by its id");
   });
 });
 
+add_test(function testGetApp() {
+  let manifestURL = APP_ORIGIN + "/manifest.webapp";
+  let request = {type: "getApp", manifestURL: manifestURL};
+  webappActorRequest(request, function (aResponse) {
+    do_check_true("app" in aResponse);
+    let app = aResponse.app;
+    do_check_eq(app.id, gAppId);
+    do_check_eq(app.name, "Test app");
+    do_check_eq(app.manifest.description, "Testing webapps actor");
+    do_check_eq(app.manifest.launch_path, "/index.html");
+    do_check_eq(app.origin, APP_ORIGIN);
+    do_check_eq(app.installOrigin, app.origin);
+    do_check_eq(app.manifestURL, app.origin + "/manifest.webapp");
+    run_next_test();
+  });
+});
+
 add_test(function testLaunchApp() {
   let manifestURL = APP_ORIGIN + "/manifest.webapp";
   let startPoint = "/index.html";
   let request = {
     type: "launch",
     manifestURL: manifestURL,
     startPoint: startPoint
   };
--- a/toolkit/devtools/server/actors/webapps.js
+++ b/toolkit/devtools/server/actors/webapps.js
@@ -539,16 +539,42 @@ WebappsActor.prototype = {
     let reg = DOMApplicationRegistry;
     reg.getAll(apps => {
       deferred.resolve({ apps: this._filterAllowedApps(apps) });
     });
 
     return deferred.promise;
   },
 
+  getApp: function wa_actorGetApp(aRequest) {
+    debug("getApp");
+
+    let manifestURL = aRequest.manifestURL;
+    if (!manifestURL) {
+      return { error: "missingParameter",
+               message: "missing parameter manifestURL" };
+    }
+
+    let reg = DOMApplicationRegistry;
+    let app = reg.getAppByManifestURL(manifestURL);
+    if (!app) {
+      return { error: "appNotFound" };
+    }
+
+    if (this._isAppAllowedForManifest(app.manifestURL)) {
+      let deferred = promise.defer();
+      reg.getManifestFor(manifestURL, function (manifest) {
+        app.manifest = manifest;
+        deferred.resolve({app: app});
+      });
+      return deferred.promise;
+    }
+    return { error: "forbidden" };
+  },
+
   _areCertifiedAppsAllowed: function wa__areCertifiedAppsAllowed() {
     let pref = "devtools.debugger.forbid-certified-apps";
     return !Services.prefs.getBoolPref(pref);
   },
 
   _isAppAllowedForManifest: function wa__isAppAllowedForManifest(aManifest) {
     if (this._areCertifiedAppsAllowed()) {
       return true;
@@ -950,16 +976,17 @@ WebappsActor.prototype.requestTypes = {
 };
 
 // Until we implement unix domain socket, we only enable app install
 // only on production devices
 if (Services.prefs.getBoolPref("devtools.debugger.enable-content-actors")) {
   let requestTypes = WebappsActor.prototype.requestTypes;
   requestTypes.uploadPackage = WebappsActor.prototype.uploadPackage;
   requestTypes.getAll = WebappsActor.prototype.getAll;
+  requestTypes.getApp = WebappsActor.prototype.getApp;
   requestTypes.launch = WebappsActor.prototype.launch;
   requestTypes.close  = WebappsActor.prototype.close;
   requestTypes.uninstall = WebappsActor.prototype.uninstall;
   requestTypes.listRunningApps = WebappsActor.prototype.listRunningApps;
   requestTypes.getAppActor = WebappsActor.prototype.getAppActor;
   requestTypes.watchApps = WebappsActor.prototype.watchApps;
   requestTypes.unwatchApps = WebappsActor.prototype.unwatchApps;
   requestTypes.getIconAsDataURL = WebappsActor.prototype.getIconAsDataURL;