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.
1 if (this.Components) {
2 throw new Error("This worker can only be loaded from a worker thread");
3 }
5 /* This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
10 // Worker thread for osfile asynchronous front-end
12 // Exception names to be posted from the worker thread to main thread
13 const EXCEPTION_NAMES = {
14 EvalError: "EvalError",
15 InternalError: "InternalError",
16 RangeError: "RangeError",
17 ReferenceError: "ReferenceError",
18 SyntaxError: "SyntaxError",
19 TypeError: "TypeError",
20 URIError: "URIError"
21 };
23 (function(exports) {
24 "use strict";
26 // Timestamps, for use in Telemetry.
27 // The object is set to |null| once it has been sent
28 // to the main thread.
29 let timeStamps = {
30 entered: Date.now(),
31 loaded: null
32 };
34 importScripts("resource://gre/modules/osfile.jsm");
36 let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
37 let LOG = SharedAll.LOG.bind(SharedAll, "Agent");
39 // Post a message to the parent, decorate it with statistics if
40 // necessary. Use this instead of self.postMessage.
41 function post(message, ...transfers) {
42 if (timeStamps) {
43 message.timeStamps = timeStamps;
44 timeStamps = null;
45 }
46 self.postMessage(message, ...transfers);
47 }
49 /**
50 * Communications with the controller.
51 *
52 * Accepts messages:
53 * {fun:function_name, args:array_of_arguments_or_null, id:id}
54 *
55 * Sends messages:
56 * {ok: result, id:id} / {fail: serialized_form_of_OS.File.Error, id:id}
57 */
58 self.onmessage = function onmessage(msg) {
59 let data = msg.data;
60 LOG("Received message", data);
61 let id = data.id;
63 let start;
64 let options;
65 if (data.args) {
66 options = data.args[data.args.length - 1];
67 }
68 // If |outExecutionDuration| option was supplied, start measuring the
69 // duration of the operation.
70 if (options && typeof options === "object" && "outExecutionDuration" in options) {
71 start = Date.now();
72 }
74 let result;
75 let exn;
76 let durationMs;
77 try {
78 let method = data.fun;
79 LOG("Calling method", method);
80 result = Agent[method].apply(Agent, data.args);
81 LOG("Method", method, "succeeded");
82 } catch (ex) {
83 exn = ex;
84 LOG("Error while calling agent method", exn, exn.moduleStack || exn.stack || "");
85 }
87 if (start) {
88 // Record duration
89 durationMs = Date.now() - start;
90 LOG("Method took", durationMs, "ms");
91 }
93 // Now, post a reply, possibly as an uncaught error.
94 // We post this message from outside the |try ... catch| block
95 // to avoid capturing errors that take place during |postMessage| and
96 // built-in serialization.
97 if (!exn) {
98 LOG("Sending positive reply", result, "id is", id);
99 if (result instanceof Meta) {
100 if ("transfers" in result.meta) {
101 // Take advantage of zero-copy transfers
102 post({ok: result.data, id: id, durationMs: durationMs},
103 result.meta.transfers);
104 } else {
105 post({ok: result.data, id:id, durationMs: durationMs});
106 }
107 if (result.meta.shutdown || false) {
108 // Time to close the worker
109 self.close();
110 }
111 } else {
112 post({ok: result, id:id, durationMs: durationMs});
113 }
114 } else if (exn == StopIteration) {
115 // StopIteration cannot be serialized automatically
116 LOG("Sending back StopIteration");
117 post({StopIteration: true, id: id, durationMs: durationMs});
118 } else if (exn instanceof exports.OS.File.Error) {
119 LOG("Sending back OS.File error", exn, "id is", id);
120 // Instances of OS.File.Error know how to serialize themselves
121 // (deserialization ensures that we end up with OS-specific
122 // instances of |OS.File.Error|)
123 post({fail: exports.OS.File.Error.toMsg(exn), id:id, durationMs: durationMs});
124 } else if (exn.constructor.name in EXCEPTION_NAMES) {
125 LOG("Sending back exception", exn.constructor.name);
126 post({fail: {exn: exn.constructor.name, message: exn.message,
127 fileName: exn.moduleName || exn.fileName, lineNumber: exn.lineNumber},
128 id: id, durationMs: durationMs});
129 } else {
130 // Other exceptions do not, and should be propagated through DOM's
131 // built-in mechanism for uncaught errors, although this mechanism
132 // may lose interesting information.
133 LOG("Sending back regular error", exn, exn.moduleStack || exn.stack, "id is", id);
135 throw exn;
136 }
137 };
139 /**
140 * A data structure used to track opened resources
141 */
142 let ResourceTracker = function ResourceTracker() {
143 // A number used to generate ids
144 this._idgen = 0;
145 // A map from id to resource
146 this._map = new Map();
147 };
148 ResourceTracker.prototype = {
149 /**
150 * Get a resource from its unique identifier.
151 */
152 get: function(id) {
153 let result = this._map.get(id);
154 if (result == null) {
155 return result;
156 }
157 return result.resource;
158 },
159 /**
160 * Remove a resource from its unique identifier.
161 */
162 remove: function(id) {
163 if (!this._map.has(id)) {
164 throw new Error("Cannot find resource id " + id);
165 }
166 this._map.delete(id);
167 },
168 /**
169 * Add a resource, return a new unique identifier
170 *
171 * @param {*} resource A resource.
172 * @param {*=} info Optional information. For debugging purposes.
173 *
174 * @return {*} A unique identifier. For the moment, this is a number,
175 * but this might not remain the case forever.
176 */
177 add: function(resource, info) {
178 let id = this._idgen++;
179 this._map.set(id, {resource:resource, info:info});
180 return id;
181 },
182 /**
183 * Return a list of all open resources i.e. the ones still present in
184 * ResourceTracker's _map.
185 */
186 listOpenedResources: function listOpenedResources() {
187 return [resource.info.path for ([id, resource] of this._map)];
188 }
189 };
191 /**
192 * A map of unique identifiers to opened files.
193 */
194 let OpenedFiles = new ResourceTracker();
196 /**
197 * Execute a function in the context of a given file.
198 *
199 * @param {*} id A unique identifier, as used by |OpenFiles|.
200 * @param {Function} f A function to call.
201 * @param {boolean} ignoreAbsent If |true|, the error is ignored. Otherwise, the error causes an exception.
202 * @return The return value of |f()|
203 *
204 * This function attempts to get the file matching |id|. If
205 * the file exists, it executes |f| within the |this| set
206 * to the corresponding file. Otherwise, it throws an error.
207 */
208 let withFile = function withFile(id, f, ignoreAbsent) {
209 let file = OpenedFiles.get(id);
210 if (file == null) {
211 if (!ignoreAbsent) {
212 throw OS.File.Error.closed("accessing file");
213 }
214 return undefined;
215 }
216 return f.call(file);
217 };
219 let OpenedDirectoryIterators = new ResourceTracker();
220 let withDir = function withDir(fd, f, ignoreAbsent) {
221 let file = OpenedDirectoryIterators.get(fd);
222 if (file == null) {
223 if (!ignoreAbsent) {
224 throw OS.File.Error.closed("accessing directory");
225 }
226 return undefined;
227 }
228 if (!(file instanceof File.DirectoryIterator)) {
229 throw new Error("file is not a directory iterator " + file.__proto__.toSource());
230 }
231 return f.call(file);
232 };
234 let Type = exports.OS.Shared.Type;
236 let File = exports.OS.File;
238 /**
239 * A constructor used to return data to the caller thread while
240 * also executing some specific treatment (e.g. shutting down
241 * the current thread, transmitting data instead of copying it).
242 *
243 * @param {object=} data The data to return to the caller thread.
244 * @param {object=} meta Additional instructions, as an object
245 * that may contain the following fields:
246 * - {bool} shutdown If |true|, shut down the current thread after
247 * having sent the result.
248 * - {Array} transfers An array of objects that should be transferred
249 * instead of being copied.
250 *
251 * @constructor
252 */
253 let Meta = function Meta(data, meta) {
254 this.data = data;
255 this.meta = meta;
256 };
258 /**
259 * The agent.
260 *
261 * It is in charge of performing method-specific deserialization
262 * of messages, calling the function/method of OS.File and serializing
263 * back the results.
264 */
265 let Agent = {
266 // Update worker's OS.Shared.DEBUG flag message from controller.
267 SET_DEBUG: function(aDEBUG) {
268 SharedAll.Config.DEBUG = aDEBUG;
269 },
270 // Return worker's current OS.Shared.DEBUG value to controller.
271 // Note: This is used for testing purposes.
272 GET_DEBUG: function() {
273 return SharedAll.Config.DEBUG;
274 },
275 /**
276 * Execute shutdown sequence, returning data on leaked file descriptors.
277 *
278 * @param {bool} If |true|, kill the worker if this would not cause
279 * leaks.
280 */
281 Meta_shutdown: function(kill) {
282 let result = {
283 openedFiles: OpenedFiles.listOpenedResources(),
284 openedDirectoryIterators: OpenedDirectoryIterators.listOpenedResources(),
285 killed: false // Placeholder
286 };
288 // Is it safe to kill the worker?
289 let safe = result.openedFiles.length == 0
290 && result.openedDirectoryIterators.length == 0;
291 result.killed = safe && kill;
293 return new Meta(result, {shutdown: result.killed});
294 },
295 // Functions of OS.File
296 stat: function stat(path, options) {
297 return exports.OS.File.Info.toMsg(
298 exports.OS.File.stat(Type.path.fromMsg(path), options));
299 },
300 setPermissions: function setPermissions(path, options = {}) {
301 return exports.OS.File.setPermissions(Type.path.fromMsg(path), options);
302 },
303 setDates: function setDates(path, accessDate, modificationDate) {
304 return exports.OS.File.setDates(Type.path.fromMsg(path), accessDate,
305 modificationDate);
306 },
307 getCurrentDirectory: function getCurrentDirectory() {
308 return exports.OS.Shared.Type.path.toMsg(File.getCurrentDirectory());
309 },
310 setCurrentDirectory: function setCurrentDirectory(path) {
311 File.setCurrentDirectory(exports.OS.Shared.Type.path.fromMsg(path));
312 },
313 copy: function copy(sourcePath, destPath, options) {
314 return File.copy(Type.path.fromMsg(sourcePath),
315 Type.path.fromMsg(destPath), options);
316 },
317 move: function move(sourcePath, destPath, options) {
318 return File.move(Type.path.fromMsg(sourcePath),
319 Type.path.fromMsg(destPath), options);
320 },
321 getAvailableFreeSpace: function getAvailableFreeSpace(sourcePath) {
322 return Type.uint64_t.toMsg(
323 File.getAvailableFreeSpace(Type.path.fromMsg(sourcePath)));
324 },
325 makeDir: function makeDir(path, options) {
326 return File.makeDir(Type.path.fromMsg(path), options);
327 },
328 removeEmptyDir: function removeEmptyDir(path, options) {
329 return File.removeEmptyDir(Type.path.fromMsg(path), options);
330 },
331 remove: function remove(path) {
332 return File.remove(Type.path.fromMsg(path));
333 },
334 open: function open(path, mode, options) {
335 let filePath = Type.path.fromMsg(path);
336 let file = File.open(filePath, mode, options);
337 return OpenedFiles.add(file, {
338 // Adding path information to keep track of opened files
339 // to report leaks when debugging.
340 path: filePath
341 });
342 },
343 openUnique: function openUnique(path, options) {
344 let filePath = Type.path.fromMsg(path);
345 let openedFile = OS.Shared.AbstractFile.openUnique(filePath, options);
346 let resourceId = OpenedFiles.add(openedFile.file, {
347 // Adding path information to keep track of opened files
348 // to report leaks when debugging.
349 path: openedFile.path
350 });
352 return {
353 path: openedFile.path,
354 file: resourceId
355 };
356 },
357 read: function read(path, bytes, options) {
358 let data = File.read(Type.path.fromMsg(path), bytes, options);
359 if (typeof data == "string") {
360 return data;
361 }
362 return new Meta({
363 buffer: data.buffer,
364 byteOffset: data.byteOffset,
365 byteLength: data.byteLength
366 }, {
367 transfers: [data.buffer]
368 });
369 },
370 exists: function exists(path) {
371 return File.exists(Type.path.fromMsg(path));
372 },
373 writeAtomic: function writeAtomic(path, buffer, options) {
374 if (options.tmpPath) {
375 options.tmpPath = Type.path.fromMsg(options.tmpPath);
376 }
377 return File.writeAtomic(Type.path.fromMsg(path),
378 Type.voidptr_t.fromMsg(buffer),
379 options
380 );
381 },
382 removeDir: function(path, options) {
383 return File.removeDir(Type.path.fromMsg(path), options);
384 },
385 new_DirectoryIterator: function new_DirectoryIterator(path, options) {
386 let directoryPath = Type.path.fromMsg(path);
387 let iterator = new File.DirectoryIterator(directoryPath, options);
388 return OpenedDirectoryIterators.add(iterator, {
389 // Adding path information to keep track of opened directory
390 // iterators to report leaks when debugging.
391 path: directoryPath
392 });
393 },
394 // Methods of OS.File
395 File_prototype_close: function close(fd) {
396 return withFile(fd,
397 function do_close() {
398 try {
399 return this.close();
400 } finally {
401 OpenedFiles.remove(fd);
402 }
403 });
404 },
405 File_prototype_stat: function stat(fd) {
406 return withFile(fd,
407 function do_stat() {
408 return exports.OS.File.Info.toMsg(this.stat());
409 });
410 },
411 File_prototype_setPermissions: function setPermissions(fd, options = {}) {
412 return withFile(fd,
413 function do_setPermissions() {
414 return this.setPermissions(options);
415 });
416 },
417 File_prototype_setDates: function setDates(fd, accessTime, modificationTime) {
418 return withFile(fd,
419 function do_setDates() {
420 return this.setDates(accessTime, modificationTime);
421 });
422 },
423 File_prototype_read: function read(fd, nbytes, options) {
424 return withFile(fd,
425 function do_read() {
426 let data = this.read(nbytes, options);
427 return new Meta({
428 buffer: data.buffer,
429 byteOffset: data.byteOffset,
430 byteLength: data.byteLength
431 }, {
432 transfers: [data.buffer]
433 });
434 }
435 );
436 },
437 File_prototype_readTo: function readTo(fd, buffer, options) {
438 return withFile(fd,
439 function do_readTo() {
440 return this.readTo(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
441 options);
442 });
443 },
444 File_prototype_write: function write(fd, buffer, options) {
445 return withFile(fd,
446 function do_write() {
447 return this.write(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
448 options);
449 });
450 },
451 File_prototype_setPosition: function setPosition(fd, pos, whence) {
452 return withFile(fd,
453 function do_setPosition() {
454 return this.setPosition(pos, whence);
455 });
456 },
457 File_prototype_getPosition: function getPosition(fd) {
458 return withFile(fd,
459 function do_getPosition() {
460 return this.getPosition();
461 });
462 },
463 File_prototype_flush: function flush(fd) {
464 return withFile(fd,
465 function do_flush() {
466 return this.flush();
467 });
468 },
469 // Methods of OS.File.DirectoryIterator
470 DirectoryIterator_prototype_next: function next(dir) {
471 return withDir(dir,
472 function do_next() {
473 try {
474 return File.DirectoryIterator.Entry.toMsg(this.next());
475 } catch (x) {
476 if (x == StopIteration) {
477 OpenedDirectoryIterators.remove(dir);
478 }
479 throw x;
480 }
481 }, false);
482 },
483 DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {
484 return withDir(dir,
485 function do_nextBatch() {
486 let result;
487 try {
488 result = this.nextBatch(size);
489 } catch (x) {
490 OpenedDirectoryIterators.remove(dir);
491 throw x;
492 }
493 return result.map(File.DirectoryIterator.Entry.toMsg);
494 }, false);
495 },
496 DirectoryIterator_prototype_close: function close(dir) {
497 return withDir(dir,
498 function do_close() {
499 this.close();
500 OpenedDirectoryIterators.remove(dir);
501 }, true);// ignore error to support double-closing |DirectoryIterator|
502 },
503 DirectoryIterator_prototype_exists: function exists(dir) {
504 return withDir(dir,
505 function do_exists() {
506 return this.exists();
507 });
508 }
509 };
510 if (!SharedAll.Constants.Win) {
511 Agent.unixSymLink = function unixSymLink(sourcePath, destPath) {
512 return File.unixSymLink(Type.path.fromMsg(sourcePath),
513 Type.path.fromMsg(destPath));
514 };
515 }
517 timeStamps.loaded = Date.now();
518 })(this);