JSGeek
JSGeek

Reputation: 93

Redirect a Popup and Post Message

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;
 //....
}

Upvotes: 8

Views: 2807

Answers (2)

guest271314
guest271314

Reputation: 1

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

Upvotes: 0

guest271314
guest271314

Reputation: 1

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()}`

Upvotes: 0

Related Questions