From 5d9dbb182f1339d51593359741b277505603c9c3 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 1 Mar 2026 16:16:58 +0800 Subject: [PATCH] Initial commit: background.js --- background.js | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 background.js diff --git a/background.js b/background.js new file mode 100644 index 0000000..10ad2bc --- /dev/null +++ b/background.js @@ -0,0 +1,158 @@ +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); + } +}