I try to create a simple auto clicker for Chromium based browsers. I make use of the activeTab
and scripting
permissions from Manifest V3.
I want my auto clicker to start when I press Alt+Shift+K
and stop when pressing the shortcut again.
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 },
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.
manifest.json
:
{
"manifest_version": 3,
"name": "Autoclicker",
"description": "A simple auto clicker.",
"version": "0.1",
"author": "name@example.com",
"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();
}
});
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)
);
};
}