-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathbackground.js
More file actions
176 lines (154 loc) · 6.62 KB
/
Copy pathbackground.js
File metadata and controls
176 lines (154 loc) · 6.62 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// background.js
// Scans only active sites. Fetches internal .js files from the page context (avoids CORS).
chrome.webNavigation.onCompleted.addListener(async (details) => {
try {
if (details.frameId !== 0) return;
if (!details.url.startsWith("http")) return;
const domain = new URL(details.url).hostname;
// read activeSites
const { activeSites = {} } = await new Promise((res) =>
chrome.storage.local.get("activeSites", res)
);
if (!activeSites[domain]) return;
// read config
chrome.storage.local.get(
["keywords", "notifyMode", "foundResults", "maxLinks", "scanInlineScripts"], // TOGGLE CHECK
async (data) => {
try {
const keywords = data.keywords || [];
const notifyMode = data.notifyMode || "notification";
const maxLinks = data.maxLinks || "10";
const scanInline = data.scanInlineScripts ?? true; // TOGGLE CHECK
let foundResults = data.foundResults || [];
if (notifyMode === "disabled") return;
if (!keywords || keywords.length === 0) return;
let found = [];
// --- (1) Scan raw HTML ---
try {
let res = await fetch(details.url);
let text = await res.text();
for (let kw of keywords) {
findMatches(text, details.url, kw, found);
}
} catch (e) {
console.error("HTML_search fetch failed (page html):", details.url, e);
}
// --- (2) Run code in page context ---
const [{ result: dom }] = await chrome.scripting.executeScript({
target: { tabId: details.tabId },
func: async () => {
await new Promise((r) => setTimeout(r, 1000));
const base = window.location.origin;
const domain = window.location.hostname;
const anchors = [...document.querySelectorAll("a[href]")];
const links = anchors.map((a) => a.href).filter((h) => typeof h === "string" && h.startsWith(base));
const scripts = [...document.querySelectorAll("script[src]")]
.map((s) => s.src)
.filter((s) => {
try { return new URL(s).hostname === domain; }
catch (e) { return false; }
});
const inlineScripts = [...document.querySelectorAll("script:not([src])")]
.map((s) => s.innerText)
.filter(Boolean);
const scriptContents = await Promise.all(
scripts.map(async (src) => {
try {
const r = await fetch(src, { cache: "no-cache" });
const txt = await r.text();
return { src, text: txt };
} catch (err) {
return { src, text: null, error: String(err) };
}
})
);
return { html: document.documentElement.outerHTML, links: [...new Set(links)], scripts: [...new Set(scripts)], inlineScripts, scriptContents };
},
});
// --- (2b) Scan DOM HTML ---
for (let kw of keywords) {
findMatches(dom.html || "", details.url, kw, found);
}
// --- (3) Scan internal script contents ---
if (Array.isArray(dom.scriptContents)) {
for (const s of dom.scriptContents) {
if (s && s.text) {
for (let kw of keywords) {
findMatches(s.text, s.src, kw, found);
}
} else {
console.error("HTML_search: failed to fetch script:", s && s.src, s && s.error);
}
}
}
// --- (3b) Scan inline JS code (TOGGLE CHECK) ---
if (scanInline && Array.isArray(dom.inlineScripts)) {
for (const scriptContent of dom.inlineScripts) {
for (let kw of keywords) {
findMatches(scriptContent, details.url + " [inline script]", kw, found);
}
}
}
// --- (4) Scan internal links ---
const linkLimit = maxLinks === "all" ? dom.links.length : parseInt(maxLinks, 10) || 0;
for (let i = 0; i < Math.min(dom.links.length, linkLimit); i++) {
try {
let res = await fetch(dom.links[i]);
let text = await res.text();
for (let kw of keywords) {
findMatches(text, dom.links[i], kw, found);
}
} catch (e) {
console.error("HTML_search link fetch failed:", dom.links[i], e);
}
}
// --- (5) Save unique results + notify ---
if (found.length > 0) {
found.forEach((f) => {
const exists = foundResults.some(
(r) => r.url === f.url && r.keyword === f.keyword && r.lineNum === f.lineNum
);
if (!exists) foundResults.push(f);
});
chrome.storage.local.set({ foundResults });
if (notifyMode !== "disabled") {
let message = found.slice(0, 3).map((f) => `${f.keyword} @ ${f.url}`).join("\n");
if (found.length > 3) message += `\n+${found.length - 3} more...`;
if (notifyMode === "notification") {
chrome.notifications.create({
type: "basic",
iconUrl: "icon.png",
title: "HTML_search found matches!",
message: message || `${found.length} matches found.`,
priority: 2,
requireInteraction: true,
});
} else if (notifyMode === "alert") {
chrome.scripting.executeScript({
target: { tabId: details.tabId },
func: (msg) => alert("HTML_search:\n" + msg),
args: [message],
});
}
}
}
} catch (err) {
console.error("HTML_search autorun inner error:", err);
}
}
);
} catch (err) {
console.error("HTML_search onCompleted outer error:", err);
}
});
// simple line-by-line match helper
function findMatches(source, url, keyword, results) {
if (!source || !keyword) return;
const lines = source.split("\n");
const lowKw = keyword.toLowerCase();
lines.forEach((line, i) => {
if (line.toLowerCase().includes(lowKw)) {
results.push({ url, keyword, line: line.trim(), lineNum: i + 1 });
}
});
}