Re-sync html/browsers/the-window-object web-platform-tests from upstream
[WebKit-https.git] / LayoutTests / imported / w3c / web-platform-tests / common / PrefixedPostMessage.js
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 };