Skip to content

Commit 0f1f821

Browse files
author
SentienceDEV
committed
fix extension path
1 parent 135f268 commit 0f1f821

File tree

3 files changed

+3375
-1391
lines changed

3 files changed

+3375
-1391
lines changed

sentience/extension/background.js

Lines changed: 224 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,242 @@
1-
import init, { analyze_page_with_options, analyze_page, prune_for_api } from "../pkg/sentience_core.js";
1+
// Sentience Chrome Extension - Background Service Worker
2+
// Auto-generated from modular source
3+
import init, { analyze_page_with_options, analyze_page, prune_for_api } from './pkg/sentience_core.js';
24

3-
let wasmReady = !1, wasmInitPromise = null;
5+
// background.js - Service Worker with WASM (CSP-Immune!)
6+
// This runs in an isolated environment, completely immune to page CSP policies
47

8+
9+
console.log('[Sentience Background] Initializing...');
10+
11+
// Global WASM initialization state
12+
let wasmReady = false;
13+
let wasmInitPromise = null;
14+
15+
/**
16+
* Initialize WASM module - called once on service worker startup
17+
* Uses static imports (not dynamic import()) which is required for Service Workers
18+
*/
519
async function initWASM() {
6-
if (!wasmReady) return wasmInitPromise || (wasmInitPromise = (async () => {
7-
try {
8-
globalThis.js_click_element = () => {}, await init(), wasmReady = !0;
9-
} catch (error) {
10-
throw error;
11-
}
12-
})(), wasmInitPromise);
13-
}
20+
if (wasmReady) return;
21+
if (wasmInitPromise) return wasmInitPromise;
1422

15-
async function handleScreenshotCapture(_tabId, options = {}) {
23+
wasmInitPromise = (async () => {
1624
try {
17-
const {format: format = "png", quality: quality = 90} = options;
18-
return await chrome.tabs.captureVisibleTab(null, {
19-
format: format,
20-
quality: quality
21-
});
25+
console.log('[Sentience Background] Loading WASM module...');
26+
27+
// Define the js_click_element function that WASM expects
28+
// In Service Workers, use 'globalThis' instead of 'window'
29+
// In background context, we can't actually click, so we log a warning
30+
globalThis.js_click_element = () => {
31+
console.warn('[Sentience Background] js_click_element called in background (ignored)');
32+
};
33+
34+
// Initialize WASM - this calls the init() function from the static import
35+
// The init() function handles fetching and instantiating the .wasm file
36+
await init();
37+
38+
wasmReady = true;
39+
console.log('[Sentience Background] ✓ WASM ready!');
40+
console.log(
41+
'[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api'
42+
);
2243
} catch (error) {
23-
throw new Error(`Failed to capture screenshot: ${error.message}`);
44+
console.error('[Sentience Background] WASM initialization failed:', error);
45+
throw error;
2446
}
47+
})();
48+
49+
return wasmInitPromise;
2550
}
2651

