修复填入当前域名:错误提示层级与侧边栏取址
This commit is contained in:
@@ -966,7 +966,8 @@ h2 {
|
||||
font-size: 13px;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
z-index: 9999;
|
||||
/* 必须高于弹窗遮罩(.modal z-index: 10000),否则弹窗内操作触发的提示会被遮罩盖住 */
|
||||
z-index: 10001;
|
||||
pointer-events: none;
|
||||
max-width: 90vw;
|
||||
white-space: nowrap;
|
||||
|
||||
+37
-1
@@ -5,8 +5,11 @@ let autoLockTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const AUTO_LOCK_MS = 10 * 60 * 1000; // 10 分钟无操作自动锁定
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
// 关闭"点击图标即开面板"的内置行为,改由 action.onClicked 接管:
|
||||
// 点击图标那一刻 Chrome 会授予 activeTab,onClicked 里趁机捕获当前标签页
|
||||
// 地址缓存起来,侧边栏里"填入当前域名"在切换标签页后也能取到地址。
|
||||
chrome.sidePanel
|
||||
.setPanelBehavior({ openPanelOnActionClick: true })
|
||||
.setPanelBehavior({ openPanelOnActionClick: false })
|
||||
.catch((err) => console.warn("[AutoLogin] 设置 sidePanel 行为失败:", err));
|
||||
|
||||
migrateLegacyVault();
|
||||
@@ -20,10 +23,43 @@ chrome.runtime.onStartup.addListener(() => {
|
||||
|
||||
chrome.action.onClicked.addListener((tab) => {
|
||||
if (tab?.id !== undefined) {
|
||||
// 点击图标的用户手势已授予 activeTab,此刻 tab.url 一定可读;
|
||||
// 缓存地址供侧边栏后续使用(侧边栏持久的场景下切换标签页后权限会失效)
|
||||
if (tab.url && /^https?:/.test(tab.url)) {
|
||||
chrome.storage.session
|
||||
.set({
|
||||
[LAST_AUTHORIZED_TAB]: {
|
||||
tabId: tab.id,
|
||||
url: tab.url,
|
||||
title: tab.title || "",
|
||||
},
|
||||
})
|
||||
.catch((err) => console.warn("[AutoLogin] 缓存当前标签页地址失败:", err));
|
||||
}
|
||||
chrome.sidePanel.open({ tabId: tab.id }).catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
// 标签页导航 / 关闭后,缓存的地址作废,避免把导航前的旧地址填进表单
|
||||
const LAST_AUTHORIZED_TAB = "lastAuthorizedTab";
|
||||
|
||||
async function clearLastAuthorizedTab(tabId: number): Promise<void> {
|
||||
try {
|
||||
const data = await chrome.storage.session.get(LAST_AUTHORIZED_TAB);
|
||||
const cached = data[LAST_AUTHORIZED_TAB] as { tabId?: number } | undefined;
|
||||
if (cached?.tabId === tabId) {
|
||||
await chrome.storage.session.remove(LAST_AUTHORIZED_TAB);
|
||||
}
|
||||
} catch {
|
||||
// 缓存清理失败不影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
||||
if (changeInfo.url) clearLastAuthorizedTab(tabId);
|
||||
});
|
||||
chrome.tabs.onRemoved.addListener((tabId) => clearLastAuthorizedTab(tabId));
|
||||
|
||||
// 旧版明文数据迁移:把 loginConfigs 转为 environments 明文结构
|
||||
async function migrateLegacyVault() {
|
||||
const data = await chrome.storage.local.get(["environments", "loginConfigs", "vaultMeta"]);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { t } from "../i18n.js";
|
||||
import { resolveAlias, fetchFaviconByDomain } from "../ui-utils.js";
|
||||
import type { DomainConfig } from "../types.js";
|
||||
import { $ } from "./dom";
|
||||
import { getActiveTabInfo } from "./current-tab";
|
||||
import type { PopupCtx } from "./context";
|
||||
|
||||
const editConfigModal = $("editConfigModal");
|
||||
@@ -131,17 +132,14 @@ export function initEditConfigModal(ctx: PopupCtx) {
|
||||
|
||||
// 填入当前标签页域名
|
||||
editFillDomainBtn.addEventListener("click", async () => {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab) {
|
||||
ctx.showToast(t("toastNoTab"));
|
||||
return;
|
||||
}
|
||||
if (!tab.url) {
|
||||
// 侧边栏场景下 activeTab 授权不可靠,统一走 getActiveTabInfo 的缓存兜底
|
||||
const info = await getActiveTabInfo();
|
||||
if (!info) {
|
||||
ctx.showToast(t("toastNoTabUrl"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const hostname = new URL(tab.url).hostname;
|
||||
const hostname = new URL(info.url).hostname;
|
||||
if (!hostname) {
|
||||
ctx.showToast(t("toastNoDomain"));
|
||||
return;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { t } from "../i18n.js";
|
||||
import { resolveAlias, fetchFaviconByDomain } from "../ui-utils.js";
|
||||
import type { DomainConfig } from "../types.js";
|
||||
import { $ } from "./dom";
|
||||
import { getActiveTabInfo } from "./current-tab";
|
||||
import type { PopupCtx } from "./context";
|
||||
|
||||
const domainInput = $("domain");
|
||||
@@ -137,18 +138,14 @@ export function initConfigForm(ctx: PopupCtx) {
|
||||
|
||||
// 填入当前标签页域名
|
||||
fillDomainBtn.addEventListener("click", async () => {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab) {
|
||||
ctx.showToast(t("toastNoTab"));
|
||||
return;
|
||||
}
|
||||
if (!tab.url) {
|
||||
// 未授权时读不到地址:提示用户点扩展图标重新授予 activeTab
|
||||
// 侧边栏场景下 activeTab 授权不可靠,统一走 getActiveTabInfo 的缓存兜底
|
||||
const info = await getActiveTabInfo();
|
||||
if (!info) {
|
||||
ctx.showToast(t("toastNoTabUrl"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const u = new URL(tab.url);
|
||||
const u = new URL(info.url);
|
||||
const host = u.host;
|
||||
if (!host) {
|
||||
ctx.showToast(t("toastNoDomain"));
|
||||
@@ -156,7 +153,7 @@ export function initConfigForm(ctx: PopupCtx) {
|
||||
}
|
||||
const value = u.pathname && u.pathname !== "/" ? host + u.pathname : host;
|
||||
domainInput.value = value;
|
||||
if (tab.title) aliasInput.value = tab.title;
|
||||
if (info.title) aliasInput.value = info.title;
|
||||
saveDraft();
|
||||
ctx.showToast(t("toastDomainFilled", value));
|
||||
} catch {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 取当前活动标签页的 URL / 标题,供"填入当前域名"等按钮使用。
|
||||
*
|
||||
* 本扩展以侧边栏(side panel)承载 popup.html,而 activeTab 是临时权限:
|
||||
* 只有用户点击浏览器工具栏的扩展图标那一瞬间才会授予"当时激活的标签页",
|
||||
* 切换标签页或页面导航后权限即失效,chrome.tabs.query 返回的 tab.url 会是
|
||||
* undefined(扩展也没有 tabs / 站点 host 权限时读不到地址)。
|
||||
*
|
||||
* 因此取址按两级兜底:
|
||||
* 1. 直接读 tab.url —— 刚点过扩展图标、或该站点已授权 host 权限时可用;
|
||||
* 2. 读 background 在"点击图标授权瞬间"缓存到 session storage 的
|
||||
* lastAuthorizedTab —— 仅当缓存的 tabId 与当前活动标签页一致才使用,
|
||||
* 防止把别的标签页/导航前的旧地址填进表单。
|
||||
*/
|
||||
export interface ActiveTabInfo {
|
||||
/** 当前活动标签页 id(用于注入 content script 等场景) */
|
||||
tabId: number;
|
||||
url: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const LAST_AUTHORIZED_TAB = "lastAuthorizedTab";
|
||||
|
||||
export async function getActiveTabInfo(): Promise<ActiveTabInfo | null> {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab || tab.id === undefined) return null;
|
||||
if (tab.url) return { tabId: tab.id, url: tab.url, title: tab.title };
|
||||
|
||||
// 直接读不到:尝试 background 在授权瞬间缓存的地址
|
||||
const data = await chrome.storage.session.get(LAST_AUTHORIZED_TAB);
|
||||
const cached = data[LAST_AUTHORIZED_TAB] as
|
||||
| { tabId?: number; url?: string; title?: string }
|
||||
| undefined;
|
||||
if (cached && cached.tabId === tab.id && cached.url) {
|
||||
return { tabId: tab.id, url: cached.url, title: cached.title };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
+10
-12
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
import { t } from "../i18n.js";
|
||||
import { $ } from "./dom";
|
||||
import { getActiveTabInfo } from "./current-tab";
|
||||
import type { PopupCtx } from "./context";
|
||||
|
||||
/** content script 已注入的标记,定义在 content.ts 顶部 */
|
||||
@@ -73,20 +74,17 @@ export function initFillCurrent(ctx: PopupCtx) {
|
||||
const fillCurrentBtn = $("fillCurrent");
|
||||
|
||||
fillCurrentBtn.addEventListener("click", async () => {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
if (!tab || tab.id === undefined) {
|
||||
ctx.showToast(t("toastNoTab"));
|
||||
return;
|
||||
}
|
||||
if (!tab.url) {
|
||||
// 该标签页没有 activeTab 授权(例如切换标签页后侧边栏仍开着)
|
||||
// 侧边栏场景下 activeTab 授权不可靠,统一走 getActiveTabInfo 的缓存兜底
|
||||
const info = await getActiveTabInfo();
|
||||
if (!info) {
|
||||
ctx.showToast(t("toastNoTabUrl"));
|
||||
return;
|
||||
}
|
||||
const tabId = info.tabId;
|
||||
|
||||
// 跨所有环境查找匹配当前标签页的配置
|
||||
const allConfigs = await EnvStore.loadAllConfigs();
|
||||
const matched = allConfigs.filter((c) => isDomainMatch(tab.url, c.domain));
|
||||
const matched = allConfigs.filter((c) => isDomainMatch(info.url, c.domain));
|
||||
|
||||
if (matched.length === 0) {
|
||||
ctx.showToast(t("toastNoMatch"));
|
||||
@@ -95,8 +93,8 @@ export function initFillCurrent(ctx: PopupCtx) {
|
||||
|
||||
if (matched.length === 1) {
|
||||
try {
|
||||
const frameId = await ensureContentScript(tab.id);
|
||||
const resp: any = await sendToTab(tab.id, { action: "manualFill", config: matched[0] }, frameId);
|
||||
const frameId = await ensureContentScript(tabId);
|
||||
const resp: any = await sendToTab(tabId, { action: "manualFill", config: matched[0] }, frameId);
|
||||
if (resp && (resp as any).success) {
|
||||
ctx.showToast(t("toastFilled"));
|
||||
} else if (resp && resp.diag) {
|
||||
@@ -112,8 +110,8 @@ export function initFillCurrent(ctx: PopupCtx) {
|
||||
}
|
||||
|
||||
try {
|
||||
const frameId = await ensureContentScript(tab.id);
|
||||
await sendToTab(tab.id, { action: "manualFillMulti", configs: matched, tabUrl: tab.url }, frameId);
|
||||
const frameId = await ensureContentScript(tabId);
|
||||
await sendToTab(tabId, { action: "manualFillMulti", configs: matched, tabUrl: info.url }, frameId);
|
||||
ctx.showToast(t("toastMultiMatch", String(matched.length)));
|
||||
} catch (err) {
|
||||
toastSendError(ctx, err);
|
||||
|
||||
Reference in New Issue
Block a user