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