Bug 913711 - Added methods for finding manifest and updates project location if necessary. r=pablo
authorPablo Terradillos <tehsis@gmail.com>
Thu, 18 Sep 2014 08:14:00 +0200
changeset 206157 4a0a256c54e227485bef20727f770a6d5f0ca8d7
parent 206156 e55ff6fab89924017a1870cd76731a18b0469c50
child 206158 5ac8150b6745644dc5f19591fba05b3995748564
push id27514
push usercbook@mozilla.com
push dateFri, 19 Sep 2014 12:24:09 +0000
treeherdermozilla-central@3475e6a1665a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspablo
bugs913711
milestone35.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 913711 - Added methods for finding manifest and updates project location if necessary. r=pablo
browser/devtools/app-manager/app-projects.js
browser/devtools/app-manager/app-validator.js
browser/devtools/app-manager/test/test_app_validator.html
browser/devtools/app-manager/test/validator/valid/alsoValid/manifest.webapp
browser/devtools/webide/modules/app-manager.js
browser/devtools/webide/test/test_import.html
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -201,16 +201,24 @@ const AppProjects = {
       return store.object.projects[store.object.projects.length - 1];
     });
   },
 
   update: function (project) {
     return IDB.update(project);
   },
 
+  updateLocation: function(project, newLocation) {
+    return IDB.remove(project.location)
+              .then(() => {
+                project.location = newLocation;
+                return IDB.add(project);
+              });
+  },
+
   remove: function(location) {
     return IDB.remove(location).then(function () {
       let projects = store.object.projects;
       for (let i = 0; i < projects.length; i++) {
         if (projects[i].location == location) {
           projects.splice(i, 1);
           return;
         }
@@ -230,9 +238,8 @@ const AppProjects = {
   },
 
   store: store
 };
 
 EventEmitter.decorate(AppProjects);
 
 exports.AppProjects = AppProjects;
-
--- a/browser/devtools/app-manager/app-validator.js
+++ b/browser/devtools/app-manager/app-validator.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 let {Ci,Cu,CC} = require("chrome");
 const promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
 let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
 
 function AppValidator(project) {
   this.project = project;
   this.errors = [];
   this.warnings = [];
 }
@@ -46,50 +47,107 @@ AppValidator.prototype._getPackagedManif
 AppValidator.prototype._getPackagedManifestURL = function () {
   let manifestFile = this._getPackagedManifestFile();
   if (!manifestFile) {
     return null;
   }
   return Services.io.newFileURI(manifestFile).spec;
 };
 
+AppValidator.checkManifest = function(manifestURL) {
+  let deferred = promise.defer();
+  let error;
+
+  let req = new XMLHttpRequest();
+  req.overrideMimeType('text/plain');
+
+  try {
+    req.open("GET", manifestURL, true);
+  } catch(e) {
+    error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
+    deferred.reject(error);
+    return deferred.promise;
+  }
+  req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
+
+  req.onload = function () {
+    let manifest = null;
+    try {
+      manifest = JSON.parse(req.responseText);
+    } catch(e) {
+      error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
+      deferred.reject(error);
+    }
+
+    deferred.resolve({manifest, manifestURL});
+  };
+
+  req.onerror = function () {
+    error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
+    deferred.reject(error);
+ };
+
+  try {
+    req.send(null);
+  } catch(e) {
+    error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
+    deferred.reject(error);
+  }
+
+  return deferred.promise;
+};
+
+AppValidator.findManifestAtOrigin = function(manifestURL) {
+  let fixedManifest = Services.io.newURI(manifestURL, null, null).prePath + '/manifest.webapp';
+  return AppValidator.checkManifest(fixedManifest);
+};
+
+AppValidator.findManifestPath = function(manifestURL) {
+  let deferred = promise.defer();
+
+  if (manifestURL.endsWith('manifest.webapp')) {
+    deferred.reject();
+  } else {
+    let fixedManifest = manifestURL + '/manifest.webapp';
+    deferred.resolve(AppValidator.checkManifest(fixedManifest));
+  }
+
+  return deferred.promise;
+};
+
+AppValidator.checkAlternateManifest = function(manifestURL) {
+  return Task.spawn(function*() {
+    let result;
+    try {
+      result = yield AppValidator.findManifestPath(manifestURL);
+    } catch(e) {
+      result = yield AppValidator.findManifestAtOrigin(manifestURL);
+    }
+
+    return result;
+  });
+};
+
 AppValidator.prototype._fetchManifest = function (manifestURL) {
   let deferred = promise.defer();
   this.manifestURL = manifestURL;
 
-  let req = new XMLHttpRequest();
-  req.overrideMimeType('text/plain');
-  try {
-    req.open("GET", manifestURL, true);
-  } catch(e) {
-    this.error(strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1));
-    deferred.resolve(null);
-    return deferred.promise;
-  }
-  req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
-  req.onload = (function () {
-    let manifest = null;
-    try {
-      manifest = JSON.parse(req.responseText);
-    } catch(e) {
-      this.error(strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2));
-    }
-    deferred.resolve(manifest);
-  }).bind(this);
-  req.onerror = (function () {
-    this.error(strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2));
-    deferred.resolve(null);
-  }).bind(this);
-
-  try {
-    req.send(null);
-  } catch(e) {
-    this.error(strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2));
-    deferred.resolve();
-  }
+  AppValidator.checkManifest(manifestURL)
+              .then(({manifest, manifestURL}) => {
+                deferred.resolve(manifest);
+              }, error => {
+                AppValidator.checkAlternateManifest(manifestURL)
+                            .then(({manifest, manifestURL}) => {
+                              this.manifestURL = manifestURL;
+                              deferred.resolve(manifest);
+                            }, () => {
+                              this.error(error);
+                              deferred.resolve(null);
+                            });
+                });
 
   return deferred.promise;
 };
 
 AppValidator.prototype._getManifest = function () {
   let manifestURL;
   if (this.project.type == "packaged") {
     manifestURL = this._getPackagedManifestURL();
--- a/browser/devtools/app-manager/test/test_app_validator.html
+++ b/browser/devtools/app-manager/test/test_app_validator.html
@@ -35,22 +35,22 @@
 
       httpserver = new HttpServer();
       httpserver.start(-1);
       origin = "http://localhost:" + httpserver.identity.primaryPort + "/";
 
       next();
     }
 
-    function createHosted(path) {
+    function createHosted(path, manifestFile="/manifest.webapp") {
       let dirPath = getTestFilePath("validator/" + path);
       httpserver.registerDirectory("/", nsFile(dirPath));
       return new AppValidator({
         type: "hosted",
-        location: origin + "/manifest.webapp"
+        location: origin + manifestFile
       });
     }
 
     function createPackaged(path) {
       let dirPath = getTestFilePath("validator/" + path);
       return new AppValidator({
         type: "packaged",
         location: dirPath
@@ -149,16 +149,48 @@
           checkNoNameOrIcon(validator);
         });
       },
       function () {
         let validator = createPackaged("no-name-or-icon");
         validator.validate().then(() => {
           checkNoNameOrIcon(validator);
         });
+      },
+
+      // Test a regular URL instead of a direct link to the manifest
+      function () {
+        let validator = createHosted("valid", "/");
+        validator.validate().then(() => {
+          is(validator.warnings.length, 0, "manifest found got no warning");
+          is(validator.errors.length, 0, "manifest found got no error");
+
+          next();
+        });
+      },
+
+      // Test finding a manifest at origin's root
+      function () {
+        let validator = createHosted("valid", "/unexisting-dir");
+        validator.validate().then(() => {
+          is(validator.warnings.length, 0, "manifest found at origin root got no warning");
+          is(validator.errors.length, 0, "manifest found at origin root got no error");
+
+          next();
+        });
+      },
+
+      // Test priorization of manifest.webapp at provided location instead of a manifest located at origin's root
+      function() {
+        let validator = createHosted("valid", "/alsoValid");
+        validator.validate().then(() => {
+          is(validator.manifest.name, "valid at subfolder", "manifest at subfolder was used");
+
+          next();
+        });
       }
     ];
 
     function checkNoNameOrIcon(validator) {
       is(validator.errors.length, 1, "app with no name has an error");
       is(validator.errors[0],
          strings.GetStringFromName("validator.missNameManifestProperty"),
          "with expected message");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/validator/valid/alsoValid/manifest.webapp
@@ -0,0 +1,7 @@
+{
+  "name": "valid at subfolder",
+  "launch_path": "/home.html",
+  "icons": {
+    "128": "/icon.png"
+  }
+}
--- a/browser/devtools/webide/modules/app-manager.js
+++ b/browser/devtools/webide/modules/app-manager.js
@@ -614,17 +614,19 @@ exports.AppManager = AppManager = {
         project.errors = "";
         project.errorsCount = 0;
       }
 
       if (project.warningsCount && project.errorsCount) {
         project.validationStatus = "error warning";
       }
 
-      if (AppProjects.get(project.location)) {
+      if (project.type === "hosted" && project.location !== validation.manifestURL) {
+        yield AppProjects.updateLocation(project, validation.manifestURL);
+      } else if (AppProjects.get(project.location)) {
         yield AppProjects.update(project);
       }
 
       if (AppManager.selectedProject === project) {
         AppManager.update("project-validated");
       }
     });
   },
