8

I am stuck in a problem where i have to redirect from a popup window to a different domain and post a message to it. Here is the scenario :-

  1. User opens a new popup window which stays in same domain.(ex: http://doamin_one.com)
  2. User fills the form in the popup window and clicks submit. This should redirect the window to http://doamin_two.com and domain_two.com should receive form data via post message.

I am able to receive messages if i open fresh popup and do post message but not in the case of redirection. Here is my code:

http://domain_one.com -

   function redirect(formData,popup) {
     //popup=window ref object
     popup.location.href="http://domain_two.com"
     popup.postMessage(JSON.stringify(formData),
          "http://domain_two.com");
}

http://domain_two.com -

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
 if (event.origin !== "http://domain_one.com")
      return;
 let data=event.data;
 //....
}
JSGeek
  • 93
  • 6

2 Answers2

0

You can pass the form data to "http://domain_two.com" using query string parameters then parse the query string of location.href at load event of the second domain

popup.location.href = `http://domain_two.com?${new URLSearchParams(formData.entries()).toString()}`
guest271314
  • 1
  • 15
  • 104
  • 177
  • Form Data can get pretty big and data transfer has to be secure. Is there any other way? – JSGeek Feb 04 '19 at 03:18
  • @JSGeek Can you include the code at `"http://domain_two.com"` at the question? `postMessage()` is called immediately, before the `window` has loaded. The request is also cross-domain. – guest271314 Feb 04 '19 at 03:27
  • I have updated my question with code. Using setTimeout for post message dint help either. – JSGeek Feb 04 '19 at 03:39
  • How do you know when `"http://domain_two.com"` is loaded? `popup.postMessage(JSON.stringify(formData), "http://domain_two.com")` is called immediately after `popup.location.href="http://domain_two.com"` at the code at the question. – guest271314 Feb 04 '19 at 03:41
  • What do u recommend – JSGeek Feb 04 '19 at 03:44
  • @JSGeek You could try using `Storage`, `history`, `MessageChannel` and `iframe` elements or a `SharedWorker` , `ServiceWorker`. Or communicating with `opener` by substituting `window.open()` for setting `popup` `.href`. Essentially the code at the question attempts to `postMessage()` to itself. The simplest approach would be to parse the query string at `load` event of new `window`. – guest271314 Feb 04 '19 at 03:50
  • @JSGeek See https://stackoverflow.com/questions/37127985/, https://stackoverflow.com/questions/46461819/, https://stackoverflow.com/questions/38810002/, https://stackoverflow.com/q/46337968/, https://stackoverflow.com/questions/33645685/ – guest271314 Feb 04 '19 at 04:03
0

One issue with the code at the question is that .postMessage() is called immediately following setting a new .location.href at popup without waiting for the load event of window at

 popup.location.href="http://domain_two.com"
 popup.postMessage(JSON.stringify(formData),
      "http://domain_two.com");

To achieve the expected result you can control the process from the original window ('index.html'). Each window.name is set to a unique value. When the <form> is submitted at "http://domain_one.com" the resulting FormData can be converted to an ArrayBuffer and transferred to index.html, then popup (a.html) location.href is set to "http://domain_two.com". At load event of b.html .postMessage() the name of the window to index.html. Then the FormData is passed to .postMessage() where b.html gets the FormData that was originally submitted at b.html.

(The origin checks might need to be adjusted at the below code. The code was tested at plnkr, where simulation of cross-domain messaging is not 1:1, though should provide a pattern for how to accomplish the requirement).

index.html (opener)

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <script>
    // open `popup`
    const popup = window.open('a.html'); 
    // create random values to assign to `name` at `a.html` and `b.html`
    const [firstName, lastName] = [...Array(2)].map(_ => new Uint32Array(16));
    [firstName, lastName].forEach(buffer => window.crypto.getRandomValues(buffer));
    const [decoder, encoder] = [new TextDecoder(), new TextEncoder()];
    const [first, last] = [firstName, lastName].map(buffer => decoder.decode(buffer));
    // set `name` of `popup` (`a.html`) to `first`
    popup.name = first;
    let data;
    window.addEventListener("message", e => {
      // check `name` of `source` 
      if (e.source.name === first) {
        console.log(e.source.name);    
        // store `formData`        
        data = decoder.decode(e.data);
        console.log(e, JSON.parse(decoder.decode(e.data)));
        // set `name` of `popup` to `last`
        popup.name = last;
        // redirect `popup` to `b.html`
        popup.location.href = "b.html";
      }
      // check if `name` of `source` (`b.html`) is `last` 
      if (e.source.name === last) {
        // encode the stored `formData`
        let curr = encoder.encode(data);
        console.log(e.source.name, e.data);
        // transfer `formData` to `b.html`
        e.source.postMessage(curr.buffer, e.source.location.href, [curr.buffer]);
        // data = void 0;
      }
    })
  </script>
</body>
</html>

a.html (popup, "http://domain_one.com")

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  a
  <form>
    <input name="input">
    <input type="submit">
  </form>
  <script>
    document.forms[0].onsubmit = e => {
      // prevent default `form` submission
      e.preventDefault();
      // pass `form` to `FormData` 
      const formData = new FormData(e.target);
      // encode `formData` as a `Uint8Array`
      const encoded = new TextEncoder().encode(JSON.stringify([...formData.entries()]));
      console.log(encoded);
      // transfer `encoded` to `opener` (`index.html`)
      opener.postMessage(encoded.buffer, opener.location.href, [encoded.buffer]);
    }
  </script>
</body>
</html>

b.html (popup, "http://domain_two.com")

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  b
  <script>
    const decoder = new TextDecoder();
    let data;
    window.addEventListener("message", receiveMessage, false);

    function receiveMessage(event) {
      // check `origin` of `event`
      if (event.origin !== opener.location.origin)
        return;
      console.log(event);
      // process `formData` from `popup`
      data = JSON.parse(decoder.decode(event.data));
      // do stuff with `formData`
      p.textContent = JSON.stringify(data, null, 2);
    }
    // wait for `load` event to be dispatched before posting message to `opener`
    onload = () => {
      opener.postMessage("ok", opener.location.href);
    }
  </script>
  <pre id="p"></pre>
</body>
</html>

plnkr

guest271314
  • 1
  • 15
  • 104
  • 177
  • Pretty sure this way won't work cross-domain because once you hit the 'opener' calls, you will get cross-origin frame exceptions. – cakidnyc Feb 27 '20 at 17:27