const pendingAutofill = new Map(); chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { // Inject script once the tab is completely loaded if (changeInfo.status === 'complete' && pendingAutofill.has(tabId)) { const creds = pendingAutofill.get(tabId); // Ensure we are injecting on the correct URL (prevent injecting if user navigated away quickly) if (tab.url && tab.url.startsWith(creds.targetUrl)) { // Remove to prevent multiple injections pendingAutofill.delete(tabId); chrome.scripting.executeScript({ target: { tabId: tabId }, func: autoFillLogin, args: [creds.username, creds.password] }).catch(err => console.error("[QuickPurge] Injection error:", err)); } } }); chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "purgeAndRedirect") { const { domain, targetUrl, username, password } = request; // Remove http/https and any path, just keep the exact domain or subdomain let cleanDomain = domain.replace(/^https?:\/\//, '').split('/')[0]; // Create the origin array as expected by browsingData API const origins = [ `http://${cleanDomain}`, `https://${cleanDomain}` ]; // 1. 快速清理核心缓存 chrome.browsingData.remove({ "origins": origins }, { cookies: true, localStorage: true, indexedDB: true }, () => { // 执行清理后跳转并记录准备填写的Tab信息 chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { let activeTabId; if (tabs && tabs.length > 0) { activeTabId = tabs[0].id; chrome.tabs.update(activeTabId, { url: targetUrl }); } else { chrome.tabs.create({ url: targetUrl }, (newTab) => { if (newTab) { pendingAutofill.set(newTab.id, { username, password, targetUrl }); } }); sendResponse({ success: true }); return; } if (activeTabId) { pendingAutofill.set(activeTabId, { username, password, targetUrl }); } sendResponse({ success: true }); }); }); // 2. 异步清理磁盘 Cache chrome.browsingData.remove({ "origins": origins }, { cache: true }, () => { console.log(`[QuickPurge] Cache cleared asynchronously for ${domain}`); }); return true; } }); /** * 这一段代码是在目标页面的上下文中执行的 (Content Script Environment) */ function autoFillLogin(username, password) { let tries = 0; // Simulate complex events common in SPAs (React/Vue) function triggerEvents(element) { element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true })); element.dispatchEvent(new Event('blur', { bubbles: true })); } // Workaround for React's hijacked value setter function setNativeValue(element, value) { const valueSetter = Object.getOwnPropertyDescriptor(element, 'value')?.set; const prototype = Object.getPrototypeOf(element); const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set; if (valueSetter && valueSetter !== prototypeValueSetter) { prototypeValueSetter.call(element, value); } else if (valueSetter) { valueSetter.call(element, value); } else { element.value = value; } } function tryFill() { tries++; // Commonly used selectors for username const userInputs = document.querySelectorAll('input[type="text"], input[type="email"], input[type="tel"], input[name*="user"], input[name*="account"], input[placeholder*="账号"], input[placeholder*="手机"]'); const passInputs = document.querySelectorAll('input[type="password"]'); let usernameEl = null; let passwordEl = null; // Find the first visible inputs for (let el of userInputs) { if (el.offsetWidth > 0 && el.offsetHeight > 0 && el.type !== 'password') { usernameEl = el; break; } } for (let el of passInputs) { if (el.offsetWidth > 0 && el.offsetHeight > 0) { passwordEl = el; break; } } if (usernameEl && passwordEl) { // Set values and trigger React/Vue update events usernameEl.focus(); setNativeValue(usernameEl, username); triggerEvents(usernameEl); // Short delay usually helps some JS frameworks process the first input state setTimeout(() => { passwordEl.focus(); setNativeValue(passwordEl, password); triggerEvents(passwordEl); }, 50); return true; } return false; } if (!tryFill()) { // Retry finding the elements for up to 5 seconds (useful for Single Page Applications where form rendered delayed) const interval = setInterval(() => { if (tryFill() || tries > 20) { clearInterval(interval); } }, 250); } }