Bug 928144 - Make Manifest Editor read-only for hosted apps. r=paul
authorJ. Ryan Stinnett <jryans@gmail.com>
Tue, 22 Oct 2013 19:42:13 -0500
changeset 166865 08f5ba64a98854444bb9aabd1b3952149130861b
parent 166864 8693c78697ae83fb6302aada797e96c537f18699
child 166866 c74facfb70ab24462bc0a55c2ff7017e03fa01fc
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaul
bugs928144
milestone27.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 928144 - Make Manifest Editor read-only for hosted apps. r=paul
browser/devtools/app-manager/content/manifest-editor.js
browser/devtools/app-manager/content/projects.js
browser/devtools/app-manager/content/projects.xhtml
browser/devtools/app-manager/test/browser.ini
browser/devtools/app-manager/test/browser_manifest_editor.js
browser/devtools/app-manager/test/head.js
browser/devtools/app-manager/test/manifest.webapp
browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
browser/themes/shared/devtools/app-manager/projects.css
--- a/browser/devtools/app-manager/content/manifest-editor.js
+++ b/browser/devtools/app-manager/content/manifest-editor.js
@@ -16,16 +16,18 @@ function ManifestEditor(project) {
   this._onEval = this._onEval.bind(this);
   this._onSwitch = this._onSwitch.bind(this);
   this._onDelete = this._onDelete.bind(this);
 }
 
 ManifestEditor.prototype = {
   get manifest() { return this.project.manifest; },
 
+  get editable() { return this.project.type == "packaged"; },
+
   show: function(containerElement) {
     let deferred = promise.defer();
     let iframe = document.createElement("iframe");
 
     iframe.addEventListener("load", function onIframeLoad() {
       iframe.removeEventListener("load", onIframeLoad, true);
       deferred.resolve(iframe.contentWindow);
     }, true);
@@ -38,19 +40,22 @@ ManifestEditor.prototype = {
   },
 
   _onContainerReady: function(varWindow) {
     let variablesContainer = varWindow.document.querySelector("#variables");
 
     let editor = this.editor = new VariablesView(variablesContainer);
 
     editor.onlyEnumVisible = true;
-    editor.eval = this._onEval;
-    editor.switch = this._onSwitch;
-    editor.delete = this._onDelete;
+
+    if (this.editable) {
+      editor.eval = this._onEval;
+      editor.switch = this._onSwitch;
+      editor.delete = this._onDelete;
+    }
 
     return this.update();
   },
 
   _onEval: function(evalString) {
     let manifest = this.manifest;
     eval("manifest" + evalString);
     this.update();
@@ -85,17 +90,17 @@ ManifestEditor.prototype = {
 
     // Wait until the animation from commitHierarchy has completed
     let deferred = promise.defer();
     setTimeout(deferred.resolve, this.editor.lazyEmptyDelay + 1);
     return deferred.promise;
   },
 
   save: function() {
-    if (this.project.type == "packaged") {
+    if (this.editable) {
       let validator = new AppValidator(this.project);
       let manifestFile = validator._getPackagedManifestFile();
       let path = manifestFile.path;
 
       let encoder = new TextEncoder();
       let data = encoder.encode(JSON.stringify(this.manifest, null, 2));
 
       return OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp" });
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.js
@@ -76,29 +76,31 @@ let UI = {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     fp.init(window, Utils.l10n("project.filePickerTitle"), Ci.nsIFilePicker.modeGetFolder);
     let res = fp.show();
     if (res != Ci.nsIFilePicker.returnCancel)
       return fp.file;
     return null;
   },
 
-  addPackaged: function() {
-    let folder = this._selectFolder();
+  addPackaged: function(folder) {
+    if (!folder) {
+      folder = this._selectFolder();
+    }
     if (!folder)
       return;
-    AppProjects.addPackaged(folder)
-               .then(function (project) {
-                 UI.validate(project);
-                 UI.selectProject(project.location);
-               });
+    return AppProjects.addPackaged(folder)
+                      .then(function (project) {
+                        UI.validate(project);
+                        UI.selectProject(project.location);
+                      });
   },
 
   addHosted: function() {
-    let form = document.querySelector("#new-hosted-project-wrapper")
+    let form = document.querySelector("#new-hosted-project-wrapper");
     if (!form.checkValidity())
       return;
 
     let urlInput = document.querySelector("#url-input");
     let manifestURL = urlInput.value;
     return AppProjects.addHosted(manifestURL)
                       .then(function (project) {
                         UI.validate(project);
--- a/browser/devtools/app-manager/content/projects.xhtml
+++ b/browser/devtools/app-manager/content/projects.xhtml
@@ -75,14 +75,15 @@
       </div>
       <div class="project-buttons">
         <button class="project-button-update" onclick="UI.update(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.updateAppTooltip;">&projects.updateApp;</button>
         <button class="device-action project-button-debug" onclick="UI.debug(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.debugAppTooltip;">&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 class="manifest-editor">
-      <h2>&projects.manifestEditor;</h2>
+    <div class="manifest-editor" template='{"type":"attribute","path":"type","name":"type"}'>
+      <h2 class="editable" title="&projects.manifestEditorTooltip;">&projects.manifestEditor;</h2>
+      <h2 class="viewable" title="&projects.manifestViewerTooltip;">&projects.manifestViewer;</h2>
     </div>
   </div>
   </template>
 </html>
--- a/browser/devtools/app-manager/test/browser.ini
+++ b/browser/devtools/app-manager/test/browser.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 support-files =
   head.js
   hosted_app.manifest
+  manifest.webapp
 
 [browser_manifest_editor.js]
--- a/browser/devtools/app-manager/test/browser_manifest_editor.js
+++ b/browser/devtools/app-manager/test/browser_manifest_editor.js
@@ -8,20 +8,20 @@ const MANIFEST_EDITOR_ENABLED = "devtool
 
 function test() {
   waitForExplicitFinish();
 
   Task.spawn(function() {
     Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, true);
     let tab = yield openAppManager();
     yield selectProjectsPanel();
-    yield addSampleHostedApp();
+    yield addSamplePackagedApp();
     yield showSampleProjectDetails();
     yield changeManifestValue("name", "the best app");
-    yield removeSampleHostedApp();
+    yield removeSamplePackagedApp();
     yield removeTab(tab);
     Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, false);
     finish();
   });
 }
 
 function changeManifestValue(key, value) {
   return Task.spawn(function() {
--- a/browser/devtools/app-manager/test/head.js
+++ b/browser/devtools/app-manager/test/head.js
@@ -1,27 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const {utils: Cu} = Components;
+const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
 const {Promise: promise} =
   Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
 const {devtools} =
   Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {require} = devtools;
 
 const {AppProjects} = require("devtools/app-manager/app-projects");
 
 const APP_MANAGER_URL = "about:app-manager";
 const TEST_BASE =
   "chrome://mochitests/content/browser/browser/devtools/app-manager/test/";
 const HOSTED_APP_MANIFEST = TEST_BASE + "hosted_app.manifest";
 
+const PACKAGED_APP_DIR_PATH = getTestFilePath(".");
+
 function addTab(url, targetWindow = window) {
   info("Adding tab: " + url);
 
   let deferred = promise.defer();
   let targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
   let tab = targetBrowser.selectedTab = targetBrowser.addTab(url);
@@ -67,16 +69,28 @@ function addSampleHostedApp() {
   return projectsWindow.UI.addHosted();
 }
 
 function removeSampleHostedApp() {
   info("Removing sample hosted app");
   return AppProjects.remove(HOSTED_APP_MANIFEST);
 }
 
+function addSamplePackagedApp() {
+  info("Adding sample packaged app");
+  let appDir = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+  appDir.initWithPath(PACKAGED_APP_DIR_PATH);
+  return getProjectsWindow().UI.addPackaged(appDir);
+}
+
+function removeSamplePackagedApp() {
+  info("Removing sample packaged app");
+  return AppProjects.remove(PACKAGED_APP_DIR_PATH);
+}
+
 function getProjectsWindow() {
   return content.document.querySelector(".projects-panel").contentWindow;
 }
 
 function getManifestWindow() {
   return getProjectsWindow().document.querySelector(".variables-view")
          .contentWindow;
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/manifest.webapp
@@ -0,0 +1,3 @@
+{
+  "name": "My packaged app"
+}
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
@@ -69,16 +69,19 @@
 <!ENTITY projects.removeAppFromList "Remove this app from the list of apps you are working on. This will not remove it from a device or a simulator.">
 <!ENTITY projects.updateApp "Update">
 <!ENTITY projects.updateAppTooltip "Execute validation checks and update the app to the connected device">
 <!ENTITY projects.debugApp "Debug">
 <!ENTITY projects.debugAppTooltip "Open Developer Tools connected to this app">
 <!ENTITY projects.hostedManifestPlaceHolder2 "http://example.com/app/manifest.webapp">
 <!ENTITY projects.noProjects "No projects. Add a new packaged app below (local directory) or a hosted app (link to a manifest file).">
 <!ENTITY projects.manifestEditor "Manifest Editor">
+<!ENTITY projects.manifestEditorTooltip "Edit your app's manifest in the panel below. The Update button will save your changes and update the app.">
+<!ENTITY projects.manifestViewer "Manifest Viewer">
+<!ENTITY projects.manifestViewerTooltip "Examine your app's manifest in the panel below.">
 
 <!ENTITY help.title "App Manager">
 <!ENTITY help.close "Close">
 <!ENTITY help.intro "This tool will help you build and install web apps on compatible devices (i.e. Firefox OS). The <strong>Apps</strong> tab will assist you in the validation and installation process of your app. The <strong>Device</strong> tab will give you information about the connected device. Use the bottom toolbar to connect to a device or start the simulator.">
 <!ENTITY help.usefullLinks "Useful links:">
 <!ENTITY help.appMgrDoc "Documentation: Using the App Manager">
 <!ENTITY help.configuringDevice "How to setup your Firefox OS device">
 <!ENTITY help.troubleShooting "Troubleshooting">
--- a/browser/themes/shared/devtools/app-manager/projects.css
+++ b/browser/themes/shared/devtools/app-manager/projects.css
@@ -453,16 +453,25 @@ strong {
   flex-direction: column;
   flex-grow: 1;
   background-color: #E1E1E1;
 }
 
 .manifest-editor > h2 {
   font-size: 18px;
   margin: 1em 30px;
+  display: none;
+}
+
+[type="packaged"] > .editable {
+  display: block;
+}
+
+[type="hosted"] > .viewable {
+  display: block;
 }
 
 .variables-view {
   flex-grow: 1;
   border: 0;
   border-top: 5px solid #C9C9C9;
 }