dom/browser-element/mochitest/browserElementTestHelpers.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* Any copyright is dedicated to the public domain.
     2    http://creativecommons.org/publicdomain/zero/1.0/ */
     4 // Helpers for managing the browser frame preferences.
     5 "use strict";
     7 function _getPath() {
     8   return window.location.pathname
     9                .substring(0, window.location.pathname.lastIndexOf('/'))
    10                .replace("/priority", "");
    11 }
    13 const browserElementTestHelpers = {
    14   _getBoolPref: function(pref) {
    15     try {
    16       return SpecialPowers.getBoolPref(pref);
    17     }
    18     catch (e) {
    19       return undefined;
    20     }
    21   },
    23   _setPref: function(pref, value) {
    24     this.lockTestReady();
    25     if (value !== undefined && value !== null) {
    26       SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, this.unlockTestReady.bind(this));
    27     } else {
    28       SpecialPowers.pushPrefEnv({'clear': [[pref]]}, this.unlockTestReady.bind(this));
    29     }
    30   },
    32   _setPrefs: function() {
    33     this.lockTestReady();
    34     SpecialPowers.pushPrefEnv({'set': Array.slice(arguments)}, this.unlockTestReady.bind(this));
    35   },
    37   _testReadyLockCount: 0,
    38   _firedTestReady: false,
    39   lockTestReady: function() {
    40     this._testReadyLockCount++;
    41   },
    43   unlockTestReady: function() {
    44     this._testReadyLockCount--;
    45     if (this._testReadyLockCount == 0 && !this._firedTestReady) {
    46       this._firedTestReady = true;
    47       dispatchEvent(new Event("testready"));
    48     }
    49   },
    51   enableProcessPriorityManager: function() {
    52     this._setPrefs(
    53       ['dom.ipc.processPriorityManager.testMode', true],
    54       ['dom.ipc.processPriorityManager.enabled', true],
    55       ['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
    56     );
    57   },
    59   setEnabledPref: function(value) {
    60     this._setPref('dom.mozBrowserFramesEnabled', value);
    61   },
    63   getOOPByDefaultPref: function() {
    64     return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
    65   },
    67   addPermission: function() {
    68     SpecialPowers.addPermission("browser", true, document);
    69     this.tempPermissions.push(location.href)
    70   },
    72   'tempPermissions': [],
    73   addPermissionForUrl: function(url) {
    74     SpecialPowers.addPermission("browser", true, url);
    75     this.tempPermissions.push(url);
    76   },
    78   _observers: [],
    80   // This function is a wrapper which lets you register an observer to one of
    81   // the process priority manager's test-only topics.  observerFn should be a
    82   // function which takes (subject, topic, data).
    83   //
    84   // We'll clean up any observers you add at the end of the test.
    85   addProcessPriorityObserver: function(processPriorityTopic, observerFn) {
    86     var topic = "process-priority-manager:TEST-ONLY:" + processPriorityTopic;
    88     // SpecialPowers appears to require that the observer be an object, not a
    89     // function.
    90     var observer = {
    91       observe: observerFn
    92     };
    94     SpecialPowers.addObserver(observer, topic, /* weak = */ false);
    95     this._observers.push([observer, topic]);
    96   },
    98   cleanUp: function() {
    99     for (var i = 0; i < this.tempPermissions.length; i++) {
   100       SpecialPowers.removePermission("browser", this.tempPermissions[i]);
   101     }
   103     for (var i = 0; i < this._observers.length; i++) {
   104       SpecialPowers.removeObserver(this._observers[i][0],
   105                                    this._observers[i][1]);
   106     }
   107   },
   109   // Some basically-empty pages from different domains you can load.
   110   'emptyPage1': 'http://example.com' + _getPath() + '/file_empty.html',
   111   'emptyPage2': 'http://example.org' + _getPath() + '/file_empty.html',
   112   'emptyPage3': 'http://test1.example.org' + _getPath() + '/file_empty.html',
   113   'focusPage': 'http://example.org' + _getPath() + '/file_focus.html',
   114 };
   116 // Returns a promise which is resolved when a subprocess is created.  The
   117 // argument to resolve() is the childID of the subprocess.
   118 function expectProcessCreated() {
   119   var deferred = Promise.defer();
   121   var observed = false;
   122   browserElementTestHelpers.addProcessPriorityObserver(
   123     "process-created",
   124     function(subject, topic, data) {
   125       // Don't run this observer twice, so we don't ok(true) twice.  (It's fine
   126       // to resolve a promise twice; the second resolve() call does nothing.)
   127       if (observed) {
   128         return;
   129       }
   130       observed = true;
   132       var childID = parseInt(data);
   133       ok(true, 'Got new process, id=' + childID);
   134       deferred.resolve(childID);
   135     }
   136   );
   138   return deferred.promise;
   139 }
   141 // Just like expectProcessCreated(), except we'll call ok(false) if a second
   142 // process is created.
   143 function expectOnlyOneProcessCreated() {
   144   var p = expectProcessCreated();
   145   p.then(function() {
   146     expectProcessCreated().then(function(childID) {
   147       ok(false, 'Got unexpected process creation, childID=' + childID);
   148     });
   149   });
   150   return p;
   151 }
   153 // Returns a promise which is resolved or rejected the next time the process
   154 // childID changes its priority.  We resolve if the (priority, CPU priority)
   155 // tuple matches (expectedPriority, expectedCPUPriority) and we reject
   156 // otherwise.
   157 //
   158 // expectedCPUPriority is an optional argument; if it's not specified, we
   159 // resolve if priority matches expectedPriority.
   161 function expectPriorityChange(childID, expectedPriority,
   162                               /* optional */ expectedCPUPriority) {
   163   var deferred = Promise.defer();
   165   var observed = false;
   166   browserElementTestHelpers.addProcessPriorityObserver(
   167     'process-priority-set',
   168     function(subject, topic, data) {
   169       if (observed) {
   170         return;
   171       }
   173       var [id, priority, cpuPriority] = data.split(":");
   174       if (id != childID) {
   175         return;
   176       }
   178       // Make sure we run the is() calls in this observer only once, otherwise
   179       // we'll expect /every/ priority change to match expectedPriority.
   180       observed = true;
   182       is(priority, expectedPriority,
   183          'Expected priority of childID ' + childID +
   184          ' to change to ' + expectedPriority);
   186       if (expectedCPUPriority) {
   187         is(cpuPriority, expectedCPUPriority,
   188            'Expected CPU priority of childID ' + childID +
   189            ' to change to ' + expectedCPUPriority);
   190       }
   192       if (priority == expectedPriority &&
   193           (!expectedCPUPriority || expectedCPUPriority == cpuPriority)) {
   194         deferred.resolve();
   195       } else {
   196         deferred.reject();
   197       }
   198     }
   199   );
   201   return deferred.promise;
   202 }
   204 // Returns a promise which is resolved or rejected the next time the background
   205 // process childID changes its priority.  We resolve if the backgroundLRU
   206 // matches expectedBackgroundLRU and we reject otherwise.
   208 function expectPriorityWithBackgroundLRUSet(childID, expectedBackgroundLRU) {
   209   var deferred = Promise.defer();
   211   browserElementTestHelpers.addProcessPriorityObserver(
   212     'process-priority-with-background-LRU-set',
   213     function(subject, topic, data) {
   215       var [id, priority, cpuPriority, backgroundLRU] = data.split(":");
   216       if (id != childID) {
   217         return;
   218       }
   220       is(backgroundLRU, expectedBackgroundLRU,
   221          'Expected backgroundLRU ' + backgroundLRU + ' of childID ' + childID +
   222          ' to change to ' + expectedBackgroundLRU);
   224       if (backgroundLRU == expectedBackgroundLRU) {
   225         deferred.resolve();
   226       } else {
   227         deferred.reject();
   228       }
   229     }
   230   );
   232   return deferred.promise;
   233 }
   235 // Returns a promise which is resolved the first time the given iframe fires
   236 // the mozbrowser##eventName event.
   237 function expectMozbrowserEvent(iframe, eventName) {
   238   var deferred = Promise.defer();
   239   iframe.addEventListener('mozbrowser' + eventName, function handler(e) {
   240     iframe.removeEventListener('mozbrowser' + eventName, handler);
   241     deferred.resolve(e);
   242   });
   243   return deferred.promise;
   244 }
   246 // Set some prefs:
   247 //
   248 //  * browser.pagethumbnails.capturing_disabled: true
   249 //
   250 //    Disable tab view; it seriously messes us up.
   251 //
   252 //  * dom.ipc.browser_frames.oop_by_default
   253 //
   254 //    Enable or disable OOP-by-default depending on the test's filename.  You
   255 //    can still force OOP on or off with <iframe mozbrowser remote=true/false>,
   256 //    at least until bug 756376 lands.
   257 //
   258 //  * dom.ipc.tabs.disabled: false
   259 //
   260 //    Allow us to create OOP frames.  Even if they're not the default, some
   261 //    "in-process" tests create OOP frames.
   262 //
   263 //  * network.disable.ipc.security: true
   264 //
   265 //    Disable the networking security checks; our test harness just tests
   266 //    browser elements without sticking them in apps, and the security checks
   267 //    dislike that.
   268 //
   269 //    Unfortunately setting network.disable.ipc.security to false before the
   270 //    child process(es) created by this test have shut down can cause us to
   271 //    assert and kill the child process.  That doesn't cause the tests to fail,
   272 //    but it's still scary looking.  So we just set the pref to true and never
   273 //    pop that value.  We'll rely on the tests which test IPC security to set
   274 //    it to false.
   275 //
   276 //  * security.mixed_content.block_active_content: false
   277 //
   278 //    Disable mixed active content blocking, so that tests can confirm that mixed
   279 //    content results in a broken security state.
   281 (function() {
   282   var oop = location.pathname.indexOf('_inproc_') == -1;
   284   browserElementTestHelpers.lockTestReady();
   285   SpecialPowers.setBoolPref("network.disable.ipc.security", true);
   286   SpecialPowers.pushPrefEnv({set: [["browser.pagethumbnails.capturing_disabled", true],
   287                                    ["dom.ipc.browser_frames.oop_by_default", oop],
   288                                    ["dom.ipc.tabs.disabled", false],
   289                                    ["security.mixed_content.block_active_content", false]]},
   290                             browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
   291 })();
   293 addEventListener('unload', function() {
   294   browserElementTestHelpers.cleanUp();
   295 });
   297 // Wait for the load event before unlocking the test-ready event.
   298 browserElementTestHelpers.lockTestReady();
   299 addEventListener('load', function() {
   300   SimpleTest.executeSoon(browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
   301 });
   303 //////////////////////////////////
   304 // promise.js from the addon SDK with some modifications to the module
   305 // boilerplate.
   306 //////////////////////////////////
   308 ;(function(id, factory) { // Module boilerplate :(
   309     var globals = this;
   310     factory(function require(id) {
   311       return globals[id];
   312     }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
   313 }).call(this, 'Promise', function Promise(require, exports, module) {
   315 'use strict';
   317 module.metadata = {
   318   "stability": "unstable"
   319 };
   321 /**
   322  * Internal utility: Wraps given `value` into simplified promise, successfully
   323  * fulfilled to a given `value`. Note the result is not a complete promise
   324  * implementation, as its method `then` does not returns anything.
   325  */
   326 function fulfilled(value) {
   327   return { then: function then(fulfill) { fulfill(value); } };
   328 }
   330 /**
   331  * Internal utility: Wraps given input into simplified promise, pre-rejected
   332  * with a given `reason`. Note the result is not a complete promise
   333  * implementation, as its method `then` does not returns anything.
   334  */
   335 function rejected(reason) {
   336   return { then: function then(fulfill, reject) { reject(reason); } };
   337 }
   339 /**
   340  * Internal utility: Returns `true` if given `value` is a promise. Value is
   341  * assumed to be a promise if it implements method `then`.
   342  */
   343 function isPromise(value) {
   344   return value && typeof(value.then) === 'function';
   345 }
   347 /**
   348  * Creates deferred object containing fresh promise & methods to either resolve
   349  * or reject it. The result is an object with the following properties:
   350  * - `promise` Eventual value representation implementing CommonJS [Promises/A]
   351  *   (http://wiki.commonjs.org/wiki/Promises/A) API.
   352  * - `resolve` Single shot function that resolves enclosed `promise` with a
   353  *   given `value`.
   354  * - `reject` Single shot function that rejects enclosed `promise` with a given
   355  *   `reason`.
   356  *
   357  * An optional `prototype` argument is used as a prototype of the returned
   358  * `promise` allowing one to implement additional API. If prototype is not
   359  * passed then it falls back to `Object.prototype`.
   360  *
   361  *  ## Example
   362  *
   363  *  function fetchURI(uri, type) {
   364  *    var deferred = defer();
   365  *    var request = new XMLHttpRequest();
   366  *    request.open("GET", uri, true);
   367  *    request.responseType = type;
   368  *    request.onload = function onload() {
   369  *      deferred.resolve(request.response);
   370  *    }
   371  *    request.onerror = function(event) {
   372  *     deferred.reject(event);
   373  *    }
   374  *    request.send();
   375  *
   376  *    return deferred.promise;
   377  *  }
   378  */
   379 function defer(prototype) {
   380   // Define FIFO queue of observer pairs. Once promise is resolved & all queued
   381   // observers are forwarded to `result` and variable is set to `null`.
   382   var observers = [];
   384   // Promise `result`, which will be assigned a resolution value once promise
   385   // is resolved. Note that result will always be assigned promise (or alike)
   386   // object to take care of propagation through promise chains. If result is
   387   // `null` promise is not resolved yet.
   388   var result = null;
   390   prototype = (prototype || prototype === null) ? prototype : Object.prototype;
   392   // Create an object implementing promise API.
   393   var promise = Object.create(prototype, {
   394     then: { value: function then(onFulfill, onError) {
   395       var deferred = defer(prototype);
   397       function resolve(value) {
   398         // If `onFulfill` handler is provided resolve `deferred.promise` with
   399         // result of invoking it with a resolution value. If handler is not
   400         // provided propagate value through.
   401         try {
   402           deferred.resolve(onFulfill ? onFulfill(value) : value);
   403         }
   404         // `onFulfill` may throw exception in which case resulting promise
   405         // is rejected with thrown exception.
   406         catch(error) {
   407           if (exports._reportErrors && typeof(console) === 'object')
   408             console.error(error);
   409           // Note: Following is equivalent of `deferred.reject(error)`,
   410           // we use this shortcut to reduce a stack.
   411           deferred.resolve(rejected(error));
   412         }
   413       }
   415       function reject(reason) {
   416         try {
   417           if (onError) deferred.resolve(onError(reason));
   418           else deferred.resolve(rejected(reason));
   419         }
   420         catch(error) {
   421           if (exports._reportErrors && typeof(console) === 'object')
   422             console.error(error)
   423           deferred.resolve(rejected(error));
   424         }
   425       }
   427       // If enclosed promise (`this.promise`) observers queue is still alive
   428       // enqueue a new observer pair into it. Note that this does not
   429       // necessary means that promise is pending, it may already be resolved,
   430       // but we still have to queue observers to guarantee an order of
   431       // propagation.
   432       if (observers) {
   433         observers.push({ resolve: resolve, reject: reject });
   434       }
   435       // Otherwise just forward observer pair right to a `result` promise.
   436       else {
   437         result.then(resolve, reject);
   438       }
   440       return deferred.promise;
   441     }}
   442   })
   444   var deferred = {
   445     promise: promise,
   446     /**
   447      * Resolves associated `promise` to a given `value`, unless it's already
   448      * resolved or rejected. Note that resolved promise is not necessary a
   449      * successfully fulfilled. Promise may be resolved with a promise `value`
   450      * in which case `value` promise's fulfillment / rejection will propagate
   451      * up to a promise resolved with `value`.
   452      */
   453     resolve: function resolve(value) {
   454       if (!result) {
   455         // Store resolution `value` in a `result` as a promise, so that all
   456         // the subsequent handlers can be simply forwarded to it. Since
   457         // `result` will be a promise all the value / error propagation will
   458         // be uniformly taken care of.
   459         result = isPromise(value) ? value : fulfilled(value);
   461         // Forward already registered observers to a `result` promise in the
   462         // order they were registered. Note that we intentionally dequeue
   463         // observer at a time until queue is exhausted. This makes sure that
   464         // handlers registered as side effect of observer forwarding are
   465         // queued instead of being invoked immediately, guaranteeing FIFO
   466         // order.
   467         while (observers.length) {
   468           var observer = observers.shift();
   469           result.then(observer.resolve, observer.reject);
   470         }
   472         // Once `observers` queue is exhausted we `null`-ify it, so that
   473         // new handlers are forwarded straight to the `result`.
   474         observers = null;
   475       }
   476     },
   477     /**
   478      * Rejects associated `promise` with a given `reason`, unless it's already
   479      * resolved / rejected. This is just a (better performing) convenience
   480      * shortcut for `deferred.resolve(reject(reason))`.
   481      */
   482     reject: function reject(reason) {
   483       // Note that if promise is resolved that does not necessary means that it
   484       // is successfully fulfilled. Resolution value may be a promise in which
   485       // case its result propagates. In other words if promise `a` is resolved
   486       // with promise `b`, `a` is either fulfilled or rejected depending
   487       // on weather `b` is fulfilled or rejected. Here `deferred.promise` is
   488       // resolved with a promise pre-rejected with a given `reason`, there for
   489       // `deferred.promise` is rejected with a given `reason`. This may feel
   490       // little awkward first, but doing it this way greatly simplifies
   491       // propagation through promise chains.
   492       deferred.resolve(rejected(reason));
   493     }
   494   };
   496   return deferred;
   497 }
   498 exports.defer = defer;
   500 /**
   501  * Returns a promise resolved to a given `value`. Optionally a second
   502  * `prototype` argument may be provided to be used as a prototype for the
   503  * returned promise.
   504  */
   505 function resolve(value, prototype) {
   506   var deferred = defer(prototype);
   507   deferred.resolve(value);
   508   return deferred.promise;
   509 }
   510 exports.resolve = resolve;
   512 /**
   513  * Returns a promise rejected with a given `reason`. Optionally a second
   514  * `prototype` argument may be provided to be used as a prototype for the
   515  * returned promise.
   516  */
   517 function reject(reason, prototype) {
   518   var deferred = defer(prototype);
   519   deferred.reject(reason);
   520   return deferred.promise;
   521 }
   522 exports.reject = reject;
   524 var promised = (function() {
   525   // Note: Define shortcuts and utility functions here in order to avoid
   526   // slower property accesses and unnecessary closure creations on each
   527   // call of this popular function.
   529   var call = Function.call;
   530   var concat = Array.prototype.concat;
   532   // Utility function that does following:
   533   // execute([ f, self, args...]) => f.apply(self, args)
   534   function execute(args) { return call.apply(call, args) }
   536   // Utility function that takes promise of `a` array and maybe promise `b`
   537   // as arguments and returns promise for `a.concat(b)`.
   538   function promisedConcat(promises, unknown) {
   539     return promises.then(function(values) {
   540       return resolve(unknown).then(function(value) {
   541         return values.concat([ value ])
   542       });
   543     });
   544   }
   546   return function promised(f, prototype) {
   547     /**
   548     Returns a wrapped `f`, which when called returns a promise that resolves to
   549     `f(...)` passing all the given arguments to it, which by the way may be
   550     promises. Optionally second `prototype` argument may be provided to be used
   551     a prototype for a returned promise.
   553     ## Example
   555     var promise = promised(Array)(1, promise(2), promise(3))
   556     promise.then(console.log) // => [ 1, 2, 3 ]
   557     **/
   559     return function promised() {
   560       // create array of [ f, this, args... ]
   561       return concat.apply([ f, this ], arguments).
   562         // reduce it via `promisedConcat` to get promised array of fulfillments
   563         reduce(promisedConcat, resolve([], prototype)).
   564         // finally map that to promise of `f.apply(this, args...)`
   565         then(execute);
   566     }
   567   }
   568 })();
   569 exports.promised = promised;
   571 var all = promised(Array);
   572 exports.all = all;
   574 });

mercurial