crxjs / chrome-extension-tools

Bundling Chrome Extensions can be pretty complex. It doesn't have to be.
https://crxjs.dev/vite-plugin
2.89k stars 193 forks source link

Add React components interaction with content script example on the docs #859

Open sovetski opened 9 months ago

sovetski commented 9 months ago

Describe the problem

Hi,

Firstly, thanks for this great tool!

For me, the problem is, by following the documentation, I am not able to interact with the current website's DOM with my extension components.

For example, I have this button on my extension. When I click on it, first I want to show a basic JavaScript alert on the current website's DOM and not the extension one. But how to do it? The button comes from my App.jsx, and we can only interact with the current tab's DOM from content script. image

I found this chrome.tabs.sendMessage and chrome.runtime.onMessage.addListener to communicate between my App.jsx and content-script.js, but it is not very clean...

Describe the proposed solution

Add an example of extension which have a button and by clicking on it, we will interact with the current tab's DOM

Alternatives considered

chrome.tabs.sendMessage and chrome.runtime.onMessage.addListener

Importance

would make my life easier

Toumash commented 6 months ago

Outside of the build tool scope - should be closed. Im using the quoted methods just fine

sovetski commented 6 months ago

Outside of the build tool scope - should be closed. Im using the quoted methods just fine

Great if it works for you, but it will be more useful if you share your solution with some examples here, to help other people too :)

Toumash commented 6 months ago

@sovetski tl;dr
code:

// popup
const [tab] = await chrome.tabs.query({active: true,currentWindow: true});
if (tab.id === undefined) return;
await chrome.tabs.sendMessage(tab.id, { type: 'do_something' });

// content_script
function listener(msgObj, _, _) {
      if (msgObj.type === 'do_something') {
        //TODO: do something
      }
    }
chrome.runtime.onMessage.addListener(listener);

the only sample from google i could find injects a script straight from a popup - you dont need a listener then https://github.com/GoogleChrome/chrome-extensions-samples/blob/main/functional-samples/reference.mv3-content-scripts/popup.js

My sample use case: i need to show a state in popup.js thats based on information from a content_script. If you dont need a response (licenseState) you can just replace respond(licenseState); with your interact with the current tab's DOM code

This is a sample from react (a hook) but it doesnt matter - same methods.

note for more complex scenarios people usually communicate through a service worker (complex code that would need to run longer than popup is open) so that would be popup -> service_worker -> content_scripts.

// popup/useLicenseStatus.tsx
export const useLicenseStatus = (setLicense: (license: LicenseState) => void) => {
  useEffect(() => {
    const fetchData = async () => {
      const [tab] = await chrome.tabs.query({
        active: true,
        currentWindow: true,
      });
      if (tab.id === undefined) return;
      const response: LicenseState = await chrome.tabs.sendMessage(tab.id, { type:  'get_license_status'});
      console.log('response', response);
      if (response !== undefined) {
        setLicense(response);
      }
    };

    void fetchData();
  });
};
// contentscript.tsx
export function useShareLicenseStatus(licenseState: LicenseState) {
  const listener = useCallback(
    (msgObj, _, respond: (_: LicenseState) => void) => {
      if (msgObj.type === 'get_license_status') {
        respond(licenseState);
      }
    },
    [licenseState]
  );
  useEffect(() => {
    chrome.runtime.onMessage.addListener(listener);
    return () => chrome.runtime.onMessage.removeListener(listener);
  }, [listener]);
}
kumaramit3 commented 5 months ago

manifest.json

 "permissions": ["scripting"]

src/App.jsx

import React from "react";
import content from "./content?script&module"; 
import "./App.css";

const App = () => {
  const handleClick = async () => {
    const [tab] = await chrome.tabs.query({
      active: true,
      currentWindow: true,
    });
    if (!tab.id) return;
    await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      files: [content],
    })
  }

  return (
    <div>
      <h2>Welcome! Coming Soon!</h2>
      <button onClick={handleClick}>Alert button example!</button>
    </div>
  );
}

export default App;

src/content.js

alert("alert from extension")
kumaramit3 commented 5 months ago

This Advanced Config tutorial will help you more.

Toumash commented 4 months ago

@jacksteamdev should be closed

sovetski commented 4 months ago

I don't think that it can be considered as closed, the solutions are "home made" and are not relevant for me. Or an example can be added to the documentation may be.

Toumash commented 4 months ago

Out of scope for the repo to teach architecture of browser extensions. Google Chrome Extension samples is a good place to discuss it. It would probably be good to insert the code provided here into the samples repository for others to use.

https://github.com/GoogleChrome/chrome-extensions-samples/issues

sovetski commented 4 months ago

Out of scope for the repo to teach architecture of browser extensions.

On the documentation, we have very simple explanations like right-click it and choose "Inspect popup window" and it is also out of scope but added to simplify the process and make developers more familiar with this. This repository was made to make extension development more easy, so why not add some "Examples" on the documentation?

Toumash commented 4 months ago

@sovetski Alright it makes sense - lets add something into the docs. What do you think would be the good spot to place it? Chrome has a full tutorial for this concept: https://developer.chrome.com/docs/extensions/develop/concepts/messaging We could also add a link for content_scripts to already existing docs https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts

"Communication" ? image

kumaramit3 commented 4 months ago

@sovetski I have updated my solution. Now it's working for content scripts.

sovetski commented 4 months ago

@Toumash it looks a good idea, I think it will be useful. "Communication" or "Communication with DOM" or "DOM interaction"?

@kumaramit3 thank you for your time and help! In my case, I already finished my extension with a similar solution that you provided. I did not close the issue because if I have to create a new plugin, it will take some time because the first one was very long to find solutions for the content script. If the documentation will be updated (by adding a link like we are talking about above etc.), maybe it will be more useful, what do you think?