toolkit/devtools/apps/app-actor-front.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 const {Ci, Cc, Cu, Cr} = require("chrome");
michael@0 2 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 3 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
michael@0 4 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 5 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
michael@0 6 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
michael@0 7
michael@0 8 // XXX: bug 912476 make this module a real protocol.js front
michael@0 9 // by converting webapps actor to protocol.js
michael@0 10
michael@0 11 const PR_USEC_PER_MSEC = 1000;
michael@0 12 const PR_RDWR = 0x04;
michael@0 13 const PR_CREATE_FILE = 0x08;
michael@0 14 const PR_TRUNCATE = 0x20;
michael@0 15
michael@0 16 const CHUNK_SIZE = 10000;
michael@0 17
michael@0 18 const appTargets = new Map();
michael@0 19
michael@0 20 function addDirToZip(writer, dir, basePath) {
michael@0 21 let files = dir.directoryEntries;
michael@0 22
michael@0 23 while (files.hasMoreElements()) {
michael@0 24 let file = files.getNext().QueryInterface(Ci.nsIFile);
michael@0 25
michael@0 26 if (file.isHidden() ||
michael@0 27 file.isSpecial() ||
michael@0 28 file.equals(writer.file))
michael@0 29 {
michael@0 30 continue;
michael@0 31 }
michael@0 32
michael@0 33 if (file.isDirectory()) {
michael@0 34 writer.addEntryDirectory(basePath + file.leafName + "/",
michael@0 35 file.lastModifiedTime * PR_USEC_PER_MSEC,
michael@0 36 true);
michael@0 37 addDirToZip(writer, file, basePath + file.leafName + "/");
michael@0 38 } else {
michael@0 39 writer.addEntryFile(basePath + file.leafName,
michael@0 40 Ci.nsIZipWriter.COMPRESSION_DEFAULT,
michael@0 41 file,
michael@0 42 true);
michael@0 43 }
michael@0 44 }
michael@0 45 }
michael@0 46
michael@0 47 /**
michael@0 48 * Convert an XPConnect result code to its name and message.
michael@0 49 * We have to extract them from an exception per bug 637307 comment 5.
michael@0 50 */
michael@0 51 function getResultTest(code) {
michael@0 52 let regexp =
michael@0 53 /^\[Exception... "(.*)" nsresult: "0x[0-9a-fA-F]* \((.*)\)" location: ".*" data: .*\]$/;
michael@0 54 let ex = Cc["@mozilla.org/js/xpc/Exception;1"].
michael@0 55 createInstance(Ci.nsIXPCException);
michael@0 56 ex.initialize(null, code, null, null, null, null);
michael@0 57 let [, message, name] = regexp.exec(ex.toString());
michael@0 58 return { name: name, message: message };
michael@0 59 }
michael@0 60
michael@0 61 function zipDirectory(zipFile, dirToArchive) {
michael@0 62 let deferred = promise.defer();
michael@0 63 let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
michael@0 64 writer.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
michael@0 65
michael@0 66 this.addDirToZip(writer, dirToArchive, "");
michael@0 67
michael@0 68 writer.processQueue({
michael@0 69 onStartRequest: function onStartRequest(request, context) {},
michael@0 70 onStopRequest: (request, context, status) => {
michael@0 71 if (status == Cr.NS_OK) {
michael@0 72 writer.close();
michael@0 73 deferred.resolve(zipFile);
michael@0 74 }
michael@0 75 else {
michael@0 76 let { name, message } = getResultText(status);
michael@0 77 deferred.reject(name + ": " + message);
michael@0 78 }
michael@0 79 }
michael@0 80 }, null);
michael@0 81
michael@0 82 return deferred.promise;
michael@0 83 }
michael@0 84
michael@0 85 function uploadPackage(client, webappsActor, packageFile) {
michael@0 86 let deferred = promise.defer();
michael@0 87
michael@0 88 let request = {
michael@0 89 to: webappsActor,
michael@0 90 type: "uploadPackage"
michael@0 91 };
michael@0 92 client.request(request, (res) => {
michael@0 93 openFile(res.actor);
michael@0 94 });
michael@0 95
michael@0 96 function openFile(actor) {
michael@0 97 OS.File.open(packageFile.path)
michael@0 98 .then(function (file) {
michael@0 99 uploadChunk(actor, file);
michael@0 100 });
michael@0 101 }
michael@0 102 function uploadChunk(actor, file) {
michael@0 103 file.read(CHUNK_SIZE)
michael@0 104 .then(function (bytes) {
michael@0 105 // To work around the fact that JSON.stringify translates the typed
michael@0 106 // array to object, we are encoding the typed array here into a string
michael@0 107 let chunk = String.fromCharCode.apply(null, bytes);
michael@0 108
michael@0 109 let request = {
michael@0 110 to: actor,
michael@0 111 type: "chunk",
michael@0 112 chunk: chunk
michael@0 113 };
michael@0 114 client.request(request, (res) => {
michael@0 115 if (bytes.length == CHUNK_SIZE) {
michael@0 116 uploadChunk(actor, file);
michael@0 117 } else {
michael@0 118 file.close().then(function () {
michael@0 119 endsUpload(actor);
michael@0 120 });
michael@0 121 }
michael@0 122 });
michael@0 123 });
michael@0 124 }
michael@0 125 function endsUpload(actor) {
michael@0 126 let request = {
michael@0 127 to: actor,
michael@0 128 type: "done"
michael@0 129 };
michael@0 130 client.request(request, (res) => {
michael@0 131 deferred.resolve(actor);
michael@0 132 });
michael@0 133 }
michael@0 134 return deferred.promise;
michael@0 135 }
michael@0 136
michael@0 137 function removeServerTemporaryFile(client, fileActor) {
michael@0 138 let request = {
michael@0 139 to: fileActor,
michael@0 140 type: "remove"
michael@0 141 };
michael@0 142 client.request(request);
michael@0 143 }
michael@0 144
michael@0 145 function installPackaged(client, webappsActor, packagePath, appId) {
michael@0 146 let deferred = promise.defer();
michael@0 147 let file = FileUtils.File(packagePath);
michael@0 148 let packagePromise;
michael@0 149 if (file.isDirectory()) {
michael@0 150 let tmpZipFile = FileUtils.getDir("TmpD", [], true);
michael@0 151 tmpZipFile.append("application.zip");
michael@0 152 tmpZipFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
michael@0 153 packagePromise = zipDirectory(tmpZipFile, file)
michael@0 154 } else {
michael@0 155 packagePromise = promise.resolve(file);
michael@0 156 }
michael@0 157 packagePromise.then((zipFile) => {
michael@0 158 uploadPackage(client, webappsActor, zipFile)
michael@0 159 .then((fileActor) => {
michael@0 160 let request = {
michael@0 161 to: webappsActor,
michael@0 162 type: "install",
michael@0 163 appId: appId,
michael@0 164 upload: fileActor
michael@0 165 };
michael@0 166 client.request(request, (res) => {
michael@0 167 // If the install method immediatly fails,
michael@0 168 // reject immediatly the installPackaged promise.
michael@0 169 // Otherwise, wait for webappsEvent for completion
michael@0 170 if (res.error) {
michael@0 171 deferred.reject(res);
michael@0 172 }
michael@0 173 if ("error" in res)
michael@0 174 deferred.reject({error: res.error, message: res.message});
michael@0 175 else
michael@0 176 deferred.resolve({appId: res.appId});
michael@0 177 });
michael@0 178 // Ensure deleting the temporary package file, but only if that a temporary
michael@0 179 // package created when we pass a directory as `packagePath`
michael@0 180 if (zipFile != file)
michael@0 181 zipFile.remove(false);
michael@0 182 // In case of success or error, ensure deleting the temporary package file
michael@0 183 // also created on the device, but only once install request is done
michael@0 184 deferred.promise.then(
michael@0 185 () => removeServerTemporaryFile(client, fileActor),
michael@0 186 () => removeServerTemporaryFile(client, fileActor));
michael@0 187 });
michael@0 188 });
michael@0 189 return deferred.promise;
michael@0 190 }
michael@0 191 exports.installPackaged = installPackaged;
michael@0 192
michael@0 193 function installHosted(client, webappsActor, appId, metadata, manifest) {
michael@0 194 let deferred = promise.defer();
michael@0 195 let request = {
michael@0 196 to: webappsActor,
michael@0 197 type: "install",
michael@0 198 appId: appId,
michael@0 199 metadata: metadata,
michael@0 200 manifest: manifest
michael@0 201 };
michael@0 202 client.request(request, (res) => {
michael@0 203 if (res.error) {
michael@0 204 deferred.reject(res);
michael@0 205 }
michael@0 206 if ("error" in res)
michael@0 207 deferred.reject({error: res.error, message: res.message});
michael@0 208 else
michael@0 209 deferred.resolve({appId: res.appId});
michael@0 210 });
michael@0 211 return deferred.promise;
michael@0 212 }
michael@0 213 exports.installHosted = installHosted;
michael@0 214
michael@0 215 function getTargetForApp(client, webappsActor, manifestURL) {
michael@0 216 // Ensure always returning the exact same JS object for a target
michael@0 217 // of the same app in order to show only one toolbox per app and
michael@0 218 // avoid re-creating lot of objects twice.
michael@0 219 let existingTarget = appTargets.get(manifestURL);
michael@0 220 if (existingTarget)
michael@0 221 return promise.resolve(existingTarget);
michael@0 222
michael@0 223 let deferred = promise.defer();
michael@0 224 let request = {
michael@0 225 to: webappsActor,
michael@0 226 type: "getAppActor",
michael@0 227 manifestURL: manifestURL,
michael@0 228 }
michael@0 229 client.request(request, (res) => {
michael@0 230 if (res.error) {
michael@0 231 deferred.reject(res.error);
michael@0 232 } else {
michael@0 233 let options = {
michael@0 234 form: res.actor,
michael@0 235 client: client,
michael@0 236 chrome: false
michael@0 237 };
michael@0 238
michael@0 239 devtools.TargetFactory.forRemoteTab(options).then((target) => {
michael@0 240 target.isApp = true;
michael@0 241 appTargets.set(manifestURL, target);
michael@0 242 target.on("close", () => {
michael@0 243 appTargets.delete(manifestURL);
michael@0 244 });
michael@0 245 deferred.resolve(target)
michael@0 246 }, (error) => {
michael@0 247 deferred.reject(error);
michael@0 248 });
michael@0 249 }
michael@0 250 });
michael@0 251 return deferred.promise;
michael@0 252 }
michael@0 253 exports.getTargetForApp = getTargetForApp;
michael@0 254
michael@0 255 function reloadApp(client, webappsActor, manifestURL) {
michael@0 256 let deferred = promise.defer();
michael@0 257 getTargetForApp(client,
michael@0 258 webappsActor,
michael@0 259 manifestURL).
michael@0 260 then((target) => {
michael@0 261 // Request the ContentActor to reload the app
michael@0 262 let request = {
michael@0 263 to: target.form.actor,
michael@0 264 type: "reload",
michael@0 265 manifestURL: manifestURL
michael@0 266 };
michael@0 267 client.request(request, (res) => {
michael@0 268 deferred.resolve();
michael@0 269 });
michael@0 270 }, () => {
michael@0 271 deferred.reject("Not running");
michael@0 272 });
michael@0 273 return deferred.promise;
michael@0 274 }
michael@0 275 exports.reloadApp = reloadApp;
michael@0 276
michael@0 277 function launchApp(client, webappsActor, manifestURL) {
michael@0 278 let deferred = promise.defer();
michael@0 279 let request = {
michael@0 280 to: webappsActor,
michael@0 281 type: "launch",
michael@0 282 manifestURL: manifestURL
michael@0 283 };
michael@0 284 client.request(request, (res) => {
michael@0 285 if (res.error) {
michael@0 286 deferred.reject(res.error);
michael@0 287 } else {
michael@0 288 deferred.resolve(res);
michael@0 289 }
michael@0 290 });
michael@0 291 return deferred.promise;
michael@0 292 }
michael@0 293 exports.launchApp = launchApp;
michael@0 294
michael@0 295 function closeApp(client, webappsActor, manifestURL) {
michael@0 296 let deferred = promise.defer();
michael@0 297 let request = {
michael@0 298 to: webappsActor,
michael@0 299 type: "close",
michael@0 300 manifestURL: manifestURL
michael@0 301 };
michael@0 302 client.request(request, (res) => {
michael@0 303 if (res.error) {
michael@0 304 deferred.reject(res.error);
michael@0 305 } else {
michael@0 306 deferred.resolve(res);
michael@0 307 }
michael@0 308 });
michael@0 309 return deferred.promise;
michael@0 310 }
michael@0 311 exports.closeApp = closeApp;

mercurial