tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PrefixedPostMessage.js (3882B)


      1 /**
      2 * Supports pseudo-"namespacing" for window-posted messages for a given test
      3 * by generating and using a unique prefix that gets wrapped into message
      4 * objects. This makes it more feasible to have multiple tests that use
      5 * `window.postMessage` in a single test file. Basically, make it possible
      6 * for the each test to listen for only the messages that are pertinent to it.
      7 *
      8 * 'Prefix' not an elegant term to use here but this models itself after
      9 * PrefixedLocalStorage.
     10 *
     11 * PrefixedMessageTest: Instantiate in testharness.js tests to generate
     12 *   a new unique-ish prefix that can be used by other test support files
     13 * PrefixedMessageResource: Instantiate in supporting test resource
     14 *   files to use/share a prefix generated by a test.
     15 */
     16 var PrefixedMessage = function () {
     17  this.prefix = '';
     18  this.param = 'prefixedMessage'; // Param to use in querystrings
     19 };
     20 
     21 /**
     22 * Generate a URL that adds/replaces param with this object's prefix
     23 * Use to link to test support files that make use of
     24 * PrefixedMessageResource.
     25 */
     26 PrefixedMessage.prototype.url = function (uri) {
     27  function updateUrlParameter (uri, key, value) {
     28    var i         = uri.indexOf('#');
     29    var hash      = (i === -1) ? '' : uri.substr(i);
     30    uri           = (i === -1) ? uri : uri.substr(0, i);
     31    var re        = new RegExp(`([?&])${key}=.*?(&|$)`, 'i');
     32    var separator = uri.indexOf('?') !== -1 ? '&' : '?';
     33    uri = (uri.match(re)) ? uri.replace(re, `$1${key}=${value}$2`) :
     34      `${uri}${separator}${key}=${value}`;
     35    return uri + hash;
     36  }
     37  return updateUrlParameter(uri, this.param, this.prefix);
     38 };
     39 
     40 /**
     41 * Add an eventListener on `message` but only invoke the given callback
     42 * for messages whose object contains this object's prefix. Remove the
     43 * event listener once the anticipated message has been received.
     44 */
     45 PrefixedMessage.prototype.onMessage = function (fn) {
     46  window.addEventListener('message', e => {
     47    if (typeof e.data === 'object' && e.data.hasOwnProperty('prefix')) {
     48      if (e.data.prefix === this.prefix) {
     49        // Only invoke callback when `data` is an object containing
     50        // a `prefix` key with this object's prefix value
     51        // Note fn is invoked with "unwrapped" data first, then the event `e`
     52        // (which contains the full, wrapped e.data should it be needed)
     53        fn.call(this, e.data.data, e);
     54        window.removeEventListener('message', fn);
     55      }
     56    }
     57  });
     58 };
     59 
     60 /**
     61 * Instantiate in a test file (e.g. during `setup`) to create a unique-ish
     62 * prefix that can be shared by support files
     63 */
     64 var PrefixedMessageTest = function () {
     65  PrefixedMessage.call(this);
     66  this.prefix = `${document.location.pathname}-${Math.random()}-${Date.now()}-`;
     67 };
     68 PrefixedMessageTest.prototype = Object.create(PrefixedMessage.prototype);
     69 PrefixedMessageTest.prototype.constructor = PrefixedMessageTest;
     70 
     71 /**
     72 * Instantiate in a test support script to use a "prefix" generated by a
     73 * PrefixedMessageTest in a controlling test file. It will look for
     74 * the prefix in a URL param (see also PrefixedMessage#url)
     75 */
     76 var PrefixedMessageResource = function () {
     77  PrefixedMessage.call(this);
     78  // Check URL querystring for prefix to use
     79  var regex = new RegExp(`[?&]${this.param}(=([^&#]*)|&|#|$)`),
     80    results = regex.exec(document.location.href);
     81  if (results && results[2]) {
     82    this.prefix = results[2];
     83  }
     84 };
     85 PrefixedMessageResource.prototype = Object.create(PrefixedMessage.prototype);
     86 PrefixedMessageResource.prototype.constructor = PrefixedMessageResource;
     87 
     88 /**
     89 * This is how a test resource document can "send info" to its
     90 * opener context. It will whatever message is being sent (`data`) in
     91 * an object that injects the prefix.
     92 */
     93 PrefixedMessageResource.prototype.postToOpener = function (data) {
     94  if (window.opener) {
     95    window.opener.postMessage({
     96      prefix: this.prefix,
     97      data: data
     98    }, '*');
     99  }
    100 };