-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbackground.js
More file actions
159 lines (139 loc) · 5.55 KB
/
Copy pathbackground.js
File metadata and controls
159 lines (139 loc) · 5.55 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
/**
* BDLawCorpus Background Service Worker
*
* Handles:
* - Side panel management
* - File downloads
* - Service worker keep-alive utilities for long-running operations
*
* Storage note: IndexedDB is the primary corpus store (accessible from service
* workers). chrome.storage.local (with unlimitedStorage permission) is the
* fallback. Global variables must NEVER be used for persistent state — they
* are reset on every service worker restart.
*
* Keep-alive: Use waitUntil() or startHeartbeat()/stopHeartbeat() when
* initiating any operation that may take >30s to prevent the service worker
* from being terminated mid-operation.
*/
// Open side panel when extension icon is clicked
chrome.action.onClicked.addListener(async (tab) => {
try {
await chrome.sidePanel.open({ windowId: tab.windowId });
} catch (e) {
console.error('Failed to open side panel:', e);
}
});
// Set side panel behavior - open on action click
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })
.catch(e => console.error('Failed to set panel behavior:', e));
// Handle download requests from popup/sidepanel
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (!msg) return;
// Handle JSON downloads
if (msg.type === 'downloadJSON') {
(async () => {
try {
const content = typeof msg.content === 'string' ? msg.content : '';
const filename = msg.filename || 'bdlaw_export.json';
// Create data URL for JSON content
const dataUrl = 'data:application/json;charset=utf-8,' + encodeURIComponent(content);
// Trigger download
await chrome.downloads.download({
url: dataUrl,
filename: filename,
saveAs: false
});
sendResponse({ ok: true });
} catch (e) {
console.error('Download failed:', e);
sendResponse({ ok: false, error: e?.message || 'Download failed' });
}
})();
return true; // Keep channel open for async response
}
// Handle text/markdown downloads
// Requirements: 9.1, 9.2, 9.5 - Support downloading research documents
if (msg.type === 'downloadText') {
(async () => {
try {
const content = typeof msg.content === 'string' ? msg.content : '';
const filename = msg.filename || 'bdlaw_export.txt';
const mimeType = msg.mimeType || 'text/plain';
// Create data URL for text content
const dataUrl = `data:${mimeType};charset=utf-8,` + encodeURIComponent(content);
// Trigger download
await chrome.downloads.download({
url: dataUrl,
filename: filename,
saveAs: false
});
sendResponse({ ok: true });
} catch (e) {
console.error('Download failed:', e);
sendResponse({ ok: false, error: e?.message || 'Download failed' });
}
})();
return true; // Keep channel open for async response
}
});
// ---------------------------------------------------------------------------
// Service Worker Keep-Alive Utilities
// (Official pattern from developer.chrome.com/docs/extensions/develop/migrate)
// ---------------------------------------------------------------------------
/**
* Wraps a long-running promise, keeping the service worker alive by pinging
* chrome.runtime.getPlatformInfo every 25 seconds. Use for any single
* async operation that might exceed the 30s service worker timeout.
*
* @param {Promise} promise - The long-running operation to await.
* @returns {Promise} Resolves/rejects with the same value as the input promise.
*/
async function waitUntil(promise) {
const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
try {
return await promise;
} finally {
clearInterval(keepAlive);
}
}
/**
* Heartbeat interval reference for startHeartbeat/stopHeartbeat pair.
* @type {number|undefined}
*/
let _heartbeatInterval;
/**
* Starts a heartbeat that writes a timestamp to chrome.storage.local every
* 20 seconds, keeping the service worker alive during extended batch work.
* Call stopHeartbeat() when the operation finishes.
*
* Preferred over waitUntil() when the duration is unknown (e.g. queued batch
* processing triggered by chrome.alarms).
*/
async function startHeartbeat() {
await chrome.storage.local.set({ 'bdlaw-sw-heartbeat': Date.now() });
_heartbeatInterval = setInterval(async () => {
await chrome.storage.local.set({ 'bdlaw-sw-heartbeat': Date.now() });
}, 20 * 1000);
}
/** Stops the service worker heartbeat started by startHeartbeat(). */
function stopHeartbeat() {
if (_heartbeatInterval !== undefined) {
clearInterval(_heartbeatInterval);
_heartbeatInterval = undefined;
}
}
// ---------------------------------------------------------------------------
// Alarm-based retry trigger
// Fired by bdlaw-queue when a persistent retry queue needs processing.
// chrome.alarms is used instead of setTimeout because alarms survive service
// worker restarts; setTimeout is canceled when the worker terminates.
// ---------------------------------------------------------------------------
chrome.alarms.onAlarm.addListener(async (alarm) => {
if (alarm.name === 'bdlaw-retry-queue') {
// Notify the side panel to process the persisted retry queue.
// The side panel may not be open; the message will be silently dropped
// if no listener is registered — this is intentional (retry on next open).
chrome.runtime.sendMessage({ type: 'processRetryQueue' }).catch(() => {});
}
});
console.log('BDLawCorpus background service worker loaded');