menifest.json
{
"manifest_version": 3,
"name": "Run Snippet from Right Click",
"version": "1.0",
"permissions": [
"contextMenus",
"activeTab",
"scripting","storage"
],
"background": {
"service_worker": "background.js"
}
}
bakground.js
// 1. Create the right-click menu item when the extension installs
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "runSearchAndViewAutomation",
title: "Run Search & View Automation",
contexts: ["all"]
});
});
// 2. Initial Trigger: User right-clicks and hits the menu item
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
if (info.menuItemId === "runSearchAndViewAutomation") {
console.log("Context menu clicked. Starting CALL_ONE...");
// Set state to CALL_TWO *before* the refresh happens
await chrome.storage.local.set({ extension_step: 'CALL_TWO' });
// Inject and execute Phase 1
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: automatedSearchAndView,
args: ['CALL_ONE']
});
}
});
// 3. AUTOMATION ENGINE: Listens for the page refresh to complete
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
// Wait until the page has fully loaded after the refresh
if (changeInfo.status === 'complete') {
// Check if CALL_ONE left a footprint telling us to run CALL_TWO
const data = await chrome.storage.local.get('extension_step');
if (data.extension_step === 'CALL_TWO') {
console.log("Page refresh detected! Automatically triggering CALL_TWO...");
// Clear storage instantly so it doesn't get trapped in an endless loop
await chrome.storage.local.remove('extension_step');
// Automatically inject and run Phase 2
chrome.scripting.executeScript({
target: { tabId: tabId },
func: automatedSearchAndView,
args: ['CALL_TWO']
});
}
}
});
// 4. Core UI runner injected directly onto the webpage
async function automatedSearchAndView(phase) {
const SEARCH_BUTTON_SELECTOR = '#edit-submit-registration-title';
const TARGET_ANCHOR_SELECTOR = 'a.view-link';
const waitForElement = (selector, timeout = 7000) => {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const interval = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
clearInterval(interval);
resolve(element);
} else if (Date.now() - startTime > timeout) {
clearInterval(interval);
reject(new Error(`Timeout: "${selector}" not found.`));
}
}, 100);
});
};
if (phase === 'CALL_ONE') {
console.log("Executing CALL_ONE: Clicking search button...");
const searchButton = document.querySelector(SEARCH_BUTTON_SELECTOR);
if (!searchButton) {
console.error("Search button missing from webpage.");
return;
}
searchButton.click();
}
else if (phase === 'CALL_TWO') {
console.log("Executing CALL_TWO: Polling for anchor link...");
try {
const viewAnchor = await waitForElement(TARGET_ANCHOR_SELECTOR);
if (viewAnchor.href && viewAnchor.href !== '#' && !viewAnchor.href.startsWith('javascript:')) {
window.location.href = viewAnchor.href;
} else {
viewAnchor.click();
}
} catch (error) {
console.error(error.message);
}
}
}
No comments:
Post a Comment