Skip to content

Commit d448964

Browse files
author
Sentience Dev
committed
Merge pull request #52 from SentienceAPI/sync-extension-v2.0.0
Sync Extension: v2.0.0
2 parents 2ab588f + 6c126e5 commit d448964

File tree

6 files changed

+451
-314
lines changed

6 files changed

+451
-314
lines changed

sentience/extension/background.js

Lines changed: 159 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,170 @@
1-
// background.js - Service Worker for screenshot capture
2-
// Chrome extensions can only capture screenshots from the background script
3-
// Listen for screenshot requests from content script
1+
// background.js - Service Worker with WASM (CSP-Immune!)
2+
// This runs in an isolated environment, completely immune to page CSP policies
3+
4+
// ✅ STATIC IMPORTS at top level - Required for Service Workers!
5+
// Dynamic import() is FORBIDDEN in ServiceWorkerGlobalScope
6+
import init, { analyze_page, analyze_page_with_options, prune_for_api } from './pkg/sentience_core.js';
7+
8+
console.log('[Sentience Background] Initializing...');
9+
10+
// Global WASM initialization state
11+
let wasmReady = false;
12+
let wasmInitPromise = null;
13+
14+
/**
15+
* Initialize WASM module - called once on service worker startup
16+
* Uses static imports (not dynamic import()) which is required for Service Workers
17+
*/
18+
async function initWASM() {
19+
if (wasmReady) return;
20+
if (wasmInitPromise) return wasmInitPromise;
21+
22+
wasmInitPromise = (async () => {
23+
try {
24+
console.log('[Sentience Background] Loading WASM module...');
25+
26+
// Define the js_click_element function that WASM expects
27+
// In Service Workers, use 'globalThis' instead of 'window'
28+
// In background context, we can't actually click, so we log a warning
29+
globalThis.js_click_element = (_id) => {
30+
console.warn('[Sentience Background] js_click_element called in background (ignored)');
31+
};
32+
33+
// Initialize WASM - this calls the init() function from the static import
34+
// The init() function handles fetching and instantiating the .wasm file
35+
await init();
36+
37+
wasmReady = true;
38+
console.log('[Sentience Background] ✓ WASM ready!');
39+
console.log('[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api');
40+
} catch (error) {
41+
console.error('[Sentience Background] WASM initialization failed:', error);
42+
throw error;
43+
}
44+
})();
45+
46+
return wasmInitPromise;
47+
}
48+
49+
// Initialize WASM on service worker startup
50+
initWASM().catch(err => {
51+
console.error('[Sentience Background] Failed to initialize WASM:', err);
52+
});
53+
54+
/**
55+
* Message handler for all extension communication
56+
*/
457
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
5-
if (request.action === 'captureScreenshot') {
6-
handleScreenshotCapture(sender.tab.id, request.options)
7-
.then(screenshot => {
8-
sendResponse({ success: true, screenshot });
9-
})
10-
.catch(error => {
11-
console.error('[Sentience] Screenshot capture failed:', error);
12-
sendResponse({
13-
success: false,
14-
error: error.message || 'Screenshot capture failed'
15-
});
16-
});
58+
// Handle screenshot requests (existing functionality)
59+
if (request.action === 'captureScreenshot') {
60+
handleScreenshotCapture(sender.tab.id, request.options)
61+
.then(screenshot => {
62+
sendResponse({ success: true, screenshot });
63+
})
64+
.catch(error => {
65+
console.error('[Sentience Background] Screenshot capture failed:', error);
66+
sendResponse({
67+
success: false,
68+
error: error.message || 'Screenshot capture failed'
69+
});
70+
});
71+
return true; // Async response
72+
}
1773

18-
// Return true to indicate we'll send response asynchronously
19-
return true;
20-
}
74+
// Handle WASM processing requests (NEW!)
75+
if (request.action === 'processSnapshot') {
76+
handleSnapshotProcessing(request.rawData, request.options)
77+
.then(result => {
78+
sendResponse({ success: true, result });
79+
})
80+
.catch(error => {
81+
console.error('[Sentience Background] Snapshot processing failed:', error);
82+
sendResponse({
83+
success: false,
84+
error: error.message || 'Snapshot processing failed'
85+
});
86+
});
87+
return true; // Async response
88+
}
89+
90+
// Unknown action
91+
console.warn('[Sentience Background] Unknown action:', request.action);
92+
sendResponse({ success: false, error: 'Unknown action' });
93+
return false;
2194
});
2295

