react-typescript-web-extension-starter icon indicating copy to clipboard operation
react-typescript-web-extension-starter copied to clipboard

send message from background to contentscript/web page?

Open jryebread opened this issue 3 years ago • 3 comments

Hi there, I was wondering: why this starter doesn't have a content script to be able to read the DOM of the web page

I am trying to create a context menu (which works fine) in backgroundPage.ts:

browser.runtime.onInstalled.addListener(function() {
    chrome.contextMenus.create({
        title: "Mage - Explain Code",
        contexts: ["selection"],
        id: "Mage - Explain Code"
    });
});
browser.contextMenus.onClicked.addListener(function (info, tab) {
    if (info.menuItemId === "Mage - Explain Code") {
        let code = info.selectionText
        console.log(code)
        browser.runtime.sendMessage({type: "mageQuery"}); //send msg to tab
    }
})

tf2BPOG

when the context menu button is pressed it should send a message to the current tab and open a tool tip on the highlighted text in the web page.

Which file should I handle this message in? Can this be done inside the popup folder component.tsx, or do I need to create a content script file somehow?

I have spent all day struggling with how to do this so I would very much appreciate help! thank you!

jryebread avatar Aug 22 '22 01:08 jryebread

This template has scripting permission, so the easiest way to do this is:

in src/backgroundPage.ts add

browser.runtime.onInstalled.addListener(function() {
    chrome.contextMenus.create({
        title: "Mage - Explain Code",
        contexts: ["selection"],
        id: "Mage - Explain Code"
    });
});

// This function is serialized, sent to content context, deserialized, and then executed,
// so all external variables will be lost. Use arguments instead.
function handler(code) {
    console.log('Hello world!');
    console.log(code);
}

browser.contextMenus.onClicked.addListener(function (info, tab) {
    if (info.menuItemId === "Mage - Explain Code") {
        let code = info.selectionText;
        // Use args to pass arguments to handler
        browser.scripting.executeScript({target: {tabId: tab.id}, func: handler, args: [code]});
    }
})

bershanskiy avatar Aug 26 '22 18:08 bershanskiy

@bershanskiy thanks I am trying to import tippy.js to insert this javascript into the page (on the highlighted text) however I'm running into issues with using the installed module in the injected script. in backgroundPage.ts

import tippy from 'tippy.js'

function executeTippy() {
    const selection = window.getSelection();
    if (selection === null) {
        throw new Error("No selection of text available")
        return;
    }
         // get the selection then
    const range = selection.getRangeAt(0);        // the range at first selection group
    const rect = range.getBoundingClientRect(); // and convert this to useful data
    // const scrollPos = window.scrollY;
    // const containerTop = scrollPos + rect.top - 50 + "px";
    // const containerLeft = rect.left + rect.width / 2 - 50 + "px";

    const divTextSelect = document.createElement('div');   // make box

    divTextSelect.setAttribute("id",
        "textSelectionTooltipContainer"
    );

    // divTextSelect.style.transform =
    // "translate3d(" + containerLeft + "," + containerTop + "," + "0px)";

    
     // With the above scripts loaded, you can call `tippy()` with a CSS
      // selector and a `content` prop:
      tippy('#textSelectionTooltipContainer', {
        content: 'My tooltip!',
        showOnCreate: true,
      });
    divTextSelect.style.border = '2px solid black';      // with outline
    divTextSelect.style.position = 'absolute';              // fixed positioning = easy mode
    divTextSelect.style.top = rect.top + 'px';       // set coordinates
    divTextSelect.style.left = rect.left + 'px';
    divTextSelect.style.height = rect.height + 'px'; // and size
    divTextSelect.style.width = rect.width + 'px';
    document.body.appendChild(divTextSelect);
    console.log("added textSelect")
}

The error I get is that tippy is undefined: image

jryebread avatar Aug 29 '22 16:08 jryebread

@jryebread This error is expected, since executeTippy() is stringified by

browser.scripting.executeScript({target: {tabId: tab.id}, func: executeTippy});

It is almost like calling .toString() on any other function, so all external variables are lost (including external imports). I recommend one of these methods:

  • use browser.scripting.executeScript with a files parameter instead of func. Just copy executeTippy() into a separate file and pass its path in files
  • use dynamic import within executeTippy(). I do not recommend this method, unless you do not need to support old browsers are able to test extension in all relevant browser versions.

bershanskiy avatar Aug 29 '22 20:08 bershanskiy