Files
clean-chrome/background.js

231 lines
7.7 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);
// --- New: Auto-click Login Button ---
setTimeout(() => {
// 常规登录按钮选择器
const loginButtonSelectors = [
'button[type="submit"]',
'input[type="submit"]',
'button.login-btn',
'button.submit-btn',
'#login-btn',
'.login-button',
'button.ant-btn-primary' // 适配 Ant Design
];
let loginBtn = null;
for (const selector of loginButtonSelectors) {
const btn = document.querySelector(selector);
if (btn && btn.offsetWidth > 0 && btn.offsetHeight > 0) {
loginBtn = btn;
break;
}
}
// 如果没有按选择器找到,尝试按文本内容查找
if (!loginBtn) {
const allBtns = document.querySelectorAll('button, div[role="button"], span');
for (const btn of allBtns) {
const text = btn.innerText || btn.textContent;
if (btn.offsetWidth > 0 && btn.offsetHeight > 0 &&
(text.includes('登录') || text.includes('Log In') || text.includes('Login')) &&
btn.tagName !== 'BODY') {
loginBtn = btn;
break;
}
}
}
if (loginBtn) {
console.log("[QuickPurge] Login button found, clicking...");
loginBtn.click();
// --- New: Secondary Confirmation Auto-click ---
// 登录后开启定时检测,识别“账号已登录”确认弹窗
let confirmTries = 0;
const confirmInterval = setInterval(() => {
confirmTries++;
const allBtns = document.querySelectorAll('button, div[role="button"], span');
let confirmBtn = null;
for (const btn of allBtns) {
const text = (btn.innerText || btn.textContent).trim();
// 匹配“确认”、“继续登录”等文字,且由于是弹窗按钮,通常会有特定的类名
if (btn.offsetWidth > 0 && btn.offsetHeight > 0 &&
(text === '确认' || text === '确定' || text === '继续登录' || text === 'Confirm') &&
btn.tagName !== 'BODY') {
confirmBtn = btn;
break;
}
}
if (confirmBtn) {
console.log("[QuickPurge] Secondary confirmation button found, clicking...");
confirmBtn.click();
clearInterval(confirmInterval);
}
if (confirmTries > 20) { // 最多检测 5 秒 (250mx * 20)
clearInterval(confirmInterval);
}
}, 250);
}
}, 300); // 填充完后等待 300ms 点击,确保框架已处理输入
}, 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);
}
}