0

I am creating a Chrome extension that will interact with a specific page of a React web app. The page includes a table, containing many rows of data, which the extension must extract. However, since the table is implemented using react-virtualized, only a subset of the total available data rows are present at any time, and the extension must simulate user behavior by scrolling the table and waiting for the UI to stabilize, before attempting to extract the next batch of row data.

I have successfully implemented a mechanism to achieve this, and it works reliably. The following snippet shows the structure of the function that is injected into the target web page by the Chrome extension. Note that it is async, since it needs to pause and wait for UI operations to complete.

async function extractTableContent() {
    return new Promise((resolve) => {
    
        // Not shown here is a fully working mechanism that
        // progressively scrapes row data from a virtualized
        // HTML table, scrolling the table after each scrape
        // cycle and waiting for the UI to stabilize.
    
        resolve(tableContent);
    });
}

With the above script code injected into the target page, I can call it from the Dev Tools Console and it always returns the compelete set of table row data, even if that entails scrolling the table many times to discover all the rows:

await extractTableContent()

However, the real-world usage of this mechanism requires it to be invoked in response to a message received from the extension's service worker. Here's how I have configured my injected script to listen for and respond to messages:

const port = chrome.runtime.connect('MY_EXTENSION_ID');

port.onMessage.addListener((request) => {
    (async () => {
        switch (request.action) {
            case 'extract-table-content':
                port.postMessage(await extractTableContent());
                break;

            //
            // Other request actions are implemented here
            //

            default:
                console.error('Invalid request action', request.action);
        }
    })();

    return true;
});

When my service worker sends the extract-table-content request message to the target web page, the extraction function is invoked, but it always returns a single batch of row data. In other words, it seems to be terminating prematurely.

In contrast, if I add a button to the target page to trigger the function, the complete extraction cycle is executed and the result set is complete.

Can you please suggest what I have done wrong, in hooking up the extraction function to the chrome.runtime.connect API?

Tim Coulter
  • 8,705
  • 11
  • 64
  • 95
  • 1
    You're using port-based messaging incorrectly - `return true` is ignored because it's for one-time messaging and there's no need for async IIFE as the listener can be async itself - this can still work if you correctly send and receive the messages in the caller, but why don't you simply use chrome.runtime.sendMessage? – wOxxOm Jul 24 '23 at 14:02
  • Thanks for your comment, wOxxOm. I would definitely prefer to use chrome.runtime.sendMessage, but the communication exists between the extension and a script that is injected into the JavaScript context of the target web app (i.e. it is treated like an external application). It is my understanding that this scenario requires the page to open a persistent connection to the extension, so that the extension may subsequently use it to send messages back. Did I misunderstand this? If there is a way for the extension to use sendMessage to call the page, I will gladly adopt that pattern. – Tim Coulter Jul 25 '23 at 16:00
  • 1
    The MAIN-world injected script cannot use any type of `chrome` messaging by default. You can enable externally_connectable messaging, but it only allows explicitly configured domains and not all URLs. A more universal solution is to use CustomEvent DOM messaging with a content script in the default world, [example](/a/19312198). – wOxxOm Jul 25 '23 at 18:19
  • That's a great solution! Thanks very much for sharing it. I guess I was asking the wrong question in my original post, but I really appreciate you leading me to a solution that fits my use case in such an elegant way. – Tim Coulter Jul 25 '23 at 19:49

0 Answers0