0

Background

I'm trying to write a greasymonkey script that allow site-A (Google for example in this discussion) to get and process result from site-B (yandex for example in this discussion)

The final form looks like this (hopefully):

// script on google.com
async function test1(){
    let targetURL = "https://yandex.com/";
    let pWin = window.open(targetURL);
    let result = await GetAnswer(pWin, "what is A");
    alert(result) // "A is not B"
  
    function GetAnswer(win, question){
      // get search result by postMessage
    }        
}

The problem

Forget about the async/await part for now, I have problem communicating with postMessage already...

If I post message immediately, site-B got nothing, the message sent before it's ready

let pWin = window.open(targetURL);
pWin.postMessage("search 'what is A'", targetURL);

If I try to post in onload event, this won't work because onload event is not accessible for cross-origin pWin

let pWin = window.open(targetURL);
pWin.onload=()=>{
  pWin.postMessage("search 'what is A'", targetURL);
}

I found an answer, but can't really understand what it says...

I'm quiet new to postMessage (the first time using it tbh), is there example or demo to learn how to communicate between cross-origin site (that not owned by me)?

Byzod
  • 466
  • 4
  • 18
  • 2
    since site-A is loading site-B, i.e. site-A is loaded, site-B isn't, yet, then you *initiate* the "communication" from site-B, in code that is in site-B – Jaromanda X Sep 25 '22 at 03:17
  • Ho, that's a new route I didn't noticed, I'll have a try – Byzod Sep 29 '22 at 01:57

1 Answers1

0

I managed to written a working demo inspired by @Jaromanda X's idea, but warping event listener in a promise looks weird, I'm not sure I'm doing the right thing. Any suggestion is welcomed

The following use Google as site-A, Baidu as site-B

Scripts in google (site-A)

  let q = "question to ask";
  let result = await GetBaiduResult(q);
  // get result from q, all details are hidden
  alert(result);

  // Sets up a new MessageChannel
  // so we can return a Promise
  async function GetBaiduResult(query) {
    // For security
    let targetOrigin = "https://www.baidu.com";
    // Open search child window
    let pWin = window.open(targetOrigin + "/s?wd=" + encodeURIComponent(query));

    // wait for opened ready, TODO: add timeout
    await new Promise(res=>{
      window.addEventListener("message", receiveMessage, false);
      async function receiveMessage(event)
      {
        // open ready message from target origin
        if (event.origin === targetOrigin && event.data === 'openedReady') {
          res();
        }
      }
    })

    return new Promise(res => {
      const channel = new MessageChannel();
      // this will fire when iframe will answer
      channel.port1.onmessage = e => res(e.data);

      // let target know we're expecting an answer
      // send it its own port
      pWin.postMessage(query, targetOrigin, [channel.port2]);
    });
  }

Scripts in Baidu(site-B)

  let targetOrigin = "https://www.google.com"
  window.addEventListener("message", receiveMessage, false);

  // tell papa we're ready
  if(window.opener){
    window.opener.postMessage('openedReady', targetOrigin);
  }

  // load event
  async function receiveMessage(event)
  {
    if (event.origin !== targetOrigin) {
      return;
    }
    let result = await GetSearchResult(event.data);
    event.ports[0].postMessage(result);
  }

  // work
  async function GetSearchResult(query){
    let result = "answer" //TODO: get actual result
    return result;
  }
Byzod
  • 466
  • 4
  • 18