dom/browser-element/mochitest/browserElementTestHelpers.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/browser-element/mochitest/browserElementTestHelpers.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,574 @@
     1.4 +/* Any copyright is dedicated to the public domain.
     1.5 +   http://creativecommons.org/publicdomain/zero/1.0/ */
     1.6 +
     1.7 +// Helpers for managing the browser frame preferences.
     1.8 +"use strict";
     1.9 +
    1.10 +function _getPath() {
    1.11 +  return window.location.pathname
    1.12 +               .substring(0, window.location.pathname.lastIndexOf('/'))
    1.13 +               .replace("/priority", "");
    1.14 +}
    1.15 +
    1.16 +const browserElementTestHelpers = {
    1.17 +  _getBoolPref: function(pref) {
    1.18 +    try {
    1.19 +      return SpecialPowers.getBoolPref(pref);
    1.20 +    }
    1.21 +    catch (e) {
    1.22 +      return undefined;
    1.23 +    }
    1.24 +  },
    1.25 +
    1.26 +  _setPref: function(pref, value) {
    1.27 +    this.lockTestReady();
    1.28 +    if (value !== undefined && value !== null) {
    1.29 +      SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, this.unlockTestReady.bind(this));
    1.30 +    } else {
    1.31 +      SpecialPowers.pushPrefEnv({'clear': [[pref]]}, this.unlockTestReady.bind(this));
    1.32 +    }
    1.33 +  },
    1.34 +
    1.35 +  _setPrefs: function() {
    1.36 +    this.lockTestReady();
    1.37 +    SpecialPowers.pushPrefEnv({'set': Array.slice(arguments)}, this.unlockTestReady.bind(this));
    1.38 +  },
    1.39 +
    1.40 +  _testReadyLockCount: 0,
    1.41 +  _firedTestReady: false,
    1.42 +  lockTestReady: function() {
    1.43 +    this._testReadyLockCount++;
    1.44 +  },
    1.45 +
    1.46 +  unlockTestReady: function() {
    1.47 +    this._testReadyLockCount--;
    1.48 +    if (this._testReadyLockCount == 0 && !this._firedTestReady) {
    1.49 +      this._firedTestReady = true;
    1.50 +      dispatchEvent(new Event("testready"));
    1.51 +    }
    1.52 +  },
    1.53 +
    1.54 +  enableProcessPriorityManager: function() {
    1.55 +    this._setPrefs(
    1.56 +      ['dom.ipc.processPriorityManager.testMode', true],
    1.57 +      ['dom.ipc.processPriorityManager.enabled', true],
    1.58 +      ['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
    1.59 +    );
    1.60 +  },
    1.61 +
    1.62 +  setEnabledPref: function(value) {
    1.63 +    this._setPref('dom.mozBrowserFramesEnabled', value);
    1.64 +  },
    1.65 +
    1.66 +  getOOPByDefaultPref: function() {
    1.67 +    return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
    1.68 +  },
    1.69 +
    1.70 +  addPermission: function() {
    1.71 +    SpecialPowers.addPermission("browser", true, document);
    1.72 +    this.tempPermissions.push(location.href)
    1.73 +  },
    1.74 +
    1.75 +  'tempPermissions': [],
    1.76 +  addPermissionForUrl: function(url) {
    1.77 +    SpecialPowers.addPermission("browser", true, url);
    1.78 +    this.tempPermissions.push(url);
    1.79 +  },
    1.80 +
    1.81 +  _observers: [],
    1.82 +
    1.83 +  // This function is a wrapper which lets you register an observer to one of
    1.84 +  // the process priority manager's test-only topics.  observerFn should be a
    1.85 +  // function which takes (subject, topic, data).
    1.86 +  //
    1.87 +  // We'll clean up any observers you add at the end of the test.
    1.88 +  addProcessPriorityObserver: function(processPriorityTopic, observerFn) {
    1.89 +    var topic = "process-priority-manager:TEST-ONLY:" + processPriorityTopic;
    1.90 +
    1.91 +    // SpecialPowers appears to require that the observer be an object, not a
    1.92 +    // function.
    1.93 +    var observer = {
    1.94 +      observe: observerFn
    1.95 +    };
    1.96 +
    1.97 +    SpecialPowers.addObserver(observer, topic, /* weak = */ false);
    1.98 +    this._observers.push([observer, topic]);
    1.99 +  },
   1.100 +
   1.101 +  cleanUp: function() {
   1.102 +    for (var i = 0; i < this.tempPermissions.length; i++) {
   1.103 +      SpecialPowers.removePermission("browser", this.tempPermissions[i]);
   1.104 +    }
   1.105 +
   1.106 +    for (var i = 0; i < this._observers.length; i++) {
   1.107 +      SpecialPowers.removeObserver(this._observers[i][0],
   1.108 +                                   this._observers[i][1]);
   1.109 +    }
   1.110 +  },
   1.111 +
   1.112 +  // Some basically-empty pages from different domains you can load.
   1.113 +  'emptyPage1': 'http://example.com' + _getPath() + '/file_empty.html',
   1.114 +  'emptyPage2': 'http://example.org' + _getPath() + '/file_empty.html',
   1.115 +  'emptyPage3': 'http://test1.example.org' + _getPath() + '/file_empty.html',
   1.116 +  'focusPage': 'http://example.org' + _getPath() + '/file_focus.html',
   1.117 +};
   1.118 +
   1.119 +// Returns a promise which is resolved when a subprocess is created.  The
   1.120 +// argument to resolve() is the childID of the subprocess.
   1.121 +function expectProcessCreated() {
   1.122 +  var deferred = Promise.defer();
   1.123 +
   1.124 +  var observed = false;
   1.125 +  browserElementTestHelpers.addProcessPriorityObserver(
   1.126 +    "process-created",
   1.127 +    function(subject, topic, data) {
   1.128 +      // Don't run this observer twice, so we don't ok(true) twice.  (It's fine
   1.129 +      // to resolve a promise twice; the second resolve() call does nothing.)
   1.130 +      if (observed) {
   1.131 +        return;
   1.132 +      }
   1.133 +      observed = true;
   1.134 +
   1.135 +      var childID = parseInt(data);
   1.136 +      ok(true, 'Got new process, id=' + childID);
   1.137 +      deferred.resolve(childID);
   1.138 +    }
   1.139 +  );
   1.140 +
   1.141 +  return deferred.promise;
   1.142 +}
   1.143 +
   1.144 +// Just like expectProcessCreated(), except we'll call ok(false) if a second
   1.145 +// process is created.
   1.146 +function expectOnlyOneProcessCreated() {
   1.147 +  var p = expectProcessCreated();
   1.148 +  p.then(function() {
   1.149 +    expectProcessCreated().then(function(childID) {
   1.150 +      ok(false, 'Got unexpected process creation, childID=' + childID);
   1.151 +    });
   1.152 +  });
   1.153 +  return p;
   1.154 +}
   1.155 +
   1.156 +// Returns a promise which is resolved or rejected the next time the process
   1.157 +// childID changes its priority.  We resolve if the (priority, CPU priority)
   1.158 +// tuple matches (expectedPriority, expectedCPUPriority) and we reject
   1.159 +// otherwise.
   1.160 +//
   1.161 +// expectedCPUPriority is an optional argument; if it's not specified, we
   1.162 +// resolve if priority matches expectedPriority.
   1.163 +
   1.164 +function expectPriorityChange(childID, expectedPriority,
   1.165 +                              /* optional */ expectedCPUPriority) {
   1.166 +  var deferred = Promise.defer();
   1.167 +
   1.168 +  var observed = false;
   1.169 +  browserElementTestHelpers.addProcessPriorityObserver(
   1.170 +    'process-priority-set',
   1.171 +    function(subject, topic, data) {
   1.172 +      if (observed) {
   1.173 +        return;
   1.174 +      }
   1.175 +
   1.176 +      var [id, priority, cpuPriority] = data.split(":");
   1.177 +      if (id != childID) {
   1.178 +        return;
   1.179 +      }
   1.180 +
   1.181 +      // Make sure we run the is() calls in this observer only once, otherwise
   1.182 +      // we'll expect /every/ priority change to match expectedPriority.
   1.183 +      observed = true;
   1.184 +
   1.185 +      is(priority, expectedPriority,
   1.186 +         'Expected priority of childID ' + childID +
   1.187 +         ' to change to ' + expectedPriority);
   1.188 +
   1.189 +      if (expectedCPUPriority) {
   1.190 +        is(cpuPriority, expectedCPUPriority,
   1.191 +           'Expected CPU priority of childID ' + childID +
   1.192 +           ' to change to ' + expectedCPUPriority);
   1.193 +      }
   1.194 +
   1.195 +      if (priority == expectedPriority &&
   1.196 +          (!expectedCPUPriority || expectedCPUPriority == cpuPriority)) {
   1.197 +        deferred.resolve();
   1.198 +      } else {
   1.199 +        deferred.reject();
   1.200 +      }
   1.201 +    }
   1.202 +  );
   1.203 +
   1.204 +  return deferred.promise;
   1.205 +}
   1.206 +
   1.207 +// Returns a promise which is resolved or rejected the next time the background
   1.208 +// process childID changes its priority.  We resolve if the backgroundLRU
   1.209 +// matches expectedBackgroundLRU and we reject otherwise.
   1.210 +
   1.211 +function expectPriorityWithBackgroundLRUSet(childID, expectedBackgroundLRU) {
   1.212 +  var deferred = Promise.defer();
   1.213 +
   1.214 +  browserElementTestHelpers.addProcessPriorityObserver(
   1.215 +    'process-priority-with-background-LRU-set',
   1.216 +    function(subject, topic, data) {
   1.217 +
   1.218 +      var [id, priority, cpuPriority, backgroundLRU] = data.split(":");
   1.219 +      if (id != childID) {
   1.220 +        return;
   1.221 +      }
   1.222 +
   1.223 +      is(backgroundLRU, expectedBackgroundLRU,
   1.224 +         'Expected backgroundLRU ' + backgroundLRU + ' of childID ' + childID +
   1.225 +         ' to change to ' + expectedBackgroundLRU);
   1.226 +
   1.227 +      if (backgroundLRU == expectedBackgroundLRU) {
   1.228 +        deferred.resolve();
   1.229 +      } else {
   1.230 +        deferred.reject();
   1.231 +      }
   1.232 +    }
   1.233 +  );
   1.234 +
   1.235 +  return deferred.promise;
   1.236 +}
   1.237 +
   1.238 +// Returns a promise which is resolved the first time the given iframe fires
   1.239 +// the mozbrowser##eventName event.
   1.240 +function expectMozbrowserEvent(iframe, eventName) {
   1.241 +  var deferred = Promise.defer();
   1.242 +  iframe.addEventListener('mozbrowser' + eventName, function handler(e) {
   1.243 +    iframe.removeEventListener('mozbrowser' + eventName, handler);
   1.244 +    deferred.resolve(e);
   1.245 +  });
   1.246 +  return deferred.promise;
   1.247 +}
   1.248 +
   1.249 +// Set some prefs:
   1.250 +//
   1.251 +//  * browser.pagethumbnails.capturing_disabled: true
   1.252 +//
   1.253 +//    Disable tab view; it seriously messes us up.
   1.254 +//
   1.255 +//  * dom.ipc.browser_frames.oop_by_default
   1.256 +//
   1.257 +//    Enable or disable OOP-by-default depending on the test's filename.  You
   1.258 +//    can still force OOP on or off with <iframe mozbrowser remote=true/false>,
   1.259 +//    at least until bug 756376 lands.
   1.260 +//
   1.261 +//  * dom.ipc.tabs.disabled: false
   1.262 +//
   1.263 +//    Allow us to create OOP frames.  Even if they're not the default, some
   1.264 +//    "in-process" tests create OOP frames.
   1.265 +//
   1.266 +//  * network.disable.ipc.security: true
   1.267 +//
   1.268 +//    Disable the networking security checks; our test harness just tests
   1.269 +//    browser elements without sticking them in apps, and the security checks
   1.270 +//    dislike that.
   1.271 +//
   1.272 +//    Unfortunately setting network.disable.ipc.security to false before the
   1.273 +//    child process(es) created by this test have shut down can cause us to
   1.274 +//    assert and kill the child process.  That doesn't cause the tests to fail,
   1.275 +//    but it's still scary looking.  So we just set the pref to true and never
   1.276 +//    pop that value.  We'll rely on the tests which test IPC security to set
   1.277 +//    it to false.
   1.278 +//
   1.279 +//  * security.mixed_content.block_active_content: false
   1.280 +//
   1.281 +//    Disable mixed active content blocking, so that tests can confirm that mixed
   1.282 +//    content results in a broken security state.
   1.283 +
   1.284 +(function() {
   1.285 +  var oop = location.pathname.indexOf('_inproc_') == -1;
   1.286 +
   1.287 +  browserElementTestHelpers.lockTestReady();
   1.288 +  SpecialPowers.setBoolPref("network.disable.ipc.security", true);
   1.289 +  SpecialPowers.pushPrefEnv({set: [["browser.pagethumbnails.capturing_disabled", true],
   1.290 +                                   ["dom.ipc.browser_frames.oop_by_default", oop],
   1.291 +                                   ["dom.ipc.tabs.disabled", false],
   1.292 +                                   ["security.mixed_content.block_active_content", false]]},
   1.293 +                            browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
   1.294 +})();
   1.295 +
   1.296 +addEventListener('unload', function() {
   1.297 +  browserElementTestHelpers.cleanUp();
   1.298 +});
   1.299 +
   1.300 +// Wait for the load event before unlocking the test-ready event.
   1.301 +browserElementTestHelpers.lockTestReady();
   1.302 +addEventListener('load', function() {
   1.303 +  SimpleTest.executeSoon(browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
   1.304 +});
   1.305 +
   1.306 +//////////////////////////////////
   1.307 +// promise.js from the addon SDK with some modifications to the module
   1.308 +// boilerplate.
   1.309 +//////////////////////////////////
   1.310 +
   1.311 +;(function(id, factory) { // Module boilerplate :(
   1.312 +    var globals = this;
   1.313 +    factory(function require(id) {
   1.314 +      return globals[id];
   1.315 +    }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
   1.316 +}).call(this, 'Promise', function Promise(require, exports, module) {
   1.317 +
   1.318 +'use strict';
   1.319 +
   1.320 +module.metadata = {
   1.321 +  "stability": "unstable"
   1.322 +};
   1.323 +
   1.324 +/**
   1.325 + * Internal utility: Wraps given `value` into simplified promise, successfully
   1.326 + * fulfilled to a given `value`. Note the result is not a complete promise
   1.327 + * implementation, as its method `then` does not returns anything.
   1.328 + */
   1.329 +function fulfilled(value) {
   1.330 +  return { then: function then(fulfill) { fulfill(value); } };
   1.331 +}
   1.332 +
   1.333 +/**
   1.334 + * Internal utility: Wraps given input into simplified promise, pre-rejected
   1.335 + * with a given `reason`. Note the result is not a complete promise
   1.336 + * implementation, as its method `then` does not returns anything.
   1.337 + */
   1.338 +function rejected(reason) {
   1.339 +  return { then: function then(fulfill, reject) { reject(reason); } };
   1.340 +}
   1.341 +
   1.342 +/**
   1.343 + * Internal utility: Returns `true` if given `value` is a promise. Value is
   1.344 + * assumed to be a promise if it implements method `then`.
   1.345 + */
   1.346 +function isPromise(value) {
   1.347 +  return value && typeof(value.then) === 'function';
   1.348 +}
   1.349 +
   1.350 +/**
   1.351 + * Creates deferred object containing fresh promise & methods to either resolve
   1.352 + * or reject it. The result is an object with the following properties:
   1.353 + * - `promise` Eventual value representation implementing CommonJS [Promises/A]
   1.354 + *   (http://wiki.commonjs.org/wiki/Promises/A) API.
   1.355 + * - `resolve` Single shot function that resolves enclosed `promise` with a
   1.356 + *   given `value`.
   1.357 + * - `reject` Single shot function that rejects enclosed `promise` with a given
   1.358 + *   `reason`.
   1.359 + *
   1.360 + * An optional `prototype` argument is used as a prototype of the returned
   1.361 + * `promise` allowing one to implement additional API. If prototype is not
   1.362 + * passed then it falls back to `Object.prototype`.
   1.363 + *
   1.364 + *  ## Example
   1.365 + *
   1.366 + *  function fetchURI(uri, type) {
   1.367 + *    var deferred = defer();
   1.368 + *    var request = new XMLHttpRequest();
   1.369 + *    request.open("GET", uri, true);
   1.370 + *    request.responseType = type;
   1.371 + *    request.onload = function onload() {
   1.372 + *      deferred.resolve(request.response);
   1.373 + *    }
   1.374 + *    request.onerror = function(event) {
   1.375 + *     deferred.reject(event);
   1.376 + *    }
   1.377 + *    request.send();
   1.378 + *
   1.379 + *    return deferred.promise;
   1.380 + *  }
   1.381 + */
   1.382 +function defer(prototype) {
   1.383 +  // Define FIFO queue of observer pairs. Once promise is resolved & all queued
   1.384 +  // observers are forwarded to `result` and variable is set to `null`.
   1.385 +  var observers = [];
   1.386 +
   1.387 +  // Promise `result`, which will be assigned a resolution value once promise
   1.388 +  // is resolved. Note that result will always be assigned promise (or alike)
   1.389 +  // object to take care of propagation through promise chains. If result is
   1.390 +  // `null` promise is not resolved yet.
   1.391 +  var result = null;
   1.392 +
   1.393 +  prototype = (prototype || prototype === null) ? prototype : Object.prototype;
   1.394 +
   1.395 +  // Create an object implementing promise API.
   1.396 +  var promise = Object.create(prototype, {
   1.397 +    then: { value: function then(onFulfill, onError) {
   1.398 +      var deferred = defer(prototype);
   1.399 +
   1.400 +      function resolve(value) {
   1.401 +        // If `onFulfill` handler is provided resolve `deferred.promise` with
   1.402 +        // result of invoking it with a resolution value. If handler is not
   1.403 +        // provided propagate value through.
   1.404 +        try {
   1.405 +          deferred.resolve(onFulfill ? onFulfill(value) : value);
   1.406 +        }
   1.407 +        // `onFulfill` may throw exception in which case resulting promise
   1.408 +        // is rejected with thrown exception.
   1.409 +        catch(error) {
   1.410 +          if (exports._reportErrors && typeof(console) === 'object')
   1.411 +            console.error(error);
   1.412 +          // Note: Following is equivalent of `deferred.reject(error)`,
   1.413 +          // we use this shortcut to reduce a stack.
   1.414 +          deferred.resolve(rejected(error));
   1.415 +        }
   1.416 +      }
   1.417 +
   1.418 +      function reject(reason) {
   1.419 +        try {
   1.420 +          if (onError) deferred.resolve(onError(reason));
   1.421 +          else deferred.resolve(rejected(reason));
   1.422 +        }
   1.423 +        catch(error) {
   1.424 +          if (exports._reportErrors && typeof(console) === 'object')
   1.425 +            console.error(error)
   1.426 +          deferred.resolve(rejected(error));
   1.427 +        }
   1.428 +      }
   1.429 +
   1.430 +      // If enclosed promise (`this.promise`) observers queue is still alive
   1.431 +      // enqueue a new observer pair into it. Note that this does not
   1.432 +      // necessary means that promise is pending, it may already be resolved,
   1.433 +      // but we still have to queue observers to guarantee an order of
   1.434 +      // propagation.
   1.435 +      if (observers) {
   1.436 +        observers.push({ resolve: resolve, reject: reject });
   1.437 +      }
   1.438 +      // Otherwise just forward observer pair right to a `result` promise.
   1.439 +      else {
   1.440 +        result.then(resolve, reject);
   1.441 +      }
   1.442 +
   1.443 +      return deferred.promise;
   1.444 +    }}
   1.445 +  })
   1.446 +
   1.447 +  var deferred = {
   1.448 +    promise: promise,
   1.449 +    /**
   1.450 +     * Resolves associated `promise` to a given `value`, unless it's already
   1.451 +     * resolved or rejected. Note that resolved promise is not necessary a
   1.452 +     * successfully fulfilled. Promise may be resolved with a promise `value`
   1.453 +     * in which case `value` promise's fulfillment / rejection will propagate
   1.454 +     * up to a promise resolved with `value`.
   1.455 +     */
   1.456 +    resolve: function resolve(value) {
   1.457 +      if (!result) {
   1.458 +        // Store resolution `value` in a `result` as a promise, so that all
   1.459 +        // the subsequent handlers can be simply forwarded to it. Since
   1.460 +        // `result` will be a promise all the value / error propagation will
   1.461 +        // be uniformly taken care of.
   1.462 +        result = isPromise(value) ? value : fulfilled(value);
   1.463 +
   1.464 +        // Forward already registered observers to a `result` promise in the
   1.465 +        // order they were registered. Note that we intentionally dequeue
   1.466 +        // observer at a time until queue is exhausted. This makes sure that
   1.467 +        // handlers registered as side effect of observer forwarding are
   1.468 +        // queued instead of being invoked immediately, guaranteeing FIFO
   1.469 +        // order.
   1.470 +        while (observers.length) {
   1.471 +          var observer = observers.shift();
   1.472 +          result.then(observer.resolve, observer.reject);
   1.473 +        }
   1.474 +
   1.475 +        // Once `observers` queue is exhausted we `null`-ify it, so that
   1.476 +        // new handlers are forwarded straight to the `result`.
   1.477 +        observers = null;
   1.478 +      }
   1.479 +    },
   1.480 +    /**
   1.481 +     * Rejects associated `promise` with a given `reason`, unless it's already
   1.482 +     * resolved / rejected. This is just a (better performing) convenience
   1.483 +     * shortcut for `deferred.resolve(reject(reason))`.
   1.484 +     */
   1.485 +    reject: function reject(reason) {
   1.486 +      // Note that if promise is resolved that does not necessary means that it
   1.487 +      // is successfully fulfilled. Resolution value may be a promise in which
   1.488 +      // case its result propagates. In other words if promise `a` is resolved
   1.489 +      // with promise `b`, `a` is either fulfilled or rejected depending
   1.490 +      // on weather `b` is fulfilled or rejected. Here `deferred.promise` is
   1.491 +      // resolved with a promise pre-rejected with a given `reason`, there for
   1.492 +      // `deferred.promise` is rejected with a given `reason`. This may feel
   1.493 +      // little awkward first, but doing it this way greatly simplifies
   1.494 +      // propagation through promise chains.
   1.495 +      deferred.resolve(rejected(reason));
   1.496 +    }
   1.497 +  };
   1.498 +
   1.499 +  return deferred;
   1.500 +}
   1.501 +exports.defer = defer;
   1.502 +
   1.503 +/**
   1.504 + * Returns a promise resolved to a given `value`. Optionally a second
   1.505 + * `prototype` argument may be provided to be used as a prototype for the
   1.506 + * returned promise.
   1.507 + */
   1.508 +function resolve(value, prototype) {
   1.509 +  var deferred = defer(prototype);
   1.510 +  deferred.resolve(value);
   1.511 +  return deferred.promise;
   1.512 +}
   1.513 +exports.resolve = resolve;
   1.514 +
   1.515 +/**
   1.516 + * Returns a promise rejected with a given `reason`. Optionally a second
   1.517 + * `prototype` argument may be provided to be used as a prototype for the
   1.518 + * returned promise.
   1.519 + */
   1.520 +function reject(reason, prototype) {
   1.521 +  var deferred = defer(prototype);
   1.522 +  deferred.reject(reason);
   1.523 +  return deferred.promise;
   1.524 +}
   1.525 +exports.reject = reject;
   1.526 +
   1.527 +var promised = (function() {
   1.528 +  // Note: Define shortcuts and utility functions here in order to avoid
   1.529 +  // slower property accesses and unnecessary closure creations on each
   1.530 +  // call of this popular function.
   1.531 +
   1.532 +  var call = Function.call;
   1.533 +  var concat = Array.prototype.concat;
   1.534 +
   1.535 +  // Utility function that does following:
   1.536 +  // execute([ f, self, args...]) => f.apply(self, args)
   1.537 +  function execute(args) { return call.apply(call, args) }
   1.538 +
   1.539 +  // Utility function that takes promise of `a` array and maybe promise `b`
   1.540 +  // as arguments and returns promise for `a.concat(b)`.
   1.541 +  function promisedConcat(promises, unknown) {
   1.542 +    return promises.then(function(values) {
   1.543 +      return resolve(unknown).then(function(value) {
   1.544 +        return values.concat([ value ])
   1.545 +      });
   1.546 +    });
   1.547 +  }
   1.548 +
   1.549 +  return function promised(f, prototype) {
   1.550 +    /**
   1.551 +    Returns a wrapped `f`, which when called returns a promise that resolves to
   1.552 +    `f(...)` passing all the given arguments to it, which by the way may be
   1.553 +    promises. Optionally second `prototype` argument may be provided to be used
   1.554 +    a prototype for a returned promise.
   1.555 +
   1.556 +    ## Example
   1.557 +
   1.558 +    var promise = promised(Array)(1, promise(2), promise(3))
   1.559 +    promise.then(console.log) // => [ 1, 2, 3 ]
   1.560 +    **/
   1.561 +
   1.562 +    return function promised() {
   1.563 +      // create array of [ f, this, args... ]
   1.564 +      return concat.apply([ f, this ], arguments).
   1.565 +        // reduce it via `promisedConcat` to get promised array of fulfillments
   1.566 +        reduce(promisedConcat, resolve([], prototype)).
   1.567 +        // finally map that to promise of `f.apply(this, args...)`
   1.568 +        then(execute);
   1.569 +    }
   1.570 +  }
   1.571 +})();
   1.572 +exports.promised = promised;
   1.573 +
   1.574 +var all = promised(Array);
   1.575 +exports.all = all;
   1.576 +
   1.577 +});

mercurial