Repository navigation
Expand file tree
/
Copy pathblocked.js
More file actions
156 lines (136 loc) · 5.05 KB
/
Copy pathblocked.js
File metadata and controls
156 lines (136 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
const { isValidRuleId } = globalThis.NoloRuleUtils;
// 无法获取原始地址时的占位文字
const BLOCKED_REQUEST_MISSING_MESSAGE = "暂时没有拿到本次拦截的原始地址";
// sessionStorage 缓存键前缀(完整键为 prefix:tabId:ruleId,避免跨规则污染)
const BLOCKED_PAGE_CACHE_KEY = "blockedPageRequestContext";
const matchedFilter = document.getElementById("matchedFilter");
const blockedUrl = document.getElementById("blockedUrl");
const blockedHint = document.getElementById("blockedHint");
initialize().catch((error) => {
console.error("[Nolo] Initialization failed", error);
renderFallback(
"页面初始化遇到错误",
"未捕获的扩展内部错误导致执行中断。",
);
});
// 页面初始化入口,所有未捕获错误均降级到 renderFallback
async function initialize() {
const ruleId = getRuleIdFromQuery();
if (ruleId === null) {
renderFallback(
"当前页面没有拿到 ruleId,无法定位命中的规则。",
"当前版本无法确认本次拦截对应的规则。",
);
return;
}
const currentTab = await getCurrentTab();
const tabId = Number.isInteger(currentTab?.id) && currentTab.id >= 0 ? currentTab.id : null;
// 并发获取命中规则信息和被拦截请求上下文,减少串行等待时间
const [rule, requestContext] = await Promise.all([
findRuleByRuleId(ruleId).catch((error) => {
console.warn("[Nolo] findRuleByRuleId error", error);
return null;
}),
tabId !== null
? consumeBlockedRequest(tabId).catch((error) => {
console.warn("[Nolo] consumeBlockedRequest error", error);
return null;
})
: Promise.resolve(null),
]);
// 优先使用 background 实时提供的上下文,其次 fallback 到 sessionStorage 缓存
// (缓存用于页面刷新场景,此时 background 的「阅后即焚」数据已被清除)
const effectiveRequestContext =
requestContext || (tabId !== null ? loadCachedRequestContext(tabId, ruleId) : null);
if (requestContext && tabId !== null) {
cacheRequestContext(tabId, ruleId, requestContext);
}
matchedFilter.textContent =
rule?.pattern || rule?.urlFilter || "未找到对应的规则";
blockedUrl.textContent =
effectiveRequestContext?.requestUrl || BLOCKED_REQUEST_MISSING_MESSAGE;
blockedHint.textContent = requestContext
? "原始地址来自 background 记录的最近一次被重定向请求。"
: effectiveRequestContext
? "原始地址来自当前拦截页缓存,刷新页面后仍可继续显示。"
: "如果这里没有显示地址,重新触发一次拦截即可刷新上下文。";
}
// 从当前页面 URL 的 query string 中提取 ruleId
function getRuleIdFromQuery() {
const searchParams = new URLSearchParams(window.location.search);
const ruleId = Number(searchParams.get("ruleId"));
return isValidRuleId(ruleId) ? ruleId : null;
}
// 向 background 查询规则信息,复用其内存缓存,避免直接读 storage
async function findRuleByRuleId(ruleId) {
const response = await sendMessage({ type: "GET_RULE_BY_ID", ruleId });
if (!response?.ok) return null;
return response.rule ?? response.site ?? null;
}
// 向 background 请求消费一次拦截上下文(阅后即焚,重复请求会返回 null)
async function consumeBlockedRequest(tabId) {
const response = await sendMessage({
type: "CONSUME_BLOCKED_REQUEST",
tabId,
});
if (!response?.ok) {
return null;
}
return response.requestContext || null;
}
// 获取当前标签页对象,用于提取 tabId
async function getCurrentTab() {
try {
return await chrome.tabs.getCurrent();
} catch {
return null;
}
}
// 初始化失败时的降级渲染,直接展示错误原因
function renderFallback(filterMessage, hintMessage) {
matchedFilter.textContent = filterMessage;
blockedUrl.textContent = BLOCKED_REQUEST_MISSING_MESSAGE;
blockedHint.textContent = hintMessage;
}
// 将拦截上下文写入 sessionStorage,键为 tabId:ruleId 复合键
// 防止同一标签页内不同规则的缓存互相污染
function cacheRequestContext(tabId, ruleId, requestContext) {
if (!requestContext || typeof requestContext.requestUrl !== "string") {
return;
}
try {
sessionStorage.setItem(
`${BLOCKED_PAGE_CACHE_KEY}:${tabId}:${ruleId}`,
requestContext.requestUrl,
);
} catch (error) {
console.warn("[Nolo] Failed to cache blocked request context", error);
}
}
// 从 sessionStorage 读取缓存的拦截上下文(刷新页面后的 fallback)
function loadCachedRequestContext(tabId, ruleId) {
try {
const requestUrl = sessionStorage.getItem(
`${BLOCKED_PAGE_CACHE_KEY}:${tabId}:${ruleId}`,
);
if (!requestUrl) {
return null;
}
return { requestUrl };
} catch (error) {
console.warn("[Nolo] Failed to read cached blocked request context", error);
return null;
}
}
// 将 chrome.runtime.sendMessage 包装为 Promise,统一错误处理
function sendMessage(message) {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(message, (response) => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
return;
}
resolve(response);
});
});
}