Skip to content

Commit c4e81a7

Browse files
authored
chore: sync extension files from sentience-chrome v2.3.0
1 parent f9002ab commit c4e81a7

File tree

7 files changed

+1286
-3074
lines changed

7 files changed

+1286
-3074
lines changed

sentience/extension/background.js

Lines changed: 85 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -1,240 +1,104 @@
11
import init, { analyze_page_with_options, analyze_page, prune_for_api } from "../pkg/sentience_core.js";
22

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

6-
7-
console.log('[Sentience Background] Initializing...');
8-
9-
// Global WASM initialization state
10-
let wasmReady = false;
11-
let wasmInitPromise = null;
12-
13-
/**
14-
* Initialize WASM module - called once on service worker startup
15-
* Uses static imports (not dynamic import()) which is required for Service Workers
16-
*/
175
async function initWASM() {
18-
if (wasmReady) return;
19-
if (wasmInitPromise) return wasmInitPromise;
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+
}
2014

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

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

204-
// Prune elements for API (prevents 413 errors on large sites)
205-
let prunedRawData;
65+
initWASM().catch(err => {}), chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
20666
try {
207-
prunedRawData = prune_for_api(rawData);
208-
} catch (e) {
209-
console.warn('[Sentience Background] prune_for_api failed, using original data:', e);
210-
prunedRawData = rawData;
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;
21199
}
212-
213-
const duration = performance.now() - startTime;
214-
console.log(
215-
`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)`
216-
);
217-
218-
return {
219-
elements: analyzedElements,
220-
raw_elements: prunedRawData,
221-
};
222-
} catch (error) {
223-
const duration = performance.now() - startTime;
224-
console.error(`[Sentience Background] Processing error after ${duration.toFixed(1)}ms:`, error);
225-
throw error;
226-
}
227-
}
228-
229-
console.log('[Sentience Background] Service worker ready');
230-
231-
// Global error handlers to prevent extension crashes
232-
self.addEventListener('error', (event) => {
233-
console.error('[Sentience Background] Global error caught:', event.error);
234-
event.preventDefault(); // Prevent extension crash
235-
});
236-
237-
self.addEventListener('unhandledrejection', (event) => {
238-
console.error('[Sentience Background] Unhandled promise rejection:', event.reason);
239-
event.preventDefault(); // Prevent extension crash
240-
});
100+
}), self.addEventListener("error", event => {
101+
event.preventDefault();
102+
}), self.addEventListener("unhandledrejection", event => {
103+
event.preventDefault();
104+
});

0 commit comments

Comments
 (0)