Bug 911785 - Allow installing local apps from the app manager UI r=paul
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 10 Sep 2013 13:26:00 +0200
changeset 159571 f4f0334971aea6659b186d7d2982f239e23b5aae
parent 159570 22d47b7958707551a10011d262784c839deed0da
child 159572 24270cebb090d0b992bc36197e0d7a9f5caea9b0
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaul
bugs911785
milestone26.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 911785 - Allow installing local apps from the app manager UI r=paul
browser/devtools/app-manager/app-projects.js
browser/devtools/app-manager/content/projects.js
browser/devtools/app-manager/content/projects.xhtml
browser/locales/en-US/chrome/browser/devtools/app-manager.properties
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -1,13 +1,14 @@
 const {Cc,Ci,Cu} = require("chrome");
 const ObservableObject = require("devtools/shared/observable-object");
 const promise = require("sdk/core/promise");
 
 const {EventEmitter} = Cu.import("resource:///modules/devtools/shared/event-emitter.js");
+const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
 
 /**
  * IndexedDB wrapper that just save project objects
  *
  * The only constraint is that project objects have to have
  * a unique `location` object.
  */
 
@@ -91,17 +92,23 @@ IDB.open().then(function (projects) {
   store.object.projects = projects;
   AppProjects.emit("ready", store.object.projects);
 });
 
 const AppProjects = {
   addPackaged: function(folder) {
     let project = {
       type: "packaged",
-      location: folder.path
+      location: folder.path,
+      // We need a unique id, that is the app origin,
+      // in order to identify the app when being installed on the device.
+      // The packaged app local path is a valid id, but only on the client.
+      // This origin will be used to generate the true id of an app:
+      // its manifest URL.
+      packagedAppOrigin: generateUUID().toString().slice(1, -1)
     };
     return IDB.add(project).then(function () {
       store.object.projects.push(project);
       // return the added objects (proxified)
       return store.object.projects[store.object.projects.length - 1];
     });
   },
 
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.js
@@ -9,16 +9,18 @@ const Cr = Components.results;
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {require} = devtools;
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const {AppValidator} = require("devtools/app-manager/app-validator");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
+const {installHosted, installPackaged} = require("devtools/app-actor-front");
+
 const promise = require("sdk/core/promise");
 
 window.addEventListener("message", function(event) {
   try {
     let json = JSON.parse(event.data);
     if (json.name == "connection") {
       let cid = parseInt(json.cid);
       for (let c of ConnectionManager.connections) {
@@ -158,16 +160,49 @@ let UI = {
   _getProjectManifestURL: function (project) {
     if (project.type == "packaged") {
       return "app://" + project.packagedAppOrigin + "/manifest.webapp";
     } else if (project.type == "hosted") {
       return project.location;
     }
   },
 
+  install: function(button, location) {
+    button.dataset.originalTextContent = button.textContent;
+    button.textContent = Utils.l10n("project.installing");
+    button.disabled = true;
+    let project = AppProjects.get(location);
+    let install;
+    if (project.type == "packaged") {
+      install = installPackaged(this.connection.client, this.listTabsResponse.webappsActor, project.location, project.packagedAppOrigin);
+    } else {
+      let manifestURLObject = Services.io.newURI(project.location, null, null);
+      let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
+      let appId = origin.host;
+      let metadata = {
+        origin: origin.spec,
+        manifestURL: project.location
+      };
+      install = installHosted(this.connection.client, this.listTabsResponse.webappsActor, appId, metadata, project.manifest);
+    }
+    install.then(function () {
+      button.disabled = false;
+      button.textContent = Utils.l10n("project.installed");
+      setTimeout(function() {
+        button.textContent = button.dataset.originalTextContent;
+      }, 1500);
+    },
+    function (res) {
+      button.disabled = false;
+      let message = res.error + ": " + res.message;
+      alert(message);
+      this.connection.log(message);
+    });
+  },
+
   start: function(location) {
     let project = AppProjects.get(location);
     let request = {
       to: this.listTabsResponse.webappsActor,
       type: "launch",
       manifestURL: this._getProjectManifestURL(project)
     };
     this.connection.client.request(request, (res) => {
--- a/browser/devtools/app-manager/content/projects.xhtml
+++ b/browser/devtools/app-manager/content/projects.xhtml
@@ -66,24 +66,20 @@
           </div>
           <span template='{"type":"textContent","path":"manifest.developer.name"}'></span>
           <p class="project-location" template='{"type":"textContent","path":"location"}' onclick="UI.reveal(this.textContent)"></p>
           <p class="project-description" template='{"type":"textContent","path":"manifest.description"}'></p>
         </div>
       </div>
       <div class="project-buttons">
         <button class="project-button-refresh" onclick="UI.update(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.reloadFiles;</button>
-        <!-- Not available until bug 911785 is fixed
         <button class="device-action project-button-install" onclick="UI.install(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.installApp;</button>
-        -->
         <button class="device-action project-button-start" onclick="UI.start(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.startApp;</button>
         <button class="device-action project-button-stop" onclick="UI.stop(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.stopApp;</button>
-        <!-- Not available until bug 911785 is fixed
         <button class="device-action project-button-debug" onclick="UI.openToolbox(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.debugApp;</button>
-        -->
       </div>
       <div class="project-errors" template='{"type":"textContent","path":"errors"}'></div>
       <div class="project-warnings" template='{"type":"textContent","path":"warnings"}'></div>
     </div>
   </div>
   </template>
 
 
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
@@ -6,16 +6,18 @@
 # the device's height, %3$S is the device's pixel density.
 # Example: 800x480 (86 DPI).
 device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
 # LOCALIZATION NOTE (connection.connectedToDevice, connection.connectTo):
 # %1$S is the host name, %2$S is the port number.
 connection.connectedToDevice=Connected to %1$S
 connection.connectTo=Connect to %1$S:%2$S
 project.filePickerTitle=Select a webapp folder
+project.installing=Installing...
+project.installed=Installed!
 validator.nonExistingFolder=The project folder doesn't exists
 validator.expectProjectFolder=The project folder ends up being a file
 validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder
 validator.invalidManifestURL=Invalid manifest URL '%S'
 # LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
 # %1$S is the error message, %2$S is the URI of the manifest.
 validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S
 validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S