michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: let {Ci,Cu,CC} = require("chrome"); michael@0: const promise = require("devtools/toolkit/deprecated-sync-thenables"); michael@0: michael@0: const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm"); michael@0: const {Services} = Cu.import("resource://gre/modules/Services.jsm"); michael@0: let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1"); michael@0: let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties"); michael@0: michael@0: function AppValidator(project) { michael@0: this.project = project; michael@0: this.errors = []; michael@0: this.warnings = []; michael@0: } michael@0: michael@0: AppValidator.prototype.error = function (message) { michael@0: this.errors.push(message); michael@0: }; michael@0: michael@0: AppValidator.prototype.warning = function (message) { michael@0: this.warnings.push(message); michael@0: }; michael@0: michael@0: AppValidator.prototype._getPackagedManifestFile = function () { michael@0: let manifestFile = FileUtils.File(this.project.location); michael@0: if (!manifestFile.exists()) { michael@0: this.error(strings.GetStringFromName("validator.nonExistingFolder")); michael@0: return null; michael@0: } michael@0: if (!manifestFile.isDirectory()) { michael@0: this.error(strings.GetStringFromName("validator.expectProjectFolder")); michael@0: return null; michael@0: } michael@0: manifestFile.append("manifest.webapp"); michael@0: if (!manifestFile.exists() || !manifestFile.isFile()) { michael@0: this.error(strings.GetStringFromName("validator.wrongManifestFileName")); michael@0: return null; michael@0: } michael@0: return manifestFile; michael@0: }; michael@0: michael@0: AppValidator.prototype._getPackagedManifestURL = function () { michael@0: let manifestFile = this._getPackagedManifestFile(); michael@0: if (!manifestFile) { michael@0: return null; michael@0: } michael@0: return Services.io.newFileURI(manifestFile).spec; michael@0: }; michael@0: michael@0: AppValidator.prototype._fetchManifest = function (manifestURL) { michael@0: let deferred = promise.defer(); michael@0: this.manifestURL = manifestURL; michael@0: michael@0: let req = new XMLHttpRequest(); michael@0: try { michael@0: req.open("GET", manifestURL, true); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1)); michael@0: deferred.resolve(null); michael@0: return deferred.promise; michael@0: } michael@0: req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; michael@0: req.onload = (function () { michael@0: let manifest = null; michael@0: try { michael@0: manifest = JSON.parse(req.responseText); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2)); michael@0: } michael@0: deferred.resolve(manifest); michael@0: }).bind(this); michael@0: req.onerror = (function () { michael@0: this.error(strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2)); michael@0: deferred.resolve(null); michael@0: }).bind(this); michael@0: michael@0: try { michael@0: req.send(null); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2)); michael@0: deferred.resolve(); michael@0: } michael@0: michael@0: return deferred.promise; michael@0: }; michael@0: michael@0: AppValidator.prototype._getManifest = function () { michael@0: let manifestURL; michael@0: if (this.project.type == "packaged") { michael@0: manifestURL = this._getPackagedManifestURL(); michael@0: if (!manifestURL) michael@0: return promise.resolve(null); michael@0: } else if (this.project.type == "hosted") { michael@0: manifestURL = this.project.location; michael@0: try { michael@0: Services.io.newURI(manifestURL, null, null); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message])); michael@0: return promise.resolve(null); michael@0: } michael@0: } else { michael@0: this.error(strings.formatStringFromName("validator.invalidProjectType", [this.project.type], 1)); michael@0: return promise.resolve(null); michael@0: } michael@0: return this._fetchManifest(manifestURL); michael@0: }; michael@0: michael@0: AppValidator.prototype.validateManifest = function (manifest) { michael@0: if (!manifest.name) { michael@0: this.error(strings.GetStringFromName("validator.missNameManifestProperty")); michael@0: } michael@0: michael@0: if (!manifest.icons || Object.keys(manifest.icons).length === 0) { michael@0: this.warning(strings.GetStringFromName("validator.missIconsManifestProperty")); michael@0: } else if (!manifest.icons["128"]) { michael@0: this.warning(strings.GetStringFromName("validator.missIconMarketplace")); michael@0: } michael@0: }; michael@0: michael@0: AppValidator.prototype._getOriginURL = function () { michael@0: if (this.project.type == "packaged") { michael@0: let manifestURL = Services.io.newURI(this.manifestURL, null, null); michael@0: return Services.io.newURI(".", null, manifestURL).spec; michael@0: } else if (this.project.type == "hosted") { michael@0: return Services.io.newURI(this.project.location, null, null).prePath; michael@0: } michael@0: }; michael@0: michael@0: AppValidator.prototype.validateLaunchPath = function (manifest) { michael@0: let deferred = promise.defer(); michael@0: // The launch_path field has to start with a `/` michael@0: if (manifest.launch_path && manifest.launch_path[0] !== "/") { michael@0: this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1)); michael@0: deferred.resolve(); michael@0: return deferred.promise; michael@0: } michael@0: let origin = this._getOriginURL(); michael@0: let path; michael@0: if (this.project.type == "packaged") { michael@0: path = "." + ( manifest.launch_path || "/index.html" ); michael@0: } else if (this.project.type == "hosted") { michael@0: path = manifest.launch_path || "/"; michael@0: } michael@0: let indexURL; michael@0: try { michael@0: indexURL = Services.io.newURI(path, null, Services.io.newURI(origin, null, null)).spec; michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1)); michael@0: deferred.resolve(); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: let req = new XMLHttpRequest(); michael@0: try { michael@0: req.open("HEAD", indexURL, true); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); michael@0: deferred.resolve(); michael@0: return deferred.promise; michael@0: } michael@0: req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; michael@0: req.onload = () => { michael@0: if (req.status >= 400) michael@0: this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2)); michael@0: deferred.resolve(); michael@0: }; michael@0: req.onerror = () => { michael@0: this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); michael@0: deferred.resolve(); michael@0: }; michael@0: michael@0: try { michael@0: req.send(null); michael@0: } catch(e) { michael@0: this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); michael@0: deferred.resolve(); michael@0: } michael@0: michael@0: return deferred.promise; michael@0: }; michael@0: michael@0: AppValidator.prototype.validateType = function (manifest) { michael@0: let appType = manifest.type || "web"; michael@0: if (["web", "privileged", "certified"].indexOf(appType) === -1) { michael@0: this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1)); michael@0: } else if (this.project.type == "hosted" && michael@0: ["certified", "privileged"].indexOf(appType) !== -1) { michael@0: this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1)); michael@0: } michael@0: michael@0: // certified app are not fully supported on the simulator michael@0: if (appType === "certified") { michael@0: this.warning(strings.GetStringFromName("validator.noCertifiedSupport")); michael@0: } michael@0: }; michael@0: michael@0: AppValidator.prototype.validate = function () { michael@0: this.errors = []; michael@0: this.warnings = []; michael@0: return this._getManifest(). michael@0: then((function (manifest) { michael@0: if (manifest) { michael@0: this.manifest = manifest; michael@0: this.validateManifest(manifest); michael@0: this.validateType(manifest); michael@0: return this.validateLaunchPath(manifest); michael@0: } michael@0: }).bind(this)); michael@0: }; michael@0: michael@0: exports.AppValidator = AppValidator;