Initial commit: background.js
This commit is contained in:
158
background.js
Normal file
158
background.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user