Search code examples
google-chrome-extension

is there a way to stop executeScript in chrome extension?


is there a way to stop chrome.scripting.executeScript ?

I have this code

  const onClick = () => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const activeTabId = tabs[0].id;
      chrome.scripting.executeScript({
        target: { tabId: activeTabId },
        args: [DURATION],
        func: script,
      });
    });
   // some code...
  }

I was searching since 2 days all over the web and I am not able to find a way to stop this script on user interaction.

One way I found is by passing the state on user interaction but it is triggering the script once again. please see the below code

    function Popup() {
      const [isActive, setActive] = useState(false);
      const [timer, setTimer] = useState(0);
      const [stop, setStop] = useState(false);
    
      const buttonRef = useRef();
    
      const onClick = () => {
        setStop((x) => !x); // change
        chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
          const activeTabId = tabs[0].id;
          chrome.scripting.executeScript({
            target: { tabId: activeTabId },
            args: [DURATION, stop], // passing the state here
            func: script,
          });
        });
      ....

onClick is from the popup which changes the state stop from false to true. But the problem is this toggling is calling the script again, please see onClick (start/stop connecting) button below

When this button is clicked to stop, I need to find a way to stop executing the script. Any hints or ideas on this are appreciated.

enter image description here

Content of the script.js

function script(duration) {
  setTimeout(() => {
    const buttons = document.querySelectorAll('[aria-label^="Invite"]');
    chrome.storage.local.set({ timer: buttons.length });
    buttons.forEach((btn, idx) => {
      setTimeout(() => {
        const button = btn;
        button.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });
        button.style.backgroundColor = 'red';
      }, duration * 1000 * (idx + 1));
    });
  }, 200);
}

export default script;

Solution

  • When using executeScript the code that was injected is running as an independent content script inside the page and no longer depends on executeScript, so you can't just "stop" it.

    What you can stop is the loop in your injected code e.g. by checking a global variable before proceeding to the next iteration of a loop or in your case setTimeout:

    function injected() {
      window.stopped = false;
      document.querySelectorAll('button').forEach(el => {
        setTimeout(() => {
          // must be a literal value like `true`,
          // otherwise it'll find an element with id="stopped"
          if (window.stopped === true) return;
          el.click();
        }, 1000);
      });
    }
    

    To set this variable you can call executeScript like this:

    chrome.scripting.executeScript({
      target: {tabId: .........},
      func: () => { window.stopped = true },
    });
    

    A variation of this approach is to store all timers in an array:

    function injected() {
      if (!Array.isArray(window.timers)) window.timers = []; 
      document.querySelectorAll('button').forEach(el => {
        window.timers.push(setTimeout(() => { el.click(); }, 1000));
      });
    }
    

    ...and then cancel them:

    chrome.scripting.executeScript({
      target: {tabId: .........},
      func: () => {
        window.timers.forEach(clearTimeout);
        window.timers.length = 0;
      }
    });