52+
// Initialize WASM on service worker startup
53+
initWASM().catch((err) => {
54+
console.error('[Sentience Background] Failed to initialize WASM:', err);
55+
});
56+
57+
/**
58+
* Message handler for all extension communication
59+
* Includes global error handling to prevent extension crashes
60+
*/
61+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
62+
// Global error handler to prevent extension crashes
63+
try {
64+
// Handle screenshot requests (existing functionality)
65+
if (request.action === 'captureScreenshot') {
66+
handleScreenshotCapture(sender.tab.id, request.options)
67+
.then((screenshot) => {
68+
sendResponse({ success: true, screenshot });
69+
})
70+
.catch((error) => {
71+
console.error('[Sentience Background] Screenshot capture failed:', error);
72+
sendResponse({
73+
success: false,
74+
error: error.message || 'Screenshot capture failed',
75+
});
76+
});
77+
return true; // Async response
78+
}
79+
80+
// Handle WASM processing requests (NEW!)
81+
if (request.action === 'processSnapshot') {
82+
handleSnapshotProcessing(request.rawData, request.options)
83+
.then((result) => {
84+
sendResponse({ success: true, result });
85+
})
86+
.catch((error) => {
87+
console.error('[Sentience Background] Snapshot processing failed:', error);
88+
sendResponse({
89+
success: false,
90+
error: error.message || 'Snapshot processing failed',
91+
});
92+
});
93+
return true; // Async response
94+
}
95+
96+
// Unknown action
97+
console.warn('[Sentience Background] Unknown action:', request.action);
98+
sendResponse({ success: false, error: 'Unknown action' });
99+
return false;
100+
} catch (error) {
101+
// Catch any synchronous errors that might crash the extension
102+
console.error('[Sentience Background] Fatal error in message handler:', error);
103+
try {
104+
sendResponse({
105+
success: false,
106+
error: `Fatal error: ${error.message || 'Unknown error'}`,
107+
});
108+
} catch (e) {
109+
// If sendResponse already called, ignore
110+
}
111+
return false;
112+
}
113+
});
114+
115+
/**
116+
* Handle screenshot capture (existing functionality)
117+
*/
118+
async function handleScreenshotCapture(_tabId, options = {}) {
119+
try {
120+
const { format = 'png', quality = 90 } = options;
121+
122+
const dataUrl = await chrome.tabs.captureVisibleTab(null, {
123+
format,
124+
quality,
125+
});
126+
127+
console.log(
128+
`[Sentience Background] Screenshot captured: ${format}, size: ${dataUrl.length} bytes`
129+
);
130+
return dataUrl;
131+
} catch (error) {
132+
console.error('[Sentience Background] Screenshot error:', error);
133+
throw new Error(`Failed to capture screenshot: ${error.message}`);
134+
}
135+
}
136+
137+
/**
138+
* Handle snapshot processing with WASM (NEW!)
139+
* This is where the magic happens - completely CSP-immune!
140+
* Includes safeguards to prevent crashes and hangs.
141+
*
142+
* @param {Array} rawData - Raw element data from injected_api.js
143+
* @param {Object} options - Snapshot options (limit, filter, etc.)
144+
* @returns {Promise<Object>} Processed snapshot result
145+
*/
27146
async function handleSnapshotProcessing(rawData, options = {}) {
28-
const startTime = performance.now();
147+
const MAX_ELEMENTS = 10000; // Safety limit to prevent hangs
148+
const startTime = performance.now();
149+
150+
try {
151+
// Safety check: limit element count to prevent hangs
152+
if (!Array.isArray(rawData)) {
153+
throw new Error('rawData must be an array');
154+
}
155+
156+
if (rawData.length > MAX_ELEMENTS) {
157+
console.warn(
158+
`[Sentience Background] ⚠️ Large dataset: ${rawData.length} elements. Limiting to ${MAX_ELEMENTS} to prevent hangs.`
159+
);
160+
rawData = rawData.slice(0, MAX_ELEMENTS);
161+
}
162+
163+
// Ensure WASM is initialized
164+
await initWASM();
165+
if (!wasmReady) {
166+
throw new Error('WASM module not initialized');
167+
}
168+
169+
console.log(
170+
`[Sentience Background] Processing ${rawData.length} elements with options:`,
171+
options
172+
);
173+
174+
// Run WASM processing using the imported functions directly
175+
// Wrap in try-catch with timeout protection
176+
let analyzedElements;
29177
try {
30-
if (!Array.isArray(rawData)) throw new Error("rawData must be an array");
31-
if (rawData.length > 1e4 && (rawData = rawData.slice(0, 1e4)), await initWASM(),
32-
!wasmReady) throw new Error("WASM module not initialized");
33-
let analyzedElements, prunedRawData;
34-
try {
35-
const wasmPromise = new Promise((resolve, reject) => {
36-
try {
37-
let result;
38-
result = options.limit || options.filter ? analyze_page_with_options(rawData, options) : analyze_page(rawData),
39-
resolve(result);
40-
} catch (e) {
41-
reject(e);
42-
}
43-
});
44-
analyzedElements = await Promise.race([ wasmPromise, new Promise((_, reject) => setTimeout(() => reject(new Error("WASM processing timeout (>18s)")), 18e3)) ]);
45-
} catch (e) {
46-
const errorMsg = e.message || "Unknown WASM error";
47-
throw new Error(`WASM analyze_page failed: ${errorMsg}`);
48-
}
178+
// Use a timeout wrapper to prevent infinite hangs
179+
const wasmPromise = new Promise((resolve, reject) => {
49180
try {
50-
prunedRawData = prune_for_api(rawData);
181+
let result;
182+
if (options.limit || options.filter) {
183+
result = analyze_page_with_options(rawData, options);
184+
} else {
185+
result = analyze_page(rawData);
186+
}
187+
resolve(result);
51188
} catch (e) {
52-
prunedRawData = rawData;
189+
reject(e);
53190
}
54-
performance.now();
55-
return {
56-
elements: analyzedElements,
57-
raw_elements: prunedRawData
58-
};
59-
} catch (error) {
60-
performance.now();
61-
throw error;
191+
});
192+
193+
// Add timeout protection (18 seconds - less than content.js timeout)
194+
analyzedElements = await Promise.race([
195+
wasmPromise,
196+
new Promise((_, reject) =>
197+
setTimeout(() => reject(new Error('WASM processing timeout (>18s)')), 18000)
198+
),
199+
]);
200+
} catch (e) {
201+
const errorMsg = e.message || 'Unknown WASM error';
202+
console.error(`[Sentience Background] WASM analyze_page failed: ${errorMsg}`, e);
203+
throw new Error(`WASM analyze_page failed: ${errorMsg}`);
62204
}
63-
}
64205

65-
initWASM().catch(err => {}), chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
206+
// Prune elements for API (prevents 413 errors on large sites)
207+
let prunedRawData;
66208
try {
67-
return "captureScreenshot" === request.action ? (handleScreenshotCapture(sender.tab.id, request.options).then(screenshot => {
68-
sendResponse({
69-
success: !0,
70-
screenshot: screenshot
71-
});
72-
}).catch(error => {
73-
sendResponse({
74-
success: !1,
75-
error: error.message || "Screenshot capture failed"
76-
});
77-
}), !0) : "processSnapshot" === request.action ? (handleSnapshotProcessing(request.rawData, request.options).then(result => {
78-
sendResponse({
79-
success: !0,
80-
result: result
81-
});
82-
}).catch(error => {
83-
sendResponse({
84-
success: !1,
85-
error: error.message || "Snapshot processing failed"
86-
});
87-
}), !0) : (sendResponse({
88-
success: !1,
89-
error: "Unknown action"
90-
}), !1);
91-
} catch (error) {
92-
try {
93-
sendResponse({
94-
success: !1,
95-
error: `Fatal error: ${error.message || "Unknown error"}`
96-
});
97-
} catch (e) {}
98-
return !1;
209+
prunedRawData = prune_for_api(rawData);
210+
} catch (e) {
211+
console.warn('[Sentience Background] prune_for_api failed, using original data:', e);
212+
prunedRawData = rawData;
99213
}
100-
}), self.addEventListener("error", event => {
101-
event.preventDefault();
102-
}), self.addEventListener("unhandledrejection", event => {
103-
event.preventDefault();
104-
});
214+
215+
const duration = performance.now() - startTime;
216+
console.log(
217+
`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)`
218+
);
219+
220+
return {
221+
elements: analyzedElements,
222+
raw_elements: prunedRawData,
223+
};
224+
} catch (error) {
225+
const duration = performance.now() - startTime;
226+
console.error(`[Sentience Background] Processing error after ${duration.toFixed(1)}ms:`, error);
227+
throw error;
228+
}
229+
}
230+
231+
console.log('[Sentience Background] Service worker ready');
232+
233+
// Global error handlers to prevent extension crashes
234+
self.addEventListener('error', (event) => {
235+
console.error('[Sentience Background] Global error caught:', event.error);
236+
event.preventDefault(); // Prevent extension crash
237+
});
238+
239+
self.addEventListener('unhandledrejection', (event) => {
240+
console.error('[Sentience Background] Unhandled promise rejection:', event.reason);
241+
event.preventDefault(); // Prevent extension crash
242+
});

0 commit comments

Comments
 (0)