lxieyang / chrome-extension-boilerplate-react

A Chrome Extensions boilerplate using React 18 and Webpack 5.
MIT License
3.53k stars 1.09k forks source link

Error using chrome.tabs.sendMessage with await #208

Open lolaraghvendra opened 4 months ago

lolaraghvendra commented 4 months ago

I used to add await directly while calling the chrome.tabs.sendMessage and it used to work great:

const data = await chrome.tabs.sendMessage(tab.id, {
  type: 'get_data',
});

for some unknown reasons it stopped working that way months ago and i desperately need to use it asynchronously!

the solution i tried looks something like this:

i created a utility function in another file utils.js to use it anywhere i needed

export function sendMessageToContent(tabId, request) {
  return new Promise((resolve, reject) => {
    chrome.tabs.sendMessage(tabId, request, (response) => {
      console.log("response recieved from content script -- ", response, "at ",new Date().toISOString());
      if (chrome.runtime.lastError) {
        console.log("error caught in response of content script -- ", chrome.runtime.lastError);
        reject(chrome.runtime.lastError);
      } else {
        resolve(response);
      }
    });
  });
}

and i use it in my popup.jsx like this:

 // I call this function on a button click
 const getCanonicalLink = async () => {
    const [tab] = await chrome.tabs.query({
      currentWindow: true,
      active: true,
    });

    console.log("message sent for tab id -- ",tab.id)
    const response = await sendMessageToContent(tab.id,{
      type:'get_data'
    });

    console.log("response -- ",response);
    return data;
  };

The thing is i am not getting a response from the sendMessageToContent and instead the chrome.runtime.lastError is set in callback of this function and this following error is caught: image

as per the code in my content script (mentioned below) the code is executed successfully and there is no error in the logic but there is always a delay between the last log of the content script and the callback in the sendMessageToContent content script log: image

popup log: image

afaik, the call back is called only when the sendResponse is called, so i guess the flow is OK. but why am i getting the response in this callback as undefined This is how far i have reached and i cant understand what shall i do exactly to make it work asynchronously!

BTW, here is the code in content script:

chrome.runtime.onMessage.addListener(async function (request, sender, sendResponse) {
  console.log('request', request);
  console.log('sender', sender);

  if (request.type === 'get_data') {
    try {

      const ab = await fetch(window.location.href);
      let parser1 = new DOMParser();

      // Parse the text
      let html = parser1.parseFromString(await ab.text(), 'text/html');
      console.log(html);
      // let canonical = html.querySelector('link[rel=canonical]')['href'];
      let canonical = html.querySelector('link[rel=canonical]');
      console.log('canonical', canonical);

      if (canonical) {
        canonical = html.querySelector('link[rel=canonical]')['href'];
        let username = document.querySelectorAll('#channel-handle')[1].textContent;

        console.log('canonical', canonical);
        // return canonical;
        sendResponse({
          canonicalLink: canonical,
          redirectedUrl: ab.url,
          username,
        });
      } else {
        sendResponse({
          canonicalLink: null,
          redirectedUrl: ab.url,
          username: window.location.href.split('@')[1],
        });
      }
      console.log("request processed!!",new Date().toISOString());
    } catch (error) {
      console.log("error caught in content script -- ",error);
      sendResponse({error})
    }
    // return true;
  }
}
Toumash commented 4 months ago

Isnt this the same as https://stackoverflow.com/a/20077854/3711660? Meaning you need to return true and then use async code like await fetch(). Wrap async code in a function call it (with void operator, not await like soe void runAsyncCode();return true; )and return true