--- a/browser/devtools/webide/test/test_import.html
+++ b/browser/devtools/webide/test/test_import.html
@@ -40,24 +40,32 @@
 
             let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
             yield win.Cmds.importHostedApp(hostedAppManifest);
 
             project = win.AppManager.selectedProject;
             is(project.location, hostedAppManifest, "Location is valid");
             is(project.name, "hosted manifest name property", "name field has been updated");
 
+            yield nextTick();
+
+            hostedAppManifest = TEST_BASE + "/app";
+            yield win.Cmds.importHostedApp(hostedAppManifest);
+
+            project = win.AppManager.selectedProject;
+            ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
+
             info("opening panel");
             yield win.Cmds.showProjectPanel();
             info("panel open");
 
             let panelNode = win.document.querySelector("#project-panel");
             let items = panelNode.querySelectorAll(".panel-item");
             // 3 controls, + 2 projects
-            is(items.length, 5, "5 projects in panel");
+            is(items.length, 6, "6 projects in panel");
             is(items[3].getAttribute("label"), "A name (in app directory)", "Panel label is correct");
             is(items[4].getAttribute("label"), "hosted manifest name property", "Panel label is correct");
 
             yield closeWebIDE(win);
 
             yield removeAllProjects();
 
             SimpleTest.finish();
@@ -66,9 +74,8 @@
           SimpleTest.finish();
         });
       }
 
 
     </script>
   </body>
 </html>
-