toolkit/components/osfile/modules/_PromiseWorker.jsm

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 /**
     2  * Thin wrapper around a ChromeWorker that wraps postMessage/onmessage/onerror
     3  * as promises.
     4  *
     5  * Not for public use yet.
     6  */
     8 "use strict";
    10 this.EXPORTED_SYMBOLS = ["PromiseWorker"];
    12 // The library of promises.
    13 Components.utils.import("resource://gre/modules/Promise.jsm", this);
    15 /**
    16  * An implementation of queues (FIFO).
    17  *
    18  * The current implementation uses one array, runs in O(n ^ 2), and is optimized
    19  * for the case in which queues are generally short.
    20  */
    21 let Queue = function Queue() {
    22   this._array = [];
    23 };
    24 Queue.prototype = {
    25   pop: function pop() {
    26     return this._array.shift();
    27   },
    28   push: function push(x) {
    29     return this._array.push(x);
    30   },
    31   isEmpty: function isEmpty() {
    32     return this._array.length == 0;
    33   }
    34 };
    36 /**
    37  * An object responsible for dispatching messages to
    38  * a chrome worker and routing the responses.
    39  *
    40  * @param {string} url The url containing the source code for this worker,
    41  * as in constructor ChromeWorker.
    42  * @param {Function} log A logging function.
    43  *
    44  * @constructor
    45  */
    46 function PromiseWorker(url, log) {
    47   if (typeof url != "string") {
    48     throw new TypeError("Expecting a string");
    49   }
    50   if (typeof log !== "function") {
    51     throw new TypeError("log is expected to be a function");
    52   }
    53   this._log = log;
    54   this._url = url;
    56   /**
    57    * The queue of deferred, waiting for the completion of their
    58    * respective job by the worker.
    59    *
    60    * Each item in the list may contain an additional field |closure|,
    61    * used to store strong references to value that must not be
    62    * garbage-collected before the reply has been received (e.g.
    63    * arrays).
    64    *
    65    * @type {Queue<{deferred:deferred, closure:*=}>}
    66    */
    67   this._queue = new Queue();
    69   /**
    70    * The number of the current message.
    71    *
    72    * Used for debugging purposes.
    73    */
    74   this._id = 0;
    76   /**
    77    * The instant at which the worker was launched.
    78    */
    79   this.launchTimeStamp = null;
    81   /**
    82    * Timestamps provided by the worker for statistics purposes.
    83    */
    84   this.workerTimeStamps = null;
    85 }
    86 PromiseWorker.prototype = {
    87   /**
    88    * Instantiate the worker lazily.
    89    */
    90   get _worker() {
    91     delete this._worker;
    92     let worker = new ChromeWorker(this._url);
    93     let self = this;
    94     Object.defineProperty(this, "_worker", {value:
    95       worker
    96     });
    98     // We assume that we call to _worker for the purpose of calling
    99     // postMessage().
   100     this.launchTimeStamp = Date.now();
   102     /**
   103      * Receive errors that are not instances of OS.File.Error, propagate
   104      * them to the listeners.
   105      *
   106      * The worker knows how to serialize errors that are instances
   107      * of |OS.File.Error|. These are treated by |worker.onmessage|.
   108      * However, for other errors, we rely on DOM's mechanism for
   109      * serializing errors, which transmits these errors through
   110      * |worker.onerror|.
   111      *
   112      * @param {Error} error Some JS error.
   113      */
   114     worker.onerror = function onerror(error) {
   115       self._log("Received uncaught error from worker", error.message, error.filename, error.lineno);
   116       error.preventDefault();
   117       let {deferred} = self._queue.pop();
   118       deferred.reject(error);
   119     };
   121     /**
   122      * Receive messages from the worker, propagate them to the listeners.
   123      *
   124      * Messages must have one of the following shapes:
   125      * - {ok: some_value} in case of success
   126      * - {fail: some_error} in case of error, where
   127      *    some_error is an instance of |PromiseWorker.WorkerError|
   128      *
   129      * Messages may also contain a field |id| to help
   130      * with debugging.
   131      *
   132      * Messages may also optionally contain a field |durationMs|, holding
   133      * the duration of the function call in milliseconds.
   134      *
   135      * @param {*} msg The message received from the worker.
   136      */
   137     worker.onmessage = function onmessage(msg) {
   138       self._log("Received message from worker", msg.data);
   139       let handler = self._queue.pop();
   140       let deferred = handler.deferred;
   141       let data = msg.data;
   142       if (data.id != handler.id) {
   143         throw new Error("Internal error: expecting msg " + handler.id + ", " +
   144                         " got " + data.id + ": " + JSON.stringify(msg.data));
   145       }
   146       if ("timeStamps" in data) {
   147         self.workerTimeStamps = data.timeStamps;
   148       }
   149       if ("ok" in data) {
   150         // Pass the data to the listeners.
   151         deferred.resolve(data);
   152       } else if ("StopIteration" in data) {
   153         // We have received a StopIteration error
   154         deferred.reject(StopIteration);
   155       } if ("fail" in data) {
   156         // We have received an error that was serialized by the
   157         // worker.
   158         deferred.reject(new PromiseWorker.WorkerError(data.fail));
   159       }
   160     };
   161     return worker;
   162   },
   164   /**
   165    * Post a message to a worker.
   166    *
   167    * @param {string} fun The name of the function to call.
   168    * @param {Array} array The contents of the message.
   169    * @param {*=} closure An object holding references that should not be
   170    * garbage-collected before the message treatment is complete.
   171    *
   172    * @return {promise}
   173    */
   174   post: function post(fun, array, closure) {
   175     let deferred = Promise.defer();
   176     let id = ++this._id;
   177     let message = {fun: fun, args: array, id: id};
   178     this._log("Posting message", message);
   179     try {
   180       this._worker.postMessage(message);
   181     } catch (ex if typeof ex == "number") {
   182       this._log("Could not post message", message, "due to xpcom error", ex);
   183       // handle raw xpcom errors (see eg bug 961317)
   184       return Promise.reject(new Components.Exception("Error in postMessage", ex));
   185     } catch (ex) {
   186       this._log("Could not post message", message, "due to error", ex);
   187       return Promise.reject(ex);
   188     }
   190     this._queue.push({deferred:deferred, closure: closure, id: id});
   191     this._log("Message posted");
   192     return deferred.promise;
   193   }
   194 };
   196 /**
   197  * An error that has been serialized by the worker.
   198  *
   199  * @constructor
   200  */
   201 PromiseWorker.WorkerError = function WorkerError(data) {
   202   this.data = data;
   203 };
   205 this.PromiseWorker = PromiseWorker;

mercurial