I'm building addon for firefox user can take screenshot, but browser.runtime.sendMessage returns undefined

34 Views Asked by At

I'm trying to implement a function that user can take screenshot using browser.tabs.captureVisibleTab .

But browser.runtime.sendMessage immediately returns undefined.

In console.log("dataUrl ::: ", dataUrl) of browser.runtime.onMessage.addListener it logged data url, so it's working. But somehow, it's undefined in screenshot.addEventListener My current code is as below. It works fine in chrome(I changed browser with chrome of course).

I appreciate any advice.

// manifest.json

"permissions": ["activeTab", "tabs", "storage", "webRequest", "<all_urls>"],
  "host_permissions": ["<all_urls>"]

// popup.js

screenshot.addEventListener("click", async () => {
  try {
    const response = await browser.runtime.sendMessage({
      type: "takeScreenshot",
    });
    console.log("response is ", response); // undefined
    if (response.dataUrl) {
      const img = document.createElement("img");
      img.src = response.dataUrl;
      document.body.appendChild(img);
    }
  } catch (error) {
    console.error("Error sending message:", error);
  }
});

// background.js

browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
  
  if (message.type === "takeScreenshot") {
    const option = { active: true, currentWindow: true }; 
    await browser.tabs.query(option, async (tabs) => {
      const activeTab = tabs[0];
      if (!activeTab) {
        await sendResponse({ error: "No active tab." });
        return;
      }
      await browser.tabs.captureVisibleTab(
        activeTab.windowId,
        { format: "png" },
       
        async (dataUrl) => {
          if (browser.runtime.lastError) {
            await sendResponse({ error: browser.runtime.lastError.message });

            return;
          }
          console.log("dataUrl ::: ", dataUrl); //it logs correct dataUrl

          await sendResponse({ dataUrl: dataUrl }); 
        }
       
      );
    });
    return true;
  }
});

I enabled the “<all_urls>” permission in the Add-ons Manager according to this . But it still returns same result.

2

There are 2 best solutions below

2
wOxxOm On BEST ANSWER

You don't need the background script and messaging in this case - just use captureVisibleTab in the popup! But, assuming you want to use the background script, the problem is that you've incorrectly used Chrome's atavistic return true in a Firefox's async onMessage.

In Firefox you simply return the value just like you do in any async code:

browser.runtime.onMessage.addListener(async (message, sender) => {
  if (message.type === 'takeScreenshot') {
    const [{windowId}] = await browser.tabs.query({active: true, currentWindow: true});
    const dataUrl = await browser.tabs.captureVisibleTab(windowId, {format: 'png'});
    return {dataUrl};
  }
});

The exceptions are automatically sent to your calling code's catch, no need to catch them here.

There may be other causes for not receving a response:

  • another onMessage listener returns something earlier
  • using a libary to promisify browser, which is not necessary for Firefox
  • loading the background script in html
  • pausing the listener in devtools debugger
1
passThru On

Observation #1:

It seems to be in fashion lately for people to add “await” outside of functions; probably thinking that it’ll make them run asynchronously, but it doesn’t work like that… a function has to be async by design (ie: promise) to be compatible with await.

Observation #2:

Your “return true” under browser.runtime.onMessage, is illegal and utterly useless and may well be making your function terminate prematurely.

Comment out “return true;” as well as all those “await”s and try again.