2396
/**
24-
* Capture screenshot of the active tab
25-
* @param {number} tabId - Tab ID to capture
26-
* @param {Object} options - Screenshot options
27-
* @returns {Promise<string>} Base64-encoded PNG data URL
97+
* Handle screenshot capture (existing functionality)
2898
*/
29-
async function handleScreenshotCapture(tabId, options = {}) {
30-
try {
31-
const {
32-
format = 'png', // 'png' or 'jpeg'
33-
quality = 90 // JPEG quality (0-100), ignored for PNG
34-
} = options;
35-
36-
// Capture visible tab as data URL
37-
const dataUrl = await chrome.tabs.captureVisibleTab(null, {
38-
format: format,
39-
quality: quality
40-
});
41-
42-
console.log(`[Sentience] Screenshot captured: ${format}, size: ${dataUrl.length} bytes`);
43-
44-
return dataUrl;
45-
} catch (error) {
46-
console.error('[Sentience] Screenshot error:', error);
47-
throw new Error(`Failed to capture screenshot: ${error.message}`);
48-
}
99+
async function handleScreenshotCapture(_tabId, options = {}) {
100+
try {
101+
const {
102+
format = 'png',
103+
quality = 90
104+
} = options;
105+
106+
const dataUrl = await chrome.tabs.captureVisibleTab(null, {
107+
format: format,
108+
quality: quality
109+
});
110+
111+
console.log(`[Sentience Background] Screenshot captured: ${format}, size: ${dataUrl.length} bytes`);
112+
return dataUrl;
113+
} catch (error) {
114+
console.error('[Sentience Background] Screenshot error:', error);
115+
throw new Error(`Failed to capture screenshot: ${error.message}`);
116+
}
49117
}
50118

51119
/**
52-
* Optional: Add viewport-specific capture (requires additional setup)
53-
* This would allow capturing specific regions, not just visible area
120+
* Handle snapshot processing with WASM (NEW!)
121+
* This is where the magic happens - completely CSP-immune!
122+
*
123+
* @param {Array} rawData - Raw element data from injected_api.js
124+
* @param {Object} options - Snapshot options (limit, filter, etc.)
125+
* @returns {Promise<Object>} Processed snapshot result
54126
*/
55-
async function captureRegion(tabId, region) {
56-
// For region capture, you'd need to:
57-
// 1. Capture full visible tab
58-
// 2. Use Canvas API to crop to region
59-
// 3. Return cropped image
60-
61-
// Not implemented in this basic version
62-
throw new Error('Region capture not yet implemented');
127+
async function handleSnapshotProcessing(rawData, options = {}) {
128+
try {
129+
// Ensure WASM is initialized
130+
await initWASM();
131+
if (!wasmReady) {
132+
throw new Error('WASM module not initialized');
133+
}
134+
135+
console.log(`[Sentience Background] Processing ${rawData.length} elements with options:`, options);
136+
137+
// Run WASM processing using the imported functions directly
138+
let analyzedElements;
139+
try {
140+
if (options.limit || options.filter) {
141+
analyzedElements = analyze_page_with_options(rawData, options);
142+
} else {
143+
analyzedElements = analyze_page(rawData);
144+
}
145+
} catch (e) {
146+
throw new Error(`WASM analyze_page failed: ${e.message}`);
147+
}
148+
149+
// Prune elements for API (prevents 413 errors on large sites)
150+
let prunedRawData;
151+
try {
152+
prunedRawData = prune_for_api(rawData);
153+
} catch (e) {
154+
console.warn('[Sentience Background] prune_for_api failed, using original data:', e);
155+
prunedRawData = rawData;
156+
}
157+
158+
console.log(`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned`);
159+
160+
return {
161+
elements: analyzedElements,
162+
raw_elements: prunedRawData
163+
};
164+
} catch (error) {
165+
console.error('[Sentience Background] Processing error:', error);
166+
throw error;
167+
}
63168
}
169+
170+
console.log('[Sentience Background] Service worker ready');

sentience/extension/content.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,83 @@
1-
// content.js - ISOLATED WORLD
2-
console.log('[Sentience] Bridge loaded.');
1+
// content.js - ISOLATED WORLD (Bridge between Main World and Background)
2+
console.log('[Sentience Bridge] Loaded.');
33

4-
// 1. Pass Extension ID to Main World (So WASM knows where to load from)
4+
// 1. Pass Extension ID to Main World (So API knows where to find resources)
55
document.documentElement.dataset.sentienceExtensionId = chrome.runtime.id;
66

7-
// 2. Proxy for Screenshots (The only thing Isolated World needs to do)
7+
// 2. Message Router - Handles all communication between page and background
88
window.addEventListener('message', (event) => {
99
// Security check: only accept messages from same window
10-
if (event.source !== window || event.data.type !== 'SENTIENCE_SCREENSHOT_REQUEST') return;
10+
if (event.source !== window) return;
1111

12+
// Route different message types
13+
switch (event.data.type) {
14+
case 'SENTIENCE_SCREENSHOT_REQUEST':
15+
handleScreenshotRequest(event.data);
16+
break;
17+
18+
case 'SENTIENCE_SNAPSHOT_REQUEST':
19+
handleSnapshotRequest(event.data);
20+
break;
21+
22+
default:
23+
// Ignore unknown message types
24+
break;
25+
}
26+
});
27+
28+
/**
29+
* Handle screenshot requests (existing functionality)
30+
*/
31+
function handleScreenshotRequest(data) {
1232
chrome.runtime.sendMessage(
13-
{ action: 'captureScreenshot', options: event.data.options },
33+
{ action: 'captureScreenshot', options: data.options },
1434
(response) => {
1535
window.postMessage({
1636
type: 'SENTIENCE_SCREENSHOT_RESULT',
17-
requestId: event.data.requestId,
18-
screenshot: response?.success ? response.screenshot : null
37+
requestId: data.requestId,
38+
screenshot: response?.success ? response.screenshot : null,
39+
error: response?.error
1940
}, '*');
2041
}
2142
);
22-
});
43+
}
44+
45+
/**
46+
* Handle snapshot processing requests (NEW!)
47+
* Sends raw DOM data to background worker for WASM processing
48+
*/
49+
function handleSnapshotRequest(data) {
50+
const startTime = performance.now();
51+
52+
chrome.runtime.sendMessage(
53+
{
54+
action: 'processSnapshot',
55+
rawData: data.rawData,
56+
options: data.options
57+
},
58+
(response) => {
59+
const duration = performance.now() - startTime;
60+
61+
if (response?.success) {
62+
console.log(`[Sentience Bridge] ✓ WASM processing complete in ${duration.toFixed(1)}ms`);
63+
window.postMessage({
64+
type: 'SENTIENCE_SNAPSHOT_RESULT',
65+
requestId: data.requestId,
66+
elements: response.result.elements,
67+
raw_elements: response.result.raw_elements,
68+
duration: duration
69+
}, '*');
70+
} else {
71+
console.error('[Sentience Bridge] WASM processing failed:', response?.error);
72+
window.postMessage({
73+
type: 'SENTIENCE_SNAPSHOT_RESULT',
74+
requestId: data.requestId,
75+
error: response?.error || 'Processing failed',
76+
duration: duration
77+
}, '*');
78+
}
79+
}
80+
);
81+
}
82+
83+
console.log('[Sentience Bridge] Ready - Extension ID:', chrome.runtime.id);

0 commit comments

Comments
 (0)