]> git.wh0rd.org - chrome-ext/tabs-backup.git/blame - background.js
makedist: delete manifest key
[chrome-ext/tabs-backup.git] / background.js
CommitLineData
d9e77fbf
MF
1// Create a backup on first install (or if storage is wiped for some reason.
2chrome.storage.local.get(function(items) {
3 // Setup defaults.
4 if (items.prefs_max_backup_items === undefined) {
5 chrome.storage.local.set({prefs_max_backup_items: 10});
6 }
7 if (items.prefs_backup_timer === undefined) {
8 chrome.storage.local.set({prefs_backup_timer: 30});
9 }
10
11 // If a backup exists already, nothing to do.
a18caa4d
MF
12 if (items.backups_list) {
13 return;
14 }
70c1267e
MF
15
16 // Create a backup now
17 var d = new Date();
18 var formattedDate = date_format (d);
19
eda98b5f 20 backupNow(true, formattedDate, function({success, backupName, backupObj}) {
70c1267e
MF
21 // backup completed
22 });
a18caa4d 23});
70c1267e
MF
24
25function initAlarm () {
e0b6a877 26 //console.log("initAlarm");
70c1267e
MF
27
28 var BACKUP_ALARM_NAME = "backup_alarm";
29
30 // Clear any previous alarm
31 chrome.alarms.clearAll();
70c1267e 32
d9e77fbf
MF
33 chrome.storage.local.get(function(items) {
34 const timerMinutes = items.prefs_backup_timer;
35 chrome.alarms.create(BACKUP_ALARM_NAME, {periodInMinutes: timerMinutes});
36 });
70c1267e
MF
37}
38
39initAlarm();
40
41function onAlarm (alarm) {
42 var d = new Date();
43 var formattedDate = date_format (d);
44
e0b6a877 45 console.log("Alarm {" + alarm + "} fired up: " + formattedDate);
70c1267e 46
70c1267e
MF
47 // if last backup time != lastTabsEdit
48 // perform automatic backup
eda98b5f 49 backupNow(true, formattedDate, function({success, backupName, backupObj}) {
70c1267e 50 // automatic backup completed
14a426d5
MF
51 chrome.runtime.sendMessage({
52 action: 'insertBackupItem',
53 args: [backupName, backupObj, true /*insertAtBeginning*/, true /*doAnimation*/],
54 });
70c1267e 55 });
70c1267e
MF
56}
57
58chrome.alarms.onAlarm.addListener(onAlarm);
59
c5a2d778
MF
60// yyyy-mm-dd hh:mm:ss
61function date_format(d) {
62 const prependZero = (val) => val.toString().padStart(2, '0');
63 return d.getFullYear() + "-" +
64 prependZero(d.getMonth() + 1) + "-" +
65 prependZero(d.getDate()) + " " +
66 prependZero(d.getHours()) + ":" +
67 prependZero(d.getMinutes()) + ":" +
68 prependZero(d.getSeconds());
70c1267e
MF
69}
70
71
72function backupNowManual (callbackDone) {
73 var d = new Date();
74 var formattedDate = date_format (d);
75
76 backupNow(false, formattedDate, callbackDone);
77
78
79}
80
71208cf4
MF
81async function deleteOldestBackup() {
82 const items = await chrome.storage.local.get();
83 if (!items.backups_list) {
84 return;
85 }
86
87 const backupsList = items.backups_list;
88 const numItemsToDelete = backupsList.length - items.prefs_max_backup_items;
89 for (let i = 0; i < numItemsToDelete; ++i) {
90 const deletedBackupName = backupsList[i];
91 await deleteBackup(deletedBackupName);
92
93 chrome.runtime.sendMessage({
94 action: 'removeBackupItemDiv',
95 args: [deletedBackupName],
96 });
97 }
70c1267e
MF
98}
99
100//var isCreatingBackup = false;
101
102function backupNow(isAutomatic, backupName, callbackDone) {
103 console.log("backupNow - isAutomatic: " + isAutomatic + " name: " + backupName);
104 /*if (isCreatingBackup === true) {
105 console.log("backupNow - already running..skipping..");
106 return;
107 }*/
108
109 //isCreatingBackup = true;
110
111 /*if (!confirm("Perform a full backup? All windows and their tabs will be saved!")) {
112 return;
113 }*/
114
115 var fullBackup = {
116 windows: [],
117 isAutomatic: isAutomatic,
118 totNumTabs: 0
119 };
120
121 chrome.windows.getAll({populate : true}, function (window_list) {
122 var totNumTabs = 0;
123
124 for(var i=0;i<window_list.length;i++) {
125 var window = window_list[i];
126
127 //console.log ("Window " + i);
128
129 var bkpWindow = {
4fe91aa7
MF
130 state: window.state,
131 top: window.top,
132 left: window.left,
133 width: window.width,
134 height: window.height,
70c1267e
MF
135 tabs: []
136 };
137
138 var windowTabs = window.tabs;
4fe91aa7
MF
139
140 // If it's a single window sittig at the new tab page, don't bother
141 // saving it. This is a nice shortcut when things crash as it will
142 // only show a single window.
143 if (windowTabs.length == 1) {
144 const tab = windowTabs[0];
3b8161c3 145 if (tab.url == 'chrome://newtab/')
4fe91aa7
MF
146 continue;
147 }
148
70c1267e
MF
149 for (var j = 0; j < windowTabs.length; j++) {
150 var tab = windowTabs[j];
151
152 //console.log("==> Tab " + j + " (" + tab.index + "): " + tabUrl);
153
e11f3338
MF
154 // Ignore windows that we can't/shouldn't backup.
155 if (tab.url.startsWith('chrome-untrusted://')) {
156 continue;
157 }
158
70c1267e
MF
159 var bkpTab = {
160 url: tab.url,
4fe91aa7
MF
161 title: tab.title,
162 highlighted: tab.highlighted,
163 pinned: tab.pinned,
70c1267e
MF
164 };
165
166 // Add tab to tabs arrays
167 bkpWindow.tabs.push(bkpTab);
168 }
169
e11f3338
MF
170 if (bkpWindow.tabs.length) {
171 totNumTabs += bkpWindow.tabs.length;
70c1267e 172
e11f3338
MF
173 fullBackup.windows.push(bkpWindow);
174 }
70c1267e
MF
175 }
176
e0b6a877
MF
177 if (totNumTabs == 0)
178 return;
179
70c1267e
MF
180 fullBackup.totNumTabs = totNumTabs;
181
182 var storageSetValues = {};
183 storageSetValues[backupName] = fullBackup;
184
185 // Store backup
186 chrome.storage.local.set(storageSetValues, function () {
187 if (chrome.runtime.lastError) {
188 //isCreatingBackup = false;
189 // TODO change icon to error..
190 //alert ("Error: " + chrome.runtime.lastError.message);
191 updateBrowserActionIcon (1);
192
eda98b5f 193 callbackDone({success: false});
70c1267e
MF
194 } else {
195 console.log("backup saved");
196 //alert("Backup saved successfully!");
197
d9e77fbf 198 chrome.storage.local.get(function(items) {
70c1267e
MF
199 var backupsList = [];
200 if(items.backups_list) {
201 backupsList = items.backups_list;
202 }
203
204 console.log("Updating 'backups_list' - cur. size: " + backupsList.length);
205
206 backupsList.push(backupName);
207
208 chrome.storage.local.set({"backups_list": backupsList}, function () {
209 //isCreatingBackup = false;
210
211 if (chrome.runtime.lastError) {
212 console.log ("Error saving backups_list: " + chrome.runtime.lastError.message);
213 updateBrowserActionIcon (1);
eda98b5f 214 callbackDone({success: false});
70c1267e
MF
215 } else {
216 console.log("Backups list saved successfully");
217
218 updateBrowserActionIcon (0);
eda98b5f
MF
219 callbackDone({
220 success: true,
221 backupName,
222 backupObj: fullBackup,
223 });
70c1267e 224
d9e77fbf 225 if (backupsList.length > items.prefs_max_backup_items) {
70c1267e
MF
226 deleteOldestBackup();
227 }
228 }
229 });
230 });
231 }
232 });
233 });
234}
235
236/**
237 * 0 ==> OK
238 * 1 ==> ERROR
239 */
240function updateBrowserActionIcon (status) {
241 var icon;
242 switch(status) {
243 case 0:
244 icon = "icon_ok.png";
245 break;
246 default:
247 icon = "icon_error.png";
248 break;
249 }
250
1f779f9c 251 chrome.action.setIcon({path: icon});
70c1267e
MF
252}
253
bc29ea6c 254async function deleteBackup(backupName) {
70c1267e
MF
255 console.log("Deleting backup " + backupName);
256
bc29ea6c
MF
257 await chrome.storage.local.remove(backupName);
258 //console.log("=> Deleted backup " + backupName);
70c1267e 259
bc29ea6c
MF
260 const items = await chrome.storage.local.get("backups_list");
261 //console.log("==> got backups_list " + backupName);
70c1267e 262
bc29ea6c
MF
263 if (!items.backups_list) {
264 return;
265 }
70c1267e 266
bc29ea6c 267 var backupsList = items.backups_list;
70c1267e 268
bc29ea6c
MF
269 var index = backupsList.indexOf(backupName);
270 if (index >= 0) {
271 backupsList.splice(index, 1);
272 }
70c1267e 273
bc29ea6c 274 //console.log("===> Updating backups_list (removing " + backupName + ")");
70c1267e 275
bc29ea6c
MF
276 await chrome.storage.local.set({"backups_list": backupsList});
277 //console.log("===> Updated backups_list (removed " + backupName + ")");
70c1267e
MF
278}
279
280function restoreNow(backupName) {
281 console.log("restoreNow");
282
283 chrome.storage.local.get(backupName, function(items) {
284 if(!items[backupName]) {
285 alert("No Backup found");
286 return;
287 }
288
289 /*if (!confirm("Restore full backup?")) {
290 return;
291 }*/
292 /*
293 if (confirm("Would you like to close all existing windows first?")) {
294 chrome.windows.getAll({populate : false}, function (window_list) {
295 for(var i=0;i<window_list.length;i++) {
296 var window = window_list[i];
297 chrome.windows.remove(window.id);
298 }
299 });
300 }*/
301
302
303 var fullBackup = items[backupName];
304
305 for(var i=0;i<fullBackup.windows.length;i++) {
4fe91aa7 306 const window = fullBackup.windows[i];
70c1267e
MF
307
308 //console.log ("Window " + i);
309
310 urlsToOpen = [];
311
4fe91aa7
MF
312 const windowTabs = window.tabs;
313 for (let j = 0; j < windowTabs.length; j++) {
314 const tab = windowTabs[j];
315 const tabUrl = tab.url;
70c1267e
MF
316 urlsToOpen.push(tabUrl);
317 }
318
4fe91aa7
MF
319 const windowProperties = {
320 state: 'normal',
321 url: urlsToOpen,
322 top: window.top,
323 left: window.left,
324 width: window.width,
325 height: window.height,
70c1267e
MF
326 };
327
328 // Create a new Window
329 chrome.windows.create(windowProperties, function(createdWindow) {
4fe91aa7
MF
330 // Chrome errors if the dimensions are set on non-normal windows.
331 // So we create the window first with the right settings, then
332 // update the window state.
333 if (window.state != 'normal') {
334 chrome.windows.update(createdWindow.id, {state: window.state});
335 }
70c1267e 336
4fe91aa7
MF
337 chrome.windows.get(createdWindow.id, {populate: true}, ({tabs}) => {
338 for (let tabi = 0; tabi < windowTabs.length; ++tabi) {
339 const oldtab = windowTabs[tabi];
340 const newtab = tabs[tabi];
341 chrome.tabs.update(newtab.id, {
342 highlighted: oldtab.highlighted,
343 pinned: oldtab.pinned,
344 }, () => {
345 if (!oldtab.highlighted) {
346 // If we discard a tab too fast, Chrome will completely
347 // throw it away. Wait until it's in a stable enough
348 // state for us to discard it.
349 let retryCount = 60;
350 const checktab = (id) => {
351 if (retryCount-- < 0)
352 return;
353 chrome.tabs.get(id, (tab) => {
354 if (tab.pendingUrl)
355 setTimeout(() => checktab(id), 500);
356 else
357 chrome.tabs.discard(id);
358 });
359 };
360 checktab(newtab.id);
361 }
362 });
363 }
364 });
70c1267e 365 });
70c1267e
MF
366 }
367 });
e0b6a877 368}
eda98b5f
MF
369
370/**
371 * Callback from other pages (like the popup).
372 */
373chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
374 console.log(`Got message from ${sender.id}: action=${request.action}`, request);
375
376 let asyncResponse = false;
377 switch (request?.action) {
378 case 'initAlarm':
379 initAlarm();
380 break;
381
382 case 'restoreNow':
383 restoreNow(...request.args);
384 break;
385
386 case 'deleteBackup':
bc29ea6c 387 deleteBackup(...request.args).then(sendResponse);
eda98b5f
MF
388 asyncResponse = true;
389 break;
390
391 case 'backupNowManual':
392 backupNowManual(sendResponse);
393 asyncResponse = true;
394 break;
395 }
396 return asyncResponse;
397});