fix: 修复右键菜单功能的多个逻辑漏洞
1. 修复时区硬编码问题 - 使用用户选择的时区而非固定 Asia/Shanghai 2. 修复 openPopup() 失败后数据残留 - 失败时清除 storage 中的待处理数据 3. 统一数据过期时间常量 - 导出 CONTEXT_MENU_DATA_EXPIRY_MS 并统一使用 4. 修复 featureKey 类型断言不安全 - 使用映射表处理非常规菜单 ID
This commit is contained in:
@@ -41,6 +41,8 @@ export default defineBackground(() => {
|
||||
await browser.action.openPopup();
|
||||
} catch (err) {
|
||||
console.error('[Context Menu] 打开 popup 失败:', err);
|
||||
// 打开失败时清除残留数据,避免下次打开 popup 时误触发
|
||||
await chrome.storage.local.remove('contextMenu/pendingData');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -74,7 +74,8 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
|
||||
}, [mode, tsInput, dtInput, unit, zone, t]);
|
||||
|
||||
// 处理右键菜单传递的数据
|
||||
const handleContextMenuData = useCallback((payload: string) => {
|
||||
const handleContextMenuData = useCallback(
|
||||
(payload: string) => {
|
||||
const trimmed = payload.trim();
|
||||
if (isTimestampLike(trimmed)) {
|
||||
// 看起来是时间戳,切换到 ts2dt 模式
|
||||
@@ -89,7 +90,7 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
|
||||
const d = detectedUnit === 'ms' ? dayjs(num) : dayjs.unix(num);
|
||||
if (d.isValid()) {
|
||||
setError('');
|
||||
setResult(d.tz('Asia/Shanghai').format(DATE_FORMAT));
|
||||
setResult(d.tz(zone).format(DATE_FORMAT));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -108,7 +109,9 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
|
||||
setTsInput(trimmed);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[zone],
|
||||
);
|
||||
|
||||
useContextMenuData({ featureKey: 'timestamp', onData: handleContextMenuData });
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
getDefaultPageOrder,
|
||||
getDefaultVisibleFeatureKeys,
|
||||
} from '@/config/features';
|
||||
import { saveContextMenuData } from '@/utils/useContextMenuData';
|
||||
import { saveContextMenuData, CONTEXT_MENU_DATA_EXPIRY_MS } from '@/utils/useContextMenuData';
|
||||
|
||||
/**
|
||||
* 校验是否为合法的页面类型
|
||||
@@ -215,7 +215,7 @@ export function RouterProvider({
|
||||
if (
|
||||
pendingData &&
|
||||
isValidPage(pendingData.featureKey) &&
|
||||
Date.now() - pendingData.timestamp < 5000
|
||||
Date.now() - pendingData.timestamp < CONTEXT_MENU_DATA_EXPIRY_MS
|
||||
) {
|
||||
navigateTo(pendingData.featureKey as PageType);
|
||||
// 不在这里清除数据,让目标页面的 useContextMenuData 来消费和清除
|
||||
@@ -292,7 +292,11 @@ export function RouterProvider({
|
||||
if (changes['contextMenu/pendingData']) {
|
||||
const newData = changes['contextMenu/pendingData']
|
||||
.newValue as ContextMenuPendingData | null;
|
||||
if (newData && isValidPage(newData.featureKey) && Date.now() - newData.timestamp < 5000) {
|
||||
if (
|
||||
newData &&
|
||||
isValidPage(newData.featureKey) &&
|
||||
Date.now() - newData.timestamp < CONTEXT_MENU_DATA_EXPIRY_MS
|
||||
) {
|
||||
setCurrentPage(newData.featureKey as PageType);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-8
@@ -22,6 +22,19 @@ const PARENT_MENU_ID = 'testing-tools-parent';
|
||||
|
||||
export const MAX_PAYLOAD_LENGTH = 10000;
|
||||
|
||||
/** 菜单项 ID 到 PageType 的映射(仅处理非常规映射) */
|
||||
const MENU_ID_TO_PAGE_TYPE: Record<string, PageType> = {
|
||||
'qrCode-page': 'qrCode',
|
||||
};
|
||||
|
||||
/**
|
||||
* 将菜单项 ID 转换为 PageType
|
||||
* 如果存在显式映射则使用映射,否则直接使用 menuItemId
|
||||
*/
|
||||
function getMenuPageType(menuItemId: string): PageType {
|
||||
return MENU_ID_TO_PAGE_TYPE[menuItemId] ?? (menuItemId as PageType);
|
||||
}
|
||||
|
||||
export const CONTEXT_MENU_CONFIGS: ContextMenuItemConfig[] = [
|
||||
{
|
||||
id: PARENT_MENU_ID,
|
||||
@@ -81,14 +94,7 @@ export function parseContextMenuClick(
|
||||
menuItemId: string,
|
||||
info: chrome.contextMenus.OnClickData,
|
||||
): ParseResult {
|
||||
const featureKey = menuItemId as PageType;
|
||||
|
||||
if (menuItemId === 'qrCode-page') {
|
||||
return {
|
||||
success: true,
|
||||
data: { featureKey: 'qrCode', payload: info.pageUrl || '' },
|
||||
};
|
||||
}
|
||||
const featureKey = getMenuPageType(menuItemId);
|
||||
|
||||
if (info.selectionText) {
|
||||
const text = info.selectionText;
|
||||
|
||||
@@ -3,7 +3,9 @@ import { storageUtil } from '@/utils/chromeStorage';
|
||||
import type { ContextMenuPendingData, PageType } from '@/types/storage';
|
||||
|
||||
const STORAGE_KEY = 'contextMenu/pendingData' as const;
|
||||
const DATA_EXPIRY_MS = 5000;
|
||||
|
||||
/** 右键菜单数据过期时间(毫秒) */
|
||||
export const CONTEXT_MENU_DATA_EXPIRY_MS = 5000;
|
||||
|
||||
export interface UseContextMenuDataOptions {
|
||||
/** 当前页面的功能标识 */
|
||||
@@ -29,7 +31,7 @@ export function useContextMenuData({ featureKey, onData }: UseContextMenuDataOpt
|
||||
|
||||
if (data.featureKey !== featureKey) return;
|
||||
|
||||
if (Date.now() - data.timestamp > DATA_EXPIRY_MS) {
|
||||
if (Date.now() - data.timestamp > CONTEXT_MENU_DATA_EXPIRY_MS) {
|
||||
await storageUtil.remove(STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user