How do I execute a script on the current tab using chrome.scripting API?

132 Views Asked by At

I try to create a simple auto clicker for Chromium based browsers. I make use of the activeTab and scripting permissions from Manifest V3.

What I try to achieve

I want my auto clicker to start when I press Alt+Shift+K and stop when pressing the shortcut again.

The error I receive

My service worker console returns the following error when pressing Alt+Shift+K to activate the extension on the current tab.

Uncaught (in promise) ReferenceError: tab is not defined
Context
service-worker.js
Stack Trace
service-worker.js:11 (anonymous function):

Line 11: target: { tabId: tab.id },

What I already tried

I want to follow best practices. The official API documentation uses a function getTabId() { ... } to get the tab the extension should run in. I am not sure if my error is due to accessing a value out of scope but I am sure there is probably a simple way for my problem.


Source Code

manifest.json:

{
  "manifest_version": 3,
  "name": "Autoclicker",
  "description": "A simple auto clicker.",
  "version": "0.1",
  "author": "[email protected]",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "background": {
    "service_worker": "service-worker.js"
  },
  "commands": {
    "toggle-auto-clicker": {
      "description": "Toggle auto clicker",
      "suggested_key": {
        "default": "Alt+Shift+K"
      }
    }
  }
}

service-worker.js:

let autoClickerEnabled = false;

// Toggle auto clicker
chrome.commands.onCommand.addListener(async (command) => {
  if (command === "toggle-auto-clicker") {
    autoClickerEnabled = !autoClickerEnabled;
    console.log("AutoClicker is now " + (autoClickerEnabled ? "enabled" : "disabled"));

    if (autoClickerEnabled) {
      chrome.scripting.executeScript({
        target: { tabId: tab.id },          // <-- error on this line
        files: ['scripts/auto-clicker.js'],
      })
      .then(() => console.log("injected scripts/auto-clicker.js"))
      .catch(err => console.error("Script injection failed: ", err));
    }   
  }
});

auto-clicker.js:

if (typeof autoClickerInterval === 'undefined') {
  let autoClickerInterval = false;
}

document.addEventListener('mousemove', (event) => {
  let mousePosition = { x: event.clientX, y: event.clientY };
});

function simulateClick() {// snip}
function startAutoClicker() {// snip}
function stopAutoClicker() {// snip}

chrome.runtime.onMessage.addListener((message) => {
  if (message === 'startAutoClicker') {
    startAutoClicker();
  } 
  if (message === 'stopAutoClicker') {
    stopAutoClicker();
  }
});
1

There are 1 best solutions below

0
On BEST ANSWER

I managed to get my auto clicker working using chrome.tabs.query. I found the sample code for it on the In depth: core concepts > Message Passing article.

The full service-worker.js file is down below.

let autoClickerEnabled = false;

// getCurrentTabId() retrieves the ID of the currently active tab
// in the last focused window,
// which is necessary for sending messages to the correct tab.
async function getCurrentTabId() {
  try {
    return (await chrome.tabs.query({ active: true, lastFocusedWindow: true }))[0]?.id;
  } catch (err) {
    console.error("error getting current tab ID: ", err);
    return null;
  }
}

// This function injects the auto-clicker script into the specified tab
// if it's not already injected.
async function injectScriptIfNeeded(tabId) {
  try {
    // Check if the script is already injected
    const [result] = await chrome.scripting.executeScript({
      target: { tabId },
      function: () => window.autoClickerInjected,
    });

    // If the script is not injected, inject it
    if (!result?.result) {
      await chrome.scripting.executeScript({
        target: { tabId },
        files: ['scripts/auto-clicker.js'],
      });

      // After successful injection, mark the script as injected
      await chrome.scripting.executeScript({
        target: { tabId },
        function: () => { window.autoClickerInjected = true; },
      });
    }
  } catch (err) {
    console.error("Failed to inject or check script: ", err);
  }
}


// This async function toggles the state of the extension.
// It sends a message with { toggle: true } to the content script
// running in the current tab. This approach simplifies the logic
// by not requiring the service worker to keep track of the auto-clicker's state.
async function toggleEnableExtension() {
  let currentTabId = await getCurrentTabId();
  if (currentTabId) {
    await injectScriptIfNeeded(currentTabId);
    chrome.tabs.sendMessage(currentTabId, { toggle: true }).catch(err =>
      console.error("failed to send message: ", err)
    );
  };
}