Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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; |