Files
clean-chrome/background.js
2026-03-01 16:16:58 +08:00

159 lines
4.9 KiB
JavaScript

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);
}
}