1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/app-manager/app-validator.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,215 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +let {Ci,Cu,CC} = require("chrome"); 1.10 +const promise = require("devtools/toolkit/deprecated-sync-thenables"); 1.11 + 1.12 +const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm"); 1.13 +const {Services} = Cu.import("resource://gre/modules/Services.jsm"); 1.14 +let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1"); 1.15 +let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties"); 1.16 + 1.17 +function AppValidator(project) { 1.18 + this.project = project; 1.19 + this.errors = []; 1.20 + this.warnings = []; 1.21 +} 1.22 + 1.23 +AppValidator.prototype.error = function (message) { 1.24 + this.errors.push(message); 1.25 +}; 1.26 + 1.27 +AppValidator.prototype.warning = function (message) { 1.28 + this.warnings.push(message); 1.29 +}; 1.30 + 1.31 +AppValidator.prototype._getPackagedManifestFile = function () { 1.32 + let manifestFile = FileUtils.File(this.project.location); 1.33 + if (!manifestFile.exists()) { 1.34 + this.error(strings.GetStringFromName("validator.nonExistingFolder")); 1.35 + return null; 1.36 + } 1.37 + if (!manifestFile.isDirectory()) { 1.38 + this.error(strings.GetStringFromName("validator.expectProjectFolder")); 1.39 + return null; 1.40 + } 1.41 + manifestFile.append("manifest.webapp"); 1.42 + if (!manifestFile.exists() || !manifestFile.isFile()) { 1.43 + this.error(strings.GetStringFromName("validator.wrongManifestFileName")); 1.44 + return null; 1.45 + } 1.46 + return manifestFile; 1.47 +}; 1.48 + 1.49 +AppValidator.prototype._getPackagedManifestURL = function () { 1.50 + let manifestFile = this._getPackagedManifestFile(); 1.51 + if (!manifestFile) { 1.52 + return null; 1.53 + } 1.54 + return Services.io.newFileURI(manifestFile).spec; 1.55 +}; 1.56 + 1.57 +AppValidator.prototype._fetchManifest = function (manifestURL) { 1.58 + let deferred = promise.defer(); 1.59 + this.manifestURL = manifestURL; 1.60 + 1.61 + let req = new XMLHttpRequest(); 1.62 + try { 1.63 + req.open("GET", manifestURL, true); 1.64 + } catch(e) { 1.65 + this.error(strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1)); 1.66 + deferred.resolve(null); 1.67 + return deferred.promise; 1.68 + } 1.69 + req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; 1.70 + req.onload = (function () { 1.71 + let manifest = null; 1.72 + try { 1.73 + manifest = JSON.parse(req.responseText); 1.74 + } catch(e) { 1.75 + this.error(strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2)); 1.76 + } 1.77 + deferred.resolve(manifest); 1.78 + }).bind(this); 1.79 + req.onerror = (function () { 1.80 + this.error(strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2)); 1.81 + deferred.resolve(null); 1.82 + }).bind(this); 1.83 + 1.84 + try { 1.85 + req.send(null); 1.86 + } catch(e) { 1.87 + this.error(strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2)); 1.88 + deferred.resolve(); 1.89 + } 1.90 + 1.91 + return deferred.promise; 1.92 +}; 1.93 + 1.94 +AppValidator.prototype._getManifest = function () { 1.95 + let manifestURL; 1.96 + if (this.project.type == "packaged") { 1.97 + manifestURL = this._getPackagedManifestURL(); 1.98 + if (!manifestURL) 1.99 + return promise.resolve(null); 1.100 + } else if (this.project.type == "hosted") { 1.101 + manifestURL = this.project.location; 1.102 + try { 1.103 + Services.io.newURI(manifestURL, null, null); 1.104 + } catch(e) { 1.105 + this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message])); 1.106 + return promise.resolve(null); 1.107 + } 1.108 + } else { 1.109 + this.error(strings.formatStringFromName("validator.invalidProjectType", [this.project.type], 1)); 1.110 + return promise.resolve(null); 1.111 + } 1.112 + return this._fetchManifest(manifestURL); 1.113 +}; 1.114 + 1.115 +AppValidator.prototype.validateManifest = function (manifest) { 1.116 + if (!manifest.name) { 1.117 + this.error(strings.GetStringFromName("validator.missNameManifestProperty")); 1.118 + } 1.119 + 1.120 + if (!manifest.icons || Object.keys(manifest.icons).length === 0) { 1.121 + this.warning(strings.GetStringFromName("validator.missIconsManifestProperty")); 1.122 + } else if (!manifest.icons["128"]) { 1.123 + this.warning(strings.GetStringFromName("validator.missIconMarketplace")); 1.124 + } 1.125 +}; 1.126 + 1.127 +AppValidator.prototype._getOriginURL = function () { 1.128 + if (this.project.type == "packaged") { 1.129 + let manifestURL = Services.io.newURI(this.manifestURL, null, null); 1.130 + return Services.io.newURI(".", null, manifestURL).spec; 1.131 + } else if (this.project.type == "hosted") { 1.132 + return Services.io.newURI(this.project.location, null, null).prePath; 1.133 + } 1.134 +}; 1.135 + 1.136 +AppValidator.prototype.validateLaunchPath = function (manifest) { 1.137 + let deferred = promise.defer(); 1.138 + // The launch_path field has to start with a `/` 1.139 + if (manifest.launch_path && manifest.launch_path[0] !== "/") { 1.140 + this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1)); 1.141 + deferred.resolve(); 1.142 + return deferred.promise; 1.143 + } 1.144 + let origin = this._getOriginURL(); 1.145 + let path; 1.146 + if (this.project.type == "packaged") { 1.147 + path = "." + ( manifest.launch_path || "/index.html" ); 1.148 + } else if (this.project.type == "hosted") { 1.149 + path = manifest.launch_path || "/"; 1.150 + } 1.151 + let indexURL; 1.152 + try { 1.153 + indexURL = Services.io.newURI(path, null, Services.io.newURI(origin, null, null)).spec; 1.154 + } catch(e) { 1.155 + this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1)); 1.156 + deferred.resolve(); 1.157 + return deferred.promise; 1.158 + } 1.159 + 1.160 + let req = new XMLHttpRequest(); 1.161 + try { 1.162 + req.open("HEAD", indexURL, true); 1.163 + } catch(e) { 1.164 + this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); 1.165 + deferred.resolve(); 1.166 + return deferred.promise; 1.167 + } 1.168 + req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; 1.169 + req.onload = () => { 1.170 + if (req.status >= 400) 1.171 + this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2)); 1.172 + deferred.resolve(); 1.173 + }; 1.174 + req.onerror = () => { 1.175 + this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); 1.176 + deferred.resolve(); 1.177 + }; 1.178 + 1.179 + try { 1.180 + req.send(null); 1.181 + } catch(e) { 1.182 + this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); 1.183 + deferred.resolve(); 1.184 + } 1.185 + 1.186 + return deferred.promise; 1.187 +}; 1.188 + 1.189 +AppValidator.prototype.validateType = function (manifest) { 1.190 + let appType = manifest.type || "web"; 1.191 + if (["web", "privileged", "certified"].indexOf(appType) === -1) { 1.192 + this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1)); 1.193 + } else if (this.project.type == "hosted" && 1.194 + ["certified", "privileged"].indexOf(appType) !== -1) { 1.195 + this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1)); 1.196 + } 1.197 + 1.198 + // certified app are not fully supported on the simulator 1.199 + if (appType === "certified") { 1.200 + this.warning(strings.GetStringFromName("validator.noCertifiedSupport")); 1.201 + } 1.202 +}; 1.203 + 1.204 +AppValidator.prototype.validate = function () { 1.205 + this.errors = []; 1.206 + this.warnings = []; 1.207 + return this._getManifest(). 1.208 + then((function (manifest) { 1.209 + if (manifest) { 1.210 + this.manifest = manifest; 1.211 + this.validateManifest(manifest); 1.212 + this.validateType(manifest); 1.213 + return this.validateLaunchPath(manifest); 1.214 + } 1.215 + }).bind(this)); 1.216 +}; 1.217 + 1.218 +exports.AppValidator = AppValidator;