toolkit/components/osfile/modules/osfile_async_worker.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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);

mercurial