aeksco / react-typescript-web-extension-starter

:desktop_computer: Web Extension starter kit built with React, TypeScript, TailwindCSS, Storybook, Jest, EsLint, Prettier, and Webpack. Supports Google Chrome + Mozilla Firefox + Brave Browser + Microsoft Edge + Opera :fire:
MIT License
926 stars 81 forks source link

send message from background to contentscript/web page? #54

Open jryebread opened 2 years ago

jryebread commented 2 years ago

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!

bershanskiy commented 2 years ago

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]});
    }
})
jryebread commented 2 years ago

@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

bershanskiy commented 2 years ago

@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: