- 删除未使用的 getActiveEnv 解构、sender 参数改为 _sender - types.ts 重命名为 types.d.ts,消除无用的 dist/types.js 产物 - 测试文件引用路径从根目录改为 dist/
549 lines
22 KiB
JavaScript
549 lines
22 KiB
JavaScript
"use strict";
|
|
const t = (key, ...args) => chrome.i18n.getMessage(key, args) || key;
|
|
const _sendMessage = chrome.runtime.sendMessage;
|
|
// 2FA / 验证码检测:检测到则不自动提交,仅填充
|
|
function detectCaptcha() {
|
|
// reCAPTCHA
|
|
if (document.querySelector(".g-recaptcha, [data-sitekey], iframe[src*='recaptcha'], iframe[src*='google.com/recaptcha']")) {
|
|
return { type: "reCAPTCHA", reason: t("captchaTitleRe") };
|
|
}
|
|
// hCaptcha
|
|
if (document.querySelector(".h-captcha, iframe[src*='hcaptcha']")) {
|
|
return { type: "hCaptcha", reason: t("captchaTitleH") };
|
|
}
|
|
// 国内常见滑块/验证码容器
|
|
const sliderSelectors = [
|
|
"#slider", ".slider", ".nc_wrapper", ".nc_iconfont",
|
|
"#captcha", ".captcha", ".geetest", "#geetest",
|
|
"[id*='captcha']", "[class*='captcha']",
|
|
"canvas[id*='captcha']", "canvas[id*='verify']"
|
|
];
|
|
for (const sel of sliderSelectors) {
|
|
try {
|
|
const el = document.querySelector(sel);
|
|
if (el && el.offsetParent !== null) {
|
|
return { type: "slider/captcha", reason: t("captchaReasonSlider", sel) };
|
|
}
|
|
}
|
|
catch { /* selector 可能无效 */ }
|
|
}
|
|
// 明显的"验证码"文字输入框(带图形码)
|
|
const codeInputs = document.querySelectorAll("input[name*='captcha' i], input[name*='verify' i], input[name*='code' i], input[id*='captcha' i], input[id*='verify' i], input[placeholder*='验证码' i], input[placeholder*='验证' i]");
|
|
for (const input of Array.from(codeInputs)) {
|
|
if (input.offsetParent !== null && input.type !== "hidden") {
|
|
return { type: "captcha-input", reason: t("captchaReasonInput") };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function setNativeValue(el, value) {
|
|
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set || Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")?.set;
|
|
if (nativeInputValueSetter) {
|
|
nativeInputValueSetter.call(el, value);
|
|
}
|
|
else {
|
|
el.value = value;
|
|
}
|
|
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
}
|
|
function deepQuerySelectorAll(selector, root) {
|
|
const result = [];
|
|
const stack = [root || document];
|
|
while (stack.length) {
|
|
const r = stack.pop();
|
|
result.push(...r.querySelectorAll(selector));
|
|
for (const el of r.querySelectorAll("*")) {
|
|
if (el.shadowRoot)
|
|
stack.push(el.shadowRoot);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function deepQuerySelector(selector, root) {
|
|
return deepQuerySelectorAll(selector, root)[0] || null;
|
|
}
|
|
function resolveElement(value, multiple) {
|
|
if (!value)
|
|
return null;
|
|
const trimmed = value.trim();
|
|
if (!trimmed.startsWith("<")) {
|
|
return multiple ? deepQuerySelectorAll(trimmed) : deepQuerySelector(trimmed);
|
|
}
|
|
const doc = new DOMParser().parseFromString(trimmed, "text/html");
|
|
const el = doc.body.firstElementChild;
|
|
if (!el)
|
|
return null;
|
|
const tag = el.tagName.toLowerCase();
|
|
const id = el.id;
|
|
const cls = Array.from(el.classList);
|
|
const type = el.getAttribute("type");
|
|
const nameAttr = el.getAttribute("name");
|
|
const candidates = [];
|
|
if (id)
|
|
candidates.push(`${tag}#${CSS.escape(id)}`);
|
|
if (cls.length)
|
|
candidates.push(`${tag}.${cls.map(CSS.escape).join(".")}`);
|
|
if (type)
|
|
candidates.push(`${tag}[type="${type}"]`);
|
|
if (nameAttr)
|
|
candidates.push(`${tag}[name="${CSS.escape(nameAttr)}"]`);
|
|
for (const c of cls)
|
|
candidates.push(`${tag}.${CSS.escape(c)}`);
|
|
candidates.push(tag);
|
|
for (const sel of candidates) {
|
|
if (multiple) {
|
|
const els = deepQuerySelectorAll(sel);
|
|
if (els.length)
|
|
return els;
|
|
}
|
|
else {
|
|
const found = deepQuerySelector(sel);
|
|
if (found)
|
|
return found;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function findUsernameField(config) {
|
|
if (config.usernameSelector) {
|
|
const el = resolveElement(config.usernameSelector);
|
|
if (el)
|
|
return el;
|
|
}
|
|
const passwordField = findPasswordField(config);
|
|
if (!passwordField)
|
|
return null;
|
|
const searchScope = passwordField.closest("form") || document;
|
|
const selectors = [
|
|
"input[type='text']",
|
|
"input[type='email']",
|
|
"input[type='tel']",
|
|
"input:not([type='password']):not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio'])",
|
|
];
|
|
for (const sel of selectors) {
|
|
const inputs = deepQuerySelectorAll(sel, searchScope);
|
|
for (const input of inputs) {
|
|
if (input.offsetParent !== null || input.offsetWidth > 0) {
|
|
const name = (input.name || "").toLowerCase();
|
|
const id = (input.id || "").toLowerCase();
|
|
const placeholder = (input.placeholder || "").toLowerCase();
|
|
const autocomplete = (input.getAttribute("autocomplete") || "").toLowerCase();
|
|
if (name.includes("user") || name.includes("name") || name.includes("account") ||
|
|
name.includes("email") || name.includes("login") || name.includes("mail") ||
|
|
id.includes("user") || id.includes("name") || id.includes("account") ||
|
|
id.includes("email") || id.includes("login") ||
|
|
placeholder.includes("user") || placeholder.includes("name") ||
|
|
placeholder.includes("email") || placeholder.includes("account") ||
|
|
placeholder.includes("登录") || placeholder.includes("账号") ||
|
|
placeholder.includes("用户") || placeholder.includes("邮箱") ||
|
|
autocomplete.includes("username") || autocomplete.includes("email")) {
|
|
return input;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const allInputs = deepQuerySelectorAll("input[type='text'], input[type='email'], input:not([type='password']):not([type='hidden']):not([type='submit']):not([type='button']):not([type='checkbox']):not([type='radio'])", searchScope);
|
|
for (const input of allInputs) {
|
|
if (input.offsetParent !== null || input.offsetWidth > 0) {
|
|
return input;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function findPasswordField(config) {
|
|
if (config.passwordSelector) {
|
|
const el = resolveElement(config.passwordSelector);
|
|
if (el)
|
|
return el;
|
|
}
|
|
const passwordInputs = deepQuerySelectorAll("input[type='password']");
|
|
for (const input of passwordInputs) {
|
|
if (input.offsetParent !== null || input.offsetWidth > 0) {
|
|
return input;
|
|
}
|
|
}
|
|
if (passwordInputs.length > 0)
|
|
return passwordInputs[0];
|
|
const allInputs = deepQuerySelectorAll("input");
|
|
for (const input of allInputs) {
|
|
if (input.type === "password")
|
|
return input;
|
|
const sig = ((input.name || "") + " " + (input.id || "") + " " +
|
|
(input.placeholder || "") + " " + (input.getAttribute("autocomplete") || "")).toLowerCase();
|
|
if (sig.includes("password") || sig.includes("passwd") || sig.includes("pwd")) {
|
|
if (input.offsetParent !== null || input.offsetWidth > 0)
|
|
return input;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function findSubmitButton(config, passwordField) {
|
|
if (config.submitSelector) {
|
|
const els = resolveElement(config.submitSelector, true);
|
|
if (els && els.length === 1)
|
|
return els[0];
|
|
if (els && els.length > 1) {
|
|
for (const el of els) {
|
|
if (el.offsetParent === null && el.offsetWidth === 0)
|
|
continue;
|
|
const compact = (el.textContent || el.value || "").toLowerCase().replace(/\s/g, "");
|
|
if (compact.includes("登录") || compact.includes("login") || compact.includes("signin")) {
|
|
return el;
|
|
}
|
|
}
|
|
for (const el of els) {
|
|
if (el.offsetParent !== null || el.offsetWidth > 0)
|
|
return el;
|
|
}
|
|
}
|
|
}
|
|
const form = passwordField ? passwordField.closest("form") : null;
|
|
const searchScope = form || document;
|
|
const submitSelectors = [
|
|
"button[type='submit']",
|
|
"input[type='submit']",
|
|
"button[type='button']",
|
|
"input[type='button']",
|
|
];
|
|
for (const sel of submitSelectors) {
|
|
const btns = deepQuerySelectorAll(sel, searchScope);
|
|
for (const btn of btns) {
|
|
if (btn.offsetParent !== null || btn.offsetWidth > 0) {
|
|
const text = (btn.textContent || btn.value || "").toLowerCase().trim();
|
|
const compact = text.replace(/\s/g, "");
|
|
if (compact.includes("登录") || compact.includes("login") || compact.includes("login") ||
|
|
compact.includes("signin") || compact.includes("提交") || compact.includes("submit") ||
|
|
compact.includes("确定") || compact.includes("ok") || compact.includes("go")) {
|
|
return btn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (const sel of submitSelectors) {
|
|
const btn = deepQuerySelector(sel, searchScope);
|
|
if (btn && (btn.offsetParent !== null || btn.offsetWidth > 0)) {
|
|
return btn;
|
|
}
|
|
}
|
|
const allButtons = deepQuerySelectorAll("button, input[type='button'], a[role='button']", searchScope);
|
|
for (const btn of allButtons) {
|
|
if (btn.offsetParent !== null || btn.offsetWidth > 0) {
|
|
const text = (btn.textContent || btn.value || "").toLowerCase().trim();
|
|
const compact = text.replace(/\s/g, "");
|
|
if (compact.includes("登录") || compact.includes("login") || compact.includes("signin")) {
|
|
return btn;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function fillForm(config, canSubmit = true) {
|
|
const passwordField = findPasswordField(config);
|
|
if (!passwordField) {
|
|
console.log("[AutoLogin]", t("logNoPasswordField"));
|
|
return false;
|
|
}
|
|
const usernameField = findUsernameField(config);
|
|
if (usernameField) {
|
|
setNativeValue(usernameField, config.username);
|
|
console.log("[AutoLogin]", t("logFilledUsername"));
|
|
}
|
|
else {
|
|
console.log("[AutoLogin]", t("logNoUsernameField"));
|
|
}
|
|
setNativeValue(passwordField, config.password);
|
|
console.log("[AutoLogin]", t("logFilledPassword"));
|
|
// 检测到验证码则强制不自动提交,提示用户手动操作
|
|
const captcha = detectCaptcha();
|
|
let actualCanSubmit = canSubmit;
|
|
if (captcha && config.autoSubmit) {
|
|
actualCanSubmit = false;
|
|
console.warn("[AutoLogin]", t("logCaptchaSkip", captcha.reason));
|
|
showCaptchaToast(captcha.reason);
|
|
}
|
|
if (config.autoSubmit && actualCanSubmit) {
|
|
const submitBtn = findSubmitButton(config, passwordField);
|
|
if (submitBtn) {
|
|
// delayMs 给需要 JS 初始化的页面留出时间
|
|
const delay = Math.max(300, Number(config.delayMs) || 300);
|
|
setTimeout(() => {
|
|
console.log("[AutoLogin]", t("logAutoClick", String(delay)));
|
|
submitBtn.click();
|
|
}, delay);
|
|
return true;
|
|
}
|
|
if (passwordField.form) {
|
|
const delay = Math.max(300, Number(config.delayMs) || 300);
|
|
console.log("[AutoLogin]", t("logTryFormSubmit"));
|
|
setTimeout(() => {
|
|
passwordField.form.requestSubmit
|
|
? passwordField.form.requestSubmit()
|
|
: passwordField.form.submit();
|
|
}, delay);
|
|
return true;
|
|
}
|
|
console.log("[AutoLogin]", t("logNoSubmitAndForm"));
|
|
}
|
|
else if (config.autoSubmit && !actualCanSubmit && captcha) {
|
|
console.warn("[AutoLogin]", t("logCaptchaFilled"));
|
|
}
|
|
else if (config.autoSubmit && !canSubmit) {
|
|
console.warn("[AutoLogin]", t("logAttemptExceeded"));
|
|
}
|
|
else {
|
|
console.log("[AutoLogin]", t("logNoAutoSubmit"));
|
|
}
|
|
return true;
|
|
}
|
|
// 验证码提示浮窗,8 秒后自动消失
|
|
let captchaToastEl = null;
|
|
function showCaptchaToast(reason) {
|
|
if (captchaToastEl && captchaToastEl.parentNode) {
|
|
captchaToastEl.parentNode.removeChild(captchaToastEl);
|
|
}
|
|
const el = document.createElement("div");
|
|
el.style.cssText = `
|
|
position: fixed;
|
|
top: 16px;
|
|
right: 16px;
|
|
z-index: 2147483647;
|
|
background: #1a1a2e;
|
|
border: 1px solid #e94560;
|
|
border-radius: 8px;
|
|
padding: 10px 14px;
|
|
color: #e0e0e0;
|
|
font-size: 13px;
|
|
font-family: "Segoe UI","Microsoft YaHei",sans-serif;
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
|
max-width: 300px;
|
|
`;
|
|
el.innerHTML = `<div style="font-weight:600;color:#e94560;margin-bottom:4px;">${t("captchaToastTitle")}</div>` +
|
|
`<div style="font-size:11px;color:#a0a0b0;">${t("captchaToastBody", reason)}</div>`;
|
|
document.body.appendChild(el);
|
|
captchaToastEl = el;
|
|
setTimeout(() => {
|
|
if (el && el.parentNode)
|
|
el.parentNode.removeChild(el);
|
|
captchaToastEl = null;
|
|
}, 8000);
|
|
}
|
|
const MAX_LOGIN_ATTEMPTS = 3;
|
|
const ATTEMPT_COOLDOWN = 5 * 60 * 1000;
|
|
async function getDomainAttempts(envId, domain) {
|
|
const resp = await _sendMessage({ action: "getDomainAttempts", envId, domain }).catch(() => null);
|
|
return resp?.attempts || { count: 0, lastAttempt: 0 };
|
|
}
|
|
async function incrementDomainAttempts(envId, domain) {
|
|
const resp = await _sendMessage({ action: "incrementDomainAttempts", envId, domain }).catch(() => null);
|
|
return resp?.attempts || { count: 0, lastAttempt: 0 };
|
|
}
|
|
async function resetDomainAttempts(envId, domain) {
|
|
await _sendMessage({ action: "resetDomainAttempts", envId, domain }).catch(() => { });
|
|
}
|
|
async function loadAllConfigs() {
|
|
const resp = await _sendMessage({ action: "loadAllConfigs" }).catch(() => null);
|
|
return resp?.configs || [];
|
|
}
|
|
async function autoFillOnLoad() {
|
|
let allConfigs = await loadAllConfigs();
|
|
if (allConfigs.length === 0) {
|
|
// session 可能为空(明文模式 SW 重启后未初始化),触发 background 加载后重试
|
|
try {
|
|
await _sendMessage({ action: "getVaultStatus" });
|
|
allConfigs = await loadAllConfigs();
|
|
}
|
|
catch (e) {
|
|
console.warn("[AutoLogin] 初始化失败:", e.message || e);
|
|
return;
|
|
}
|
|
if (allConfigs.length === 0)
|
|
return;
|
|
}
|
|
// 每个配置有独立开关 enabled
|
|
const enabledConfigs = allConfigs.filter((c) => c.enabled !== false);
|
|
if (enabledConfigs.length === 0)
|
|
return;
|
|
const matched = enabledConfigs.filter((c) => isDomainMatch(window.location.href, c.domain));
|
|
if (matched.length === 0)
|
|
return;
|
|
if (matched.length === 1) {
|
|
await fillSingleConfig(matched[0]);
|
|
return;
|
|
}
|
|
// 多环境命中:弹浮窗让用户选择
|
|
console.log("[AutoLogin]", t("logMatchedMulti", String(matched.length)));
|
|
showEnvPicker(matched);
|
|
}
|
|
// SPA 表单可能晚于脚本加载,填不到时每 500ms 重试,30 秒超时
|
|
const SPA_FILL_TIMEOUT_MS = 30000;
|
|
const SPA_RETRY_INTERVAL_MS = 500;
|
|
async function fillSingleConfig(config) {
|
|
console.log("[AutoLogin]", t("logStartFillEnv", config.envName || t("defaultEnvName")));
|
|
// 超限后仅填充不自动提交
|
|
let canSubmit = config.autoSubmit;
|
|
if (config.autoSubmit) {
|
|
const attempts = await getDomainAttempts(config.envId, config.domain);
|
|
const now = Date.now();
|
|
if (now - attempts.lastAttempt > ATTEMPT_COOLDOWN) {
|
|
attempts.count = 0;
|
|
}
|
|
if (attempts.count >= MAX_LOGIN_ATTEMPTS) {
|
|
canSubmit = false;
|
|
console.warn("[AutoLogin] " + t("logAttemptExceededDetail", config.envName, config.domain, String(attempts.count), String(MAX_LOGIN_ATTEMPTS), String(Math.ceil(ATTEMPT_COOLDOWN / 60000))));
|
|
}
|
|
else {
|
|
await incrementDomainAttempts(config.envId, config.domain);
|
|
}
|
|
}
|
|
// SPA 重试填充,30 秒超时
|
|
const startTime = Date.now();
|
|
const tryFill = () => {
|
|
const elapsed = Date.now() - startTime;
|
|
if (elapsed > SPA_FILL_TIMEOUT_MS) {
|
|
const inputs = deepQuerySelectorAll("input");
|
|
const pwd = deepQuerySelectorAll("input[type='password']");
|
|
console.warn("[AutoLogin] 填充超时: input=" + inputs.length + " pwd=" + pwd.length + " form=" + document.querySelectorAll("form").length + " | " + inputs.slice(0, 6).map((i) => "type=" + (i.type || "?") + "/name=" + (i.name || i.id || "?")).join(", "));
|
|
return;
|
|
}
|
|
const filled = fillForm(config, canSubmit);
|
|
if (!filled) {
|
|
setTimeout(tryFill, SPA_RETRY_INTERVAL_MS);
|
|
}
|
|
};
|
|
tryFill();
|
|
}
|
|
// 多环境命中时在页面右上角插入选择浮窗
|
|
let envPickerEl = null;
|
|
function showEnvPicker(configs) {
|
|
if (envPickerEl && envPickerEl.parentNode) {
|
|
envPickerEl.parentNode.removeChild(envPickerEl);
|
|
envPickerEl = null;
|
|
}
|
|
const overlay = document.createElement("div");
|
|
overlay.id = "auto-login-env-picker";
|
|
overlay.style.cssText = `
|
|
position: fixed;
|
|
top: 16px;
|
|
right: 16px;
|
|
z-index: 2147483647;
|
|
background: #1a1a2e;
|
|
border: 1px solid #e94560;
|
|
border-radius: 10px;
|
|
padding: 14px 16px;
|
|
min-width: 240px;
|
|
max-width: 320px;
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
|
font-family: "Segoe UI","Microsoft YaHei",sans-serif;
|
|
color: #e0e0e0;
|
|
font-size: 13px;
|
|
`;
|
|
const title = document.createElement("div");
|
|
title.textContent = t("envPickerTitle");
|
|
title.style.cssText = "font-weight:600;color:#e94560;margin-bottom:10px;font-size:14px;";
|
|
overlay.appendChild(title);
|
|
const list = document.createElement("div");
|
|
list.style.cssText = "display:flex;flex-direction:column;gap:6px;";
|
|
configs.forEach((cfg) => {
|
|
const btn = document.createElement("button");
|
|
btn.style.cssText = `
|
|
text-align:left;
|
|
background:#16213e;
|
|
border:1px solid #0f3460;
|
|
border-radius:6px;
|
|
padding:8px 10px;
|
|
color:#e0e0e0;
|
|
cursor:pointer;
|
|
font-size:12px;
|
|
font-family:inherit;
|
|
transition:background 0.15s;
|
|
`;
|
|
const envLabel = escapeHtml(cfg.envName || t("defaultEnvName"));
|
|
const domainLabel = escapeHtml(cfg.alias || cfg.domain);
|
|
const userLabel = escapeHtml(cfg.username);
|
|
btn.innerHTML = `
|
|
<div style="font-weight:600;color:#e94560;">${envLabel}</div>
|
|
<div style="color:#888;font-size:11px;margin-top:2px;">${domainLabel} · ${userLabel}</div>
|
|
`;
|
|
btn.addEventListener("mouseenter", () => {
|
|
btn.style.background = "#0f3460";
|
|
});
|
|
btn.addEventListener("mouseleave", () => {
|
|
btn.style.background = "#16213e";
|
|
});
|
|
btn.addEventListener("click", async () => {
|
|
overlay.parentNode && overlay.parentNode.removeChild(overlay);
|
|
envPickerEl = null;
|
|
await fillSingleConfig(cfg);
|
|
});
|
|
list.appendChild(btn);
|
|
});
|
|
overlay.appendChild(list);
|
|
const closeBtn = document.createElement("button");
|
|
closeBtn.textContent = t("cancelBtn");
|
|
closeBtn.style.cssText = `
|
|
margin-top:10px;
|
|
width:100%;
|
|
padding:6px;
|
|
background:transparent;
|
|
border:1px solid #0f3460;
|
|
border-radius:6px;
|
|
color:#888;
|
|
cursor:pointer;
|
|
font-size:11px;
|
|
font-family:inherit;
|
|
`;
|
|
closeBtn.addEventListener("click", () => {
|
|
overlay.parentNode && overlay.parentNode.removeChild(overlay);
|
|
envPickerEl = null;
|
|
});
|
|
overlay.appendChild(closeBtn);
|
|
document.body.appendChild(overlay);
|
|
envPickerEl = overlay;
|
|
}
|
|
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
if (message.action === "manualFill" && message.config) {
|
|
resetDomainAttempts(message.config.envId, message.config.domain);
|
|
const filled = fillForm(message.config, true);
|
|
if (filled) {
|
|
sendResponse({ success: true });
|
|
}
|
|
else {
|
|
const inputs = deepQuerySelectorAll("input");
|
|
const pwdInputs = deepQuerySelectorAll("input[type='password']");
|
|
sendResponse({
|
|
success: false,
|
|
diag: {
|
|
total: inputs.length,
|
|
pwd: pwdInputs.length,
|
|
forms: document.querySelectorAll("form").length,
|
|
sample: inputs.slice(0, 10).map((i) => `type=${i.type || "?"}|name=${(i.name || i.id || i.placeholder || "?").slice(0, 20)}`),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
else if (message.action === "manualFillMulti" && Array.isArray(message.configs)) {
|
|
showEnvPicker(message.configs);
|
|
sendResponse({ success: true });
|
|
}
|
|
return true;
|
|
});
|
|
autoFillOnLoad();
|
|
// URL 变化后若新页面不再匹配任何配置,重置之前匹配环境的失败计数
|
|
let lastUrl = window.location.href;
|
|
const observer = new MutationObserver(() => {
|
|
if (window.location.href !== lastUrl) {
|
|
const prevUrl = lastUrl;
|
|
lastUrl = window.location.href;
|
|
loadAllConfigs().then((allConfigs) => {
|
|
const stillMatched = allConfigs.some((c) => isDomainMatch(window.location.href, c.domain));
|
|
if (!stillMatched) {
|
|
const prevMatched = allConfigs.filter((c) => isDomainMatch(prevUrl, c.domain));
|
|
prevMatched.forEach((c) => resetDomainAttempts(c.envId, c.domain));
|
|
}
|
|
});
|
|
setTimeout(autoFillOnLoad, 1000);
|
|
}
|
|
});
|
|
observer.observe(document.body, { childList: true, subtree: true });
|