зеркало из https://github.com/mozilla/pjs.git
Bug 477583 - Backups of bookmarks stops working if a future backup exists, r=mano
This commit is contained in:
Родитель
9bddf793b1
Коммит
dd2f545943
|
@ -53,12 +53,13 @@ Cu.import("resource:///modules/distribution.js");
|
||||||
|
|
||||||
const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
|
const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
|
||||||
|
|
||||||
// Check to see if bookmarks need backing up once per
|
// We try to backup bookmarks at idle times, to avoid doing that at shutdown.
|
||||||
// day on 1 hour idle.
|
// Number of idle seconds before trying to backup bookmarks. 15 minutes.
|
||||||
const BOOKMARKS_ARCHIVE_IDLE_TIME = 60 * 60;
|
const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60;
|
||||||
|
// Minimum interval in milliseconds between backups.
|
||||||
// Backup bookmarks once every 24 hours.
|
const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000;
|
||||||
const BOOKMARKS_ARCHIVE_INTERVAL = 86400 * 1000;
|
// Maximum number of backups to create. Old ones will be purged.
|
||||||
|
const BOOKMARKS_BACKUP_MAX_BACKUPS = 10;
|
||||||
|
|
||||||
// Factory object
|
// Factory object
|
||||||
const BrowserGlueServiceFactory = {
|
const BrowserGlueServiceFactory = {
|
||||||
|
@ -76,29 +77,21 @@ const BrowserGlueServiceFactory = {
|
||||||
|
|
||||||
function BrowserGlue() {
|
function BrowserGlue() {
|
||||||
|
|
||||||
this.__defineGetter__("_prefs", function() {
|
XPCOMUtils.defineLazyServiceGetter(this, "_prefs",
|
||||||
delete this._prefs;
|
"@mozilla.org/preferences-service;1",
|
||||||
return this._prefs = Cc["@mozilla.org/preferences-service;1"].
|
"nsIPrefBranch");
|
||||||
getService(Ci.nsIPrefBranch);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.__defineGetter__("_bundleService", function() {
|
XPCOMUtils.defineLazyServiceGetter(this, "_bundleService",
|
||||||
delete this._bundleService;
|
"@mozilla.org/intl/stringbundle;1",
|
||||||
return this._bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
|
"nsIStringBundleService");
|
||||||
getService(Ci.nsIStringBundleService);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.__defineGetter__("_idleService", function() {
|
XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
|
||||||
delete this._idleService;
|
"@mozilla.org/widget/idleservice;1",
|
||||||
return this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
|
"nsIIdleService");
|
||||||
getService(Ci.nsIIdleService);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.__defineGetter__("_observerService", function() {
|
XPCOMUtils.defineLazyServiceGetter(this, "_observerService",
|
||||||
delete this._observerService;
|
"@mozilla.org/observer-service;1",
|
||||||
return this._observerService = Cc['@mozilla.org/observer-service;1'].
|
"nsIObserverService");
|
||||||
getService(Ci.nsIObserverService);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
@ -112,6 +105,10 @@ function BrowserGlue() {
|
||||||
BrowserGlue.prototype = {
|
BrowserGlue.prototype = {
|
||||||
|
|
||||||
_saveSession: false,
|
_saveSession: false,
|
||||||
|
_isIdleObserver: false,
|
||||||
|
_isPlacesInitObserver: false,
|
||||||
|
_isPlacesLockedObserver: false,
|
||||||
|
_isPlacesDatabaseLocked: false,
|
||||||
|
|
||||||
_setPrefToSaveSession: function()
|
_setPrefToSaveSession: function()
|
||||||
{
|
{
|
||||||
|
@ -177,20 +174,21 @@ BrowserGlue.prototype = {
|
||||||
case "places-init-complete":
|
case "places-init-complete":
|
||||||
this._initPlaces();
|
this._initPlaces();
|
||||||
this._observerService.removeObserver(this, "places-init-complete");
|
this._observerService.removeObserver(this, "places-init-complete");
|
||||||
|
this._isPlacesInitObserver = false;
|
||||||
// no longer needed, since history was initialized completely.
|
// no longer needed, since history was initialized completely.
|
||||||
this._observerService.removeObserver(this, "places-database-locked");
|
this._observerService.removeObserver(this, "places-database-locked");
|
||||||
|
this._isPlacesLockedObserver = false;
|
||||||
break;
|
break;
|
||||||
case "places-database-locked":
|
case "places-database-locked":
|
||||||
this._isPlacesDatabaseLocked = true;
|
this._isPlacesDatabaseLocked = true;
|
||||||
// stop observing, so further attempts to load history service
|
// stop observing, so further attempts to load history service
|
||||||
// do not show the prompt.
|
// do not show the prompt.
|
||||||
this._observerService.removeObserver(this, "places-database-locked");
|
this._observerService.removeObserver(this, "places-database-locked");
|
||||||
|
this._isPlacesLockedObserver = false;
|
||||||
break;
|
break;
|
||||||
case "idle":
|
case "idle":
|
||||||
if (this._idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
|
if (this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000)
|
||||||
// Back up bookmarks.
|
this._backupBookmarks();
|
||||||
this._archiveBookmarks();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -213,7 +211,9 @@ BrowserGlue.prototype = {
|
||||||
#endif
|
#endif
|
||||||
osvr.addObserver(this, "session-save", false);
|
osvr.addObserver(this, "session-save", false);
|
||||||
osvr.addObserver(this, "places-init-complete", false);
|
osvr.addObserver(this, "places-init-complete", false);
|
||||||
|
this._isPlacesInitObserver = true;
|
||||||
osvr.addObserver(this, "places-database-locked", false);
|
osvr.addObserver(this, "places-database-locked", false);
|
||||||
|
this._isPlacesLockedObserver = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
// cleanup (called on application shutdown)
|
// cleanup (called on application shutdown)
|
||||||
|
@ -227,12 +227,18 @@ BrowserGlue.prototype = {
|
||||||
osvr.removeObserver(this, "sessionstore-windows-restored");
|
osvr.removeObserver(this, "sessionstore-windows-restored");
|
||||||
osvr.removeObserver(this, "browser:purge-session-history");
|
osvr.removeObserver(this, "browser:purge-session-history");
|
||||||
osvr.removeObserver(this, "quit-application-requested");
|
osvr.removeObserver(this, "quit-application-requested");
|
||||||
|
osvr.removeObserver(this, "quit-application-granted");
|
||||||
#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
|
#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
|
||||||
osvr.removeObserver(this, "browser-lastwindow-close-requested");
|
osvr.removeObserver(this, "browser-lastwindow-close-requested");
|
||||||
osvr.removeObserver(this, "browser-lastwindow-close-granted");
|
osvr.removeObserver(this, "browser-lastwindow-close-granted");
|
||||||
#endif
|
#endif
|
||||||
osvr.removeObserver(this, "quit-application-granted");
|
|
||||||
osvr.removeObserver(this, "session-save");
|
osvr.removeObserver(this, "session-save");
|
||||||
|
if (this._isIdleObserver)
|
||||||
|
this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
|
||||||
|
if (this._isPlacesInitObserver)
|
||||||
|
osvr.removeObserver(this, "places-init-complete");
|
||||||
|
if (this._isPlacesLockedObserver)
|
||||||
|
osvr.removeObserver(this, "places-database-locked");
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAppDefaults: function()
|
_onAppDefaults: function()
|
||||||
|
@ -288,7 +294,8 @@ BrowserGlue.prototype = {
|
||||||
_onProfileShutdown: function()
|
_onProfileShutdown: function()
|
||||||
{
|
{
|
||||||
this._shutdownPlaces();
|
this._shutdownPlaces();
|
||||||
this._idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
|
this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
|
||||||
|
this._isIdleObserver = false;
|
||||||
this.Sanitizer.onShutdown();
|
this.Sanitizer.onShutdown();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -592,8 +599,8 @@ BrowserGlue.prototype = {
|
||||||
restoreDefaultBookmarks =
|
restoreDefaultBookmarks =
|
||||||
this._prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
|
this._prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
|
||||||
if (restoreDefaultBookmarks) {
|
if (restoreDefaultBookmarks) {
|
||||||
// Ensure that we already have a bookmarks backup for today
|
// Ensure that we already have a bookmarks backup for today.
|
||||||
this._archiveBookmarks();
|
this._backupBookmarks();
|
||||||
importBookmarks = true;
|
importBookmarks = true;
|
||||||
}
|
}
|
||||||
} catch(ex) {}
|
} catch(ex) {}
|
||||||
|
@ -603,10 +610,10 @@ BrowserGlue.prototype = {
|
||||||
if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
|
if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
|
||||||
// get latest JSON backup
|
// get latest JSON backup
|
||||||
Cu.import("resource://gre/modules/utils.js");
|
Cu.import("resource://gre/modules/utils.js");
|
||||||
var bookmarksBackupFile = PlacesUtils.getMostRecentBackup();
|
var bookmarksBackupFile = PlacesUtils.backups.getMostRecent("json");
|
||||||
if (bookmarksBackupFile && bookmarksBackupFile.leafName.match("\.json$")) {
|
if (bookmarksBackupFile) {
|
||||||
// restore from JSON backup
|
// restore from JSON backup
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(bookmarksBackupFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(bookmarksBackupFile);
|
||||||
importBookmarks = false;
|
importBookmarks = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -683,29 +690,29 @@ BrowserGlue.prototype = {
|
||||||
|
|
||||||
// Initialize bookmark archiving on idle.
|
// Initialize bookmark archiving on idle.
|
||||||
// Once a day, either on idle or shutdown, bookmarks are backed up.
|
// Once a day, either on idle or shutdown, bookmarks are backed up.
|
||||||
this._idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
|
if (!this._isIdleObserver) {
|
||||||
|
this._idleService.addIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
|
||||||
|
this._isIdleObserver = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places shut-down tasks
|
* Places shut-down tasks
|
||||||
* - back up and archive bookmarks
|
* - back up bookmarks if needed.
|
||||||
* - export bookmarks as HTML, if so configured
|
* - export bookmarks as HTML, if so configured.
|
||||||
*
|
*
|
||||||
* Note: quit-application-granted notification is received twice
|
* Note: quit-application-granted notification is received twice
|
||||||
* so replace this method with a no-op when first called.
|
* so replace this method with a no-op when first called.
|
||||||
*/
|
*/
|
||||||
_shutdownPlaces: function bg__shutdownPlaces() {
|
_shutdownPlaces: function bg__shutdownPlaces() {
|
||||||
// Backup and archive Places bookmarks.
|
this._backupBookmarks();
|
||||||
this._archiveBookmarks();
|
|
||||||
|
|
||||||
// Backup bookmarks to bookmarks.html to support apps that depend
|
// Backup bookmarks to bookmarks.html to support apps that depend
|
||||||
// on the legacy format.
|
// on the legacy format.
|
||||||
var autoExportHTML = false;
|
var autoExportHTML = false;
|
||||||
try {
|
try {
|
||||||
autoExportHTML = this._prefs.getBoolPref("browser.bookmarks.autoExportHTML");
|
autoExportHTML = this._prefs.getBoolPref("browser.bookmarks.autoExportHTML");
|
||||||
} catch(ex) {
|
} catch(ex) { /* Don't export */ }
|
||||||
Components.utils.reportError(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autoExportHTML) {
|
if (autoExportHTML) {
|
||||||
Cc["@mozilla.org/browser/places/import-export-service;1"].
|
Cc["@mozilla.org/browser/places/import-export-service;1"].
|
||||||
|
@ -715,23 +722,24 @@ BrowserGlue.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Back up and archive bookmarks
|
* Backup bookmarks if needed.
|
||||||
*/
|
*/
|
||||||
_archiveBookmarks: function nsBrowserGlue__archiveBookmarks() {
|
_backupBookmarks: function nsBrowserGlue__backupBookmarks() {
|
||||||
Cu.import("resource://gre/modules/utils.js");
|
Cu.import("resource://gre/modules/utils.js");
|
||||||
|
|
||||||
var lastBackup = PlacesUtils.getMostRecentBackup();
|
let lastBackupFile = PlacesUtils.backups.getMostRecent();
|
||||||
|
|
||||||
// Backup bookmarks if there aren't any backups or
|
// Backup bookmarks if there are no backups or the maximum interval between
|
||||||
// they haven't been backed up in the last 24 hrs.
|
// backups elapsed.
|
||||||
if (!lastBackup ||
|
if (!lastBackupFile ||
|
||||||
Date.now() - lastBackup.lastModifiedTime > BOOKMARKS_ARCHIVE_INTERVAL) {
|
new Date() - PlacesUtils.backups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_INTERVAL) {
|
||||||
var maxBackups = 5;
|
let maxBackups = BOOKMARKS_BACKUP_MAX_BACKUPS;
|
||||||
try {
|
try {
|
||||||
maxBackups = this._prefs.getIntPref("browser.bookmarks.max_backups");
|
maxBackups = this._prefs.getIntPref("browser.bookmarks.max_backups");
|
||||||
} catch(ex) {}
|
}
|
||||||
|
catch(ex) { /* Use default. */ }
|
||||||
|
|
||||||
PlacesUtils.archiveBookmarksFile(maxBackups, false /* don't force */);
|
PlacesUtils.backups.create(maxBackups); // Don't force creation.
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -423,54 +423,36 @@ var PlacesOrganizer = {
|
||||||
* Populates the restore menu with the dates of the backups available.
|
* Populates the restore menu with the dates of the backups available.
|
||||||
*/
|
*/
|
||||||
populateRestoreMenu: function PO_populateRestoreMenu() {
|
populateRestoreMenu: function PO_populateRestoreMenu() {
|
||||||
var restorePopup = document.getElementById("fileRestorePopup");
|
let restorePopup = document.getElementById("fileRestorePopup");
|
||||||
|
|
||||||
var dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"].
|
let dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"].
|
||||||
getService(Ci.nsIScriptableDateFormat);
|
getService(Ci.nsIScriptableDateFormat);
|
||||||
|
|
||||||
// remove existing menu items
|
// Remove existing menu items. Last item is the restoreFromFile item.
|
||||||
// last item is the restoreFromFile item
|
|
||||||
while (restorePopup.childNodes.length > 1)
|
while (restorePopup.childNodes.length > 1)
|
||||||
restorePopup.removeChild(restorePopup.firstChild);
|
restorePopup.removeChild(restorePopup.firstChild);
|
||||||
|
|
||||||
// get list of files
|
let backupFiles = PlacesUtils.backups.entries;
|
||||||
var localizedFilename = PlacesUtils.getString("bookmarksArchiveFilename");
|
if (backupFiles.length == 0)
|
||||||
var localizedFilenamePrefix = localizedFilename.substr(0, localizedFilename.indexOf("-"));
|
|
||||||
var fileList = [];
|
|
||||||
var files = this.bookmarksBackupDir.directoryEntries;
|
|
||||||
while (files.hasMoreElements()) {
|
|
||||||
var f = files.getNext().QueryInterface(Ci.nsIFile);
|
|
||||||
var rx = new RegExp("^(bookmarks|" + localizedFilenamePrefix +
|
|
||||||
")-([0-9]{4}-[0-9]{2}-[0-9]{2})\.json$");
|
|
||||||
if (!f.isHidden() && f.leafName.match(rx)) {
|
|
||||||
var date = f.leafName.match(rx)[2].replace(/-/g, "/");
|
|
||||||
var dateObj = new Date(date);
|
|
||||||
fileList.push({date: dateObj, filename: f.leafName});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileList.sort(function PO_fileList_compare(a, b) {
|
|
||||||
return b.date - a.date;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fileList.length == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// populate menu
|
// Populate menu with backups.
|
||||||
for (var i = 0; i < fileList.length; i++) {
|
for (let i = 0; i < backupFiles.length; i++) {
|
||||||
var m = restorePopup.insertBefore
|
let backupDate = PlacesUtils.backups.getDateForFile(backupFiles[i]);
|
||||||
(document.createElement("menuitem"),
|
let m = restorePopup.insertBefore(document.createElement("menuitem"),
|
||||||
document.getElementById("restoreFromFile"));
|
document.getElementById("restoreFromFile"));
|
||||||
m.setAttribute("label",
|
m.setAttribute("label",
|
||||||
dateSvc.FormatDate("",
|
dateSvc.FormatDate("",
|
||||||
Ci.nsIScriptableDateFormat.dateFormatLong,
|
Ci.nsIScriptableDateFormat.dateFormatLong,
|
||||||
fileList[i].date.getFullYear(),
|
backupDate.getFullYear(),
|
||||||
fileList[i].date.getMonth() + 1,
|
backupDate.getMonth() + 1,
|
||||||
fileList[i].date.getDate()));
|
backupDate.getDate()));
|
||||||
m.setAttribute("value", fileList[i].filename);
|
m.setAttribute("value", backupFiles[i].leafName);
|
||||||
m.setAttribute("oncommand",
|
m.setAttribute("oncommand",
|
||||||
"PlacesOrganizer.onRestoreMenuItemClick(this);");
|
"PlacesOrganizer.onRestoreMenuItemClick(this);");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the restoreFromFile item.
|
||||||
restorePopup.insertBefore(document.createElement("menuseparator"),
|
restorePopup.insertBefore(document.createElement("menuseparator"),
|
||||||
document.getElementById("restoreFromFile"));
|
document.getElementById("restoreFromFile"));
|
||||||
},
|
},
|
||||||
|
@ -479,14 +461,14 @@ var PlacesOrganizer = {
|
||||||
* Called when a menuitem is selected from the restore menu.
|
* Called when a menuitem is selected from the restore menu.
|
||||||
*/
|
*/
|
||||||
onRestoreMenuItemClick: function PO_onRestoreMenuItemClick(aMenuItem) {
|
onRestoreMenuItemClick: function PO_onRestoreMenuItemClick(aMenuItem) {
|
||||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
let backupName = aMenuItem.getAttribute("value");
|
||||||
getService(Ci.nsIProperties);
|
let backupFiles = PlacesUtils.backups.entries;
|
||||||
var bookmarksFile = dirSvc.get("ProfD", Ci.nsIFile);
|
for (let i = 0; i < backupFiles.length; i++) {
|
||||||
bookmarksFile.append("bookmarkbackups");
|
if (backupFiles[i].leafName == backupName) {
|
||||||
bookmarksFile.append(aMenuItem.getAttribute("value"));
|
this.restoreBookmarksFromFile(backupFiles[i]);
|
||||||
if (!bookmarksFile.exists())
|
break;
|
||||||
return;
|
}
|
||||||
this.restoreBookmarksFromFile(bookmarksFile);
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -529,7 +511,7 @@ var PlacesOrganizer = {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(aFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(aFile);
|
||||||
}
|
}
|
||||||
catch(ex) {
|
catch(ex) {
|
||||||
this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError"));
|
this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError"));
|
||||||
|
@ -562,30 +544,10 @@ var PlacesOrganizer = {
|
||||||
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
||||||
fp.displayDirectory = backupsDir;
|
fp.displayDirectory = backupsDir;
|
||||||
|
|
||||||
fp.defaultString = PlacesUtils.getBackupFilename();
|
fp.defaultString = PlacesUtils.backups.getFilenameForDate();
|
||||||
|
|
||||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
if (fp.show() != Ci.nsIFilePicker.returnCancel)
|
||||||
PlacesUtils.backupBookmarksToFile(fp.file);
|
PlacesUtils.backups.saveBookmarksToJSONFile(fp.file);
|
||||||
|
|
||||||
// copy new backup to /backups dir (bug 424389)
|
|
||||||
var latestBackup = PlacesUtils.getMostRecentBackup();
|
|
||||||
if (!latestBackup || latestBackup != fp.file) {
|
|
||||||
latestBackup.remove(false);
|
|
||||||
var name = PlacesUtils.getBackupFilename();
|
|
||||||
fp.file.copyTo(this.bookmarksBackupDir, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get bookmarksBackupDir() {
|
|
||||||
delete this.bookmarksBackupDir;
|
|
||||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
|
||||||
getService(Ci.nsIProperties);
|
|
||||||
var bookmarksBackupDir = dirSvc.get("ProfD", Ci.nsIFile);
|
|
||||||
bookmarksBackupDir.append("bookmarkbackups");
|
|
||||||
if (!bookmarksBackupDir.exists())
|
|
||||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
|
||||||
return this.bookmarksBackupDir = bookmarksBackupDir;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_paneDisabled: false,
|
_paneDisabled: false,
|
||||||
|
|
|
@ -157,10 +157,14 @@ const FILENAME_BOOKMARKS_HTML = "bookmarks.html";
|
||||||
let backup_date = new Date().toLocaleFormat("%Y-%m-%d");
|
let backup_date = new Date().toLocaleFormat("%Y-%m-%d");
|
||||||
const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json";
|
const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json";
|
||||||
|
|
||||||
// Smart bookmarks constants
|
// Smart bookmarks constants.
|
||||||
const SMART_BOOKMARKS_VERSION = 2;
|
const SMART_BOOKMARKS_VERSION = 2;
|
||||||
const SMART_BOOKMARKS_ON_TOOLBAR = 1;
|
const SMART_BOOKMARKS_ON_TOOLBAR = 1;
|
||||||
const SMART_BOOKMARKS_ON_MENU = 2;
|
const SMART_BOOKMARKS_ON_MENU = 3; // Takes in count the additional separator.
|
||||||
|
|
||||||
|
// Default bookmarks constants.
|
||||||
|
const DEFAULT_BOOKMARKS_ON_TOOLBAR = 2;
|
||||||
|
const DEFAULT_BOOKMARKS_ON_MENU = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a bookmarks.html file in the profile folder from a given source file.
|
* Creates a bookmarks.html file in the profile folder from a given source file.
|
||||||
|
|
|
@ -99,11 +99,11 @@ function run_test() {
|
||||||
// 3. import bookmarks.exported.json
|
// 3. import bookmarks.exported.json
|
||||||
// 4. run the test-suite
|
// 4. run the test-suite
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
LOG("exported json");
|
LOG("exported json");
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
LOG("imported json");
|
LOG("imported json");
|
||||||
validate();
|
validate();
|
||||||
|
|
|
@ -80,10 +80,10 @@ var tests = [
|
||||||
run: function () {
|
run: function () {
|
||||||
this.file = createFile("bookmarks-test_restoreNotification.json");
|
this.file = createFile("bookmarks-test_restoreNotification.json");
|
||||||
addBookmarks();
|
addBookmarks();
|
||||||
PlacesUtils.backupBookmarksToFile(this.file);
|
PlacesUtils.backups.saveBookmarksToJSONFile(this.file);
|
||||||
remove_all_bookmarks();
|
remove_all_bookmarks();
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(this.file);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(this.file);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
do_throw(" Restore should not have failed");
|
do_throw(" Restore should not have failed");
|
||||||
|
@ -100,7 +100,7 @@ var tests = [
|
||||||
run: function () {
|
run: function () {
|
||||||
this.file = createFile("bookmarks-test_restoreNotification.json");
|
this.file = createFile("bookmarks-test_restoreNotification.json");
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(this.file);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(this.file);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
do_throw(" Restore should not have failed");
|
do_throw(" Restore should not have failed");
|
||||||
|
@ -118,7 +118,7 @@ var tests = [
|
||||||
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
this.file = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||||
this.file.append("this file doesn't exist because nobody created it");
|
this.file.append("this file doesn't exist because nobody created it");
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(this.file);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(this.file);
|
||||||
do_throw(" Restore should have failed");
|
do_throw(" Restore should have failed");
|
||||||
}
|
}
|
||||||
catch (e) {}
|
catch (e) {}
|
||||||
|
|
|
@ -82,9 +82,9 @@ tests.push({
|
||||||
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
||||||
|
|
||||||
// Count items on toolbar.
|
// Count items on toolbar.
|
||||||
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR);
|
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
|
||||||
// Count items on menu (+1 for the separator).
|
// Count items on menu (+1 for the separator).
|
||||||
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + 1);
|
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
|
||||||
|
|
||||||
// Check version has been updated.
|
// Check version has been updated.
|
||||||
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
||||||
|
@ -113,9 +113,9 @@ tests.push({
|
||||||
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
||||||
|
|
||||||
// Count items on toolbar.
|
// Count items on toolbar.
|
||||||
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR);
|
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
|
||||||
// Count items on menu (+1 for the separator).
|
// Count items on menu (+1 for the separator).
|
||||||
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + 1);
|
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
|
||||||
|
|
||||||
// check smart bookmark has been replaced, itemId has changed.
|
// check smart bookmark has been replaced, itemId has changed.
|
||||||
itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
|
itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
|
||||||
|
@ -138,15 +138,15 @@ tests.push({
|
||||||
// Set preferences.
|
// Set preferences.
|
||||||
ps.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
|
ps.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
|
||||||
// Remove toolbar's smart bookmarks
|
// Remove toolbar's smart bookmarks
|
||||||
bs.removeFolderChildren(bs.toolbarFolder);
|
bs.removeItem(bs.getIdForItemAt(bs.toolbarFolder, 0));
|
||||||
|
|
||||||
// Force nsBrowserGlue::_initPlaces().
|
// Force nsBrowserGlue::_initPlaces().
|
||||||
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
||||||
|
|
||||||
// Count items on toolbar, we should not have recreated the smart bookmark.
|
// Count items on toolbar, we should not have recreated the smart bookmark.
|
||||||
do_check_eq(countFolderChildren(bs.toolbarFolder), 0);
|
do_check_eq(countFolderChildren(bs.toolbarFolder), DEFAULT_BOOKMARKS_ON_TOOLBAR);
|
||||||
// Count items on menu (+1 for the separator).
|
// Count items on menu (+1 for the separator).
|
||||||
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + 1);
|
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
|
||||||
|
|
||||||
// Check version has been updated.
|
// Check version has been updated.
|
||||||
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
||||||
|
@ -167,9 +167,9 @@ tests.push({
|
||||||
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
os.notifyObservers(null, TOPIC_PLACES_INIT_COMPLETE, null);
|
||||||
|
|
||||||
// Count items on toolbar, we should not have recreated the smart bookmark.
|
// Count items on toolbar, we should not have recreated the smart bookmark.
|
||||||
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR);
|
do_check_eq(countFolderChildren(bs.toolbarFolder), SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
|
||||||
// Count items on menu (+1 for the separator).
|
// Count items on menu (+1 for the separator).
|
||||||
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + 1);
|
do_check_eq(countFolderChildren(bs.bookmarksMenuFolder), SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
|
||||||
|
|
||||||
// Check version has been updated.
|
// Check version has been updated.
|
||||||
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
do_check_eq(ps.getIntPref(PREF_SMART_BOOKMARKS_VERSION), SMART_BOOKMARKS_VERSION);
|
||||||
|
@ -193,23 +193,19 @@ function countFolderChildren(aFolderItemId) {
|
||||||
function finish_test() {
|
function finish_test() {
|
||||||
// Clean up database from all bookmarks.
|
// Clean up database from all bookmarks.
|
||||||
remove_all_bookmarks();
|
remove_all_bookmarks();
|
||||||
// Simulate application closing to remove the idle observer and avoid leaks.
|
|
||||||
os.notifyObservers(null, "quit-application-granted", null);
|
|
||||||
do_test_finished();
|
do_test_finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
var testIndex = 0;
|
var testIndex = 0;
|
||||||
function next_test() {
|
function next_test() {
|
||||||
// Simulate application closing to remove the idle observer and avoid leaks.
|
|
||||||
os.notifyObservers(null, "quit-application-granted", null);
|
|
||||||
|
|
||||||
// nsBrowserGlue stops observing topics after first notification,
|
// nsBrowserGlue stops observing topics after first notification,
|
||||||
// so we add back the observer to test additional runs.
|
// so we add back the observer to test additional runs.
|
||||||
os.addObserver(bg, TOPIC_PLACES_INIT_COMPLETE, false);
|
os.addObserver(bg, TOPIC_PLACES_INIT_COMPLETE, false);
|
||||||
|
|
||||||
// Execute next test.
|
// Execute next test.
|
||||||
var test = tests.shift();
|
let test = tests.shift();
|
||||||
dump("\nTEST " + (++testIndex) + ": " + test.description);
|
print("\nTEST " + (++testIndex) + ": " + test.description);
|
||||||
test.exec();
|
test.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,12 @@ var PlacesUtils = {
|
||||||
getService(Ci.nsITaggingService);
|
getService(Ci.nsITaggingService);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get observerSvc() {
|
||||||
|
delete this.observerSvc;
|
||||||
|
return this.observerSvc = Cc["@mozilla.org/observer-service;1"].
|
||||||
|
getService(Ci.nsIObserverService);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a URI from a spec.
|
* Makes a URI from a spec.
|
||||||
* @param aSpec
|
* @param aSpec
|
||||||
|
@ -278,9 +284,7 @@ var PlacesUtils = {
|
||||||
this.annotations.addObserver(this, false);
|
this.annotations.addObserver(this, false);
|
||||||
|
|
||||||
// observe shutdown, so we can remove the anno observer
|
// observe shutdown, so we can remove the anno observer
|
||||||
const os = Cc["@mozilla.org/observer-service;1"].
|
this.observerSvc.addObserver(this, "xpcom-shutdown", false);
|
||||||
getService(Ci.nsIObserverService);
|
|
||||||
os.addObserver(this, "xpcom-shutdown", false);
|
|
||||||
|
|
||||||
var readOnly = this.annotations.getItemsWithAnnotation(READ_ONLY_ANNO, {});
|
var readOnly = this.annotations.getItemsWithAnnotation(READ_ONLY_ANNO, {});
|
||||||
this.__defineGetter__("_readOnly", function() readOnly);
|
this.__defineGetter__("_readOnly", function() readOnly);
|
||||||
|
@ -294,9 +298,7 @@ var PlacesUtils = {
|
||||||
observe: function PU_observe(aSubject, aTopic, aData) {
|
observe: function PU_observe(aSubject, aTopic, aData) {
|
||||||
if (aTopic == "xpcom-shutdown") {
|
if (aTopic == "xpcom-shutdown") {
|
||||||
this.annotations.removeObserver(this);
|
this.annotations.removeObserver(this);
|
||||||
const os = Cc["@mozilla.org/observer-service;1"].
|
this.observerSvc.removeObserver(this, "xpcom-shutdown");
|
||||||
getService(Ci.nsIObserverService);
|
|
||||||
os.removeObserver(this, "xpcom-shutdown");
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1170,62 +1172,6 @@ var PlacesUtils = {
|
||||||
return urls;
|
return urls;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores bookmarks/tags from a JSON file.
|
|
||||||
* WARNING: This method *removes* any bookmarks in the collection before
|
|
||||||
* restoring from the file.
|
|
||||||
*
|
|
||||||
* @param aFile
|
|
||||||
* nsIFile of bookmarks in JSON format to be restored.
|
|
||||||
*/
|
|
||||||
restoreBookmarksFromJSONFile:
|
|
||||||
function PU_restoreBookmarksFromJSONFile(aFile) {
|
|
||||||
var failed = false;
|
|
||||||
var obsServ = Cc["@mozilla.org/observer-service;1"].
|
|
||||||
getService(Ci.nsIObserverService);
|
|
||||||
obsServ.notifyObservers(null,
|
|
||||||
RESTORE_BEGIN_NSIOBSERVER_TOPIC,
|
|
||||||
RESTORE_NSIOBSERVER_DATA);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// open file stream
|
|
||||||
var stream = Cc["@mozilla.org/network/file-input-stream;1"].
|
|
||||||
createInstance(Ci.nsIFileInputStream);
|
|
||||||
stream.init(aFile, 0x01, 0, 0);
|
|
||||||
var converted = Cc["@mozilla.org/intl/converter-input-stream;1"].
|
|
||||||
createInstance(Ci.nsIConverterInputStream);
|
|
||||||
converted.init(stream, "UTF-8", 8192,
|
|
||||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
|
||||||
|
|
||||||
// read in contents
|
|
||||||
var str = {};
|
|
||||||
var jsonStr = "";
|
|
||||||
while (converted.readString(8192, str) != 0)
|
|
||||||
jsonStr += str.value;
|
|
||||||
converted.close();
|
|
||||||
|
|
||||||
if (jsonStr.length == 0)
|
|
||||||
return; // empty file
|
|
||||||
|
|
||||||
this.restoreBookmarksFromJSONString(jsonStr, true);
|
|
||||||
}
|
|
||||||
catch (exc) {
|
|
||||||
failed = true;
|
|
||||||
obsServ.notifyObservers(null,
|
|
||||||
RESTORE_FAILED_NSIOBSERVER_TOPIC,
|
|
||||||
RESTORE_NSIOBSERVER_DATA);
|
|
||||||
Components.utils.reportError("Bookmarks JSON restore failed: " + exc);
|
|
||||||
throw exc;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (!failed) {
|
|
||||||
obsServ.notifyObservers(null,
|
|
||||||
RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
|
|
||||||
RESTORE_NSIOBSERVER_DATA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import bookmarks from a JSON string.
|
* Import bookmarks from a JSON string.
|
||||||
* Note: any item annotated with "places/excludeFromBackup" won't be removed
|
* Note: any item annotated with "places/excludeFromBackup" won't be removed
|
||||||
|
@ -1702,7 +1648,119 @@ var PlacesUtils = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* backupBookmarksToFile()
|
* Helper to create and manage backups.
|
||||||
|
*/
|
||||||
|
backups: {
|
||||||
|
|
||||||
|
get _filenamesRegex() {
|
||||||
|
// Get the localized backup filename, will be used to clear out
|
||||||
|
// old backups with a localized name (bug 445704).
|
||||||
|
let localizedFilename =
|
||||||
|
PlacesUtils.getFormattedString("bookmarksArchiveFilename", [new Date()]);
|
||||||
|
let localizedFilenamePrefix =
|
||||||
|
localizedFilename.substr(0, localizedFilename.indexOf("-"));
|
||||||
|
delete this._filenamesRegex;
|
||||||
|
return this._filenamesRegex =
|
||||||
|
new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-([0-9-]+)\.(json|html)");
|
||||||
|
},
|
||||||
|
|
||||||
|
get folder() {
|
||||||
|
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||||
|
getService(Ci.nsIProperties);
|
||||||
|
let bookmarksBackupDir = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||||
|
bookmarksBackupDir.append("bookmarkbackups");
|
||||||
|
if (!bookmarksBackupDir.exists()) {
|
||||||
|
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
||||||
|
if (!bookmarksBackupDir.exists())
|
||||||
|
throw("Unable to create bookmarks backup folder");
|
||||||
|
}
|
||||||
|
delete this.folder;
|
||||||
|
return this.folder = bookmarksBackupDir;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache current backups in a sorted (by date DESC) array.
|
||||||
|
*/
|
||||||
|
get entries() {
|
||||||
|
delete this.entries;
|
||||||
|
this.entries = [];
|
||||||
|
let files = this.folder.directoryEntries;
|
||||||
|
while (files.hasMoreElements()) {
|
||||||
|
let entry = files.getNext().QueryInterface(Ci.nsIFile);
|
||||||
|
// A valid backup is any file that matches either the localized or
|
||||||
|
// not-localized filename (bug 445704).
|
||||||
|
let matches = entry.leafName.match(this._filenamesRegex);
|
||||||
|
if (!entry.isHidden() && matches) {
|
||||||
|
// Remove bogus backups in future dates.
|
||||||
|
if (this.getDateForFile(entry) > new Date()) {
|
||||||
|
entry.remove(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.entries.sort(function compare(a, b) {
|
||||||
|
aDate = PlacesUtils.backups.getDateForFile(a);
|
||||||
|
bDate = PlacesUtils.backups.getDateForFile(b);
|
||||||
|
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
|
||||||
|
});
|
||||||
|
return this.entries;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a filename for bookmarks backup files.
|
||||||
|
*
|
||||||
|
* @param [optional] aDateObj
|
||||||
|
* Date object used to build the filename.
|
||||||
|
* Will use current date if empty.
|
||||||
|
* @return A bookmarks backup filename.
|
||||||
|
*/
|
||||||
|
getFilenameForDate:
|
||||||
|
function PU_B_getFilenameForDate(aDateObj) {
|
||||||
|
let dateObj = aDateObj || new Date();
|
||||||
|
// Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters
|
||||||
|
// and makes the alphabetical order of multiple backup files more useful.
|
||||||
|
return "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + ".json";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Date object from a backup file. The date is the backup
|
||||||
|
* creation date.
|
||||||
|
*
|
||||||
|
* @param aBackupFile
|
||||||
|
* nsIFile of the backup.
|
||||||
|
* @return A Date object for the backup's creation time.
|
||||||
|
*/
|
||||||
|
getDateForFile:
|
||||||
|
function PU_B_getDateForFile(aBackupFile) {
|
||||||
|
let filename = aBackupFile.leafName;
|
||||||
|
let matches = filename.match(this._filenamesRegex);
|
||||||
|
if (!matches)
|
||||||
|
do_throw("Invalid backup file name: " + filename);
|
||||||
|
return new Date(matches[2].replace(/-/g, "/"));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the most recent backup file.
|
||||||
|
*
|
||||||
|
* @param [optional] aFileExt
|
||||||
|
* Force file extension. Either "html" or "json".
|
||||||
|
* Will check for both if not defined.
|
||||||
|
* @returns nsIFile backup file
|
||||||
|
*/
|
||||||
|
getMostRecent:
|
||||||
|
function PU__B_getMostRecent(aFileExt) {
|
||||||
|
let fileExt = aFileExt || "(json|html)";
|
||||||
|
for (let i = 0; i < this.entries.length; i++) {
|
||||||
|
let rx = new RegExp("\." + fileExt + "$");
|
||||||
|
if (this.entries[i].leafName.match(rx))
|
||||||
|
return this.entries[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saveBookmarksToJSONFile()
|
||||||
*
|
*
|
||||||
* Serializes bookmarks using JSON, and writes to the supplied file.
|
* Serializes bookmarks using JSON, and writes to the supplied file.
|
||||||
* Note: any item that should not be backed up must be annotated with
|
* Note: any item that should not be backed up must be annotated with
|
||||||
|
@ -1711,188 +1769,197 @@ var PlacesUtils = {
|
||||||
* @param aFile
|
* @param aFile
|
||||||
* nsIFile where to save JSON backup.
|
* nsIFile where to save JSON backup.
|
||||||
*/
|
*/
|
||||||
backupBookmarksToFile: function PU_backupBookmarksToFile(aFile) {
|
saveBookmarksToJSONFile:
|
||||||
if (aFile.exists() && !aFile.isWritable())
|
function PU_B_saveBookmarksToFile(aFile) {
|
||||||
return; // XXX
|
if (!aFile.exists())
|
||||||
|
aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
|
||||||
|
if (!aFile.exists() || !aFile.isWritable()) {
|
||||||
|
Cu.reportError("Unable to create bookmarks backup file: " + aFile.leafName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// init stream
|
this._writeBackupFile(aFile);
|
||||||
var stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
|
||||||
|
if (aFile.parent.equals(this.folder)) {
|
||||||
|
// Update internal cache.
|
||||||
|
this.entries.push(aFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If we are saving to a folder different than our backups folder, then
|
||||||
|
// we also want to copy this new backup to it.
|
||||||
|
// This way we ensure the latest valid backup is the same saved by the
|
||||||
|
// user. See bug 424389.
|
||||||
|
var latestBackup = this.getMostRecent("json");
|
||||||
|
if (!latestBackup || latestBackup != aFile) {
|
||||||
|
let name = this.getFilenameForDate();
|
||||||
|
let file = this.folder.clone();
|
||||||
|
file.append(name);
|
||||||
|
if (file.exists())
|
||||||
|
file.remove(false);
|
||||||
|
else {
|
||||||
|
// Update internal cache if we are not replacing an existing
|
||||||
|
// backup file.
|
||||||
|
this.entries.push(file);
|
||||||
|
}
|
||||||
|
aFile.copyTo(this.folder, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_writeBackupFile:
|
||||||
|
function PU_B__writeBackupFile(aFile) {
|
||||||
|
// Init stream.
|
||||||
|
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||||
createInstance(Ci.nsIFileOutputStream);
|
createInstance(Ci.nsIFileOutputStream);
|
||||||
stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0);
|
stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0);
|
||||||
|
|
||||||
// utf-8 converter stream
|
// UTF-8 converter stream.
|
||||||
var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
|
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
|
||||||
createInstance(Ci.nsIConverterOutputStream);
|
createInstance(Ci.nsIConverterOutputStream);
|
||||||
converter.init(stream, "UTF-8", 0, 0x0000);
|
converter.init(stream, "UTF-8", 0, 0x0000);
|
||||||
|
|
||||||
// weep over stream interface variance
|
// Weep over stream interface variance.
|
||||||
var streamProxy = {
|
let streamProxy = {
|
||||||
converter: converter,
|
converter: converter,
|
||||||
write: function(aData, aLen) {
|
write: function(aData, aLen) {
|
||||||
this.converter.writeString(aData);
|
this.converter.writeString(aData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get itemIds to be exluded from the backup
|
// Get list of itemIds that must be exluded from the backup.
|
||||||
var excludeItems = this.annotations
|
let excludeItems =
|
||||||
.getItemsWithAnnotation(EXCLUDE_FROM_BACKUP_ANNO, {});
|
PlacesUtils.annotations.getItemsWithAnnotation(EXCLUDE_FROM_BACKUP_ANNO, {});
|
||||||
|
|
||||||
// query places root
|
// Query the Places root.
|
||||||
var options = this.history.getNewQueryOptions();
|
let options = PlacesUtils.history.getNewQueryOptions();
|
||||||
options.expandQueries = false;
|
options.expandQueries = false;
|
||||||
var query = this.history.getNewQuery();
|
let query = PlacesUtils.history.getNewQuery();
|
||||||
query.setFolders([this.placesRootId], 1);
|
query.setFolders([PlacesUtils.placesRootId], 1);
|
||||||
var result = this.history.executeQuery(query, options);
|
let root = PlacesUtils.history.executeQuery(query, options).root;
|
||||||
result.root.containerOpen = true;
|
root.containerOpen = true;
|
||||||
// serialize as JSON, write to stream
|
// Serialize to JSON and write to stream.
|
||||||
this.serializeNodeAsJSONToOutputStream(result.root, streamProxy,
|
PlacesUtils.serializeNodeAsJSONToOutputStream(root, streamProxy,
|
||||||
false, false, excludeItems);
|
false, false, excludeItems);
|
||||||
result.root.containerOpen = false;
|
root.containerOpen = false;
|
||||||
|
|
||||||
// close converter and stream
|
// Close converter and stream.
|
||||||
converter.close();
|
converter.close();
|
||||||
stream.close();
|
stream.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a filename for bookmarks backup files.
|
* create()
|
||||||
*
|
*
|
||||||
* @param [optional] aDateObj Date object used to build the filename.
|
* Creates a dated backup in <profile>/bookmarkbackups.
|
||||||
* Will use current date if empty.
|
|
||||||
* @return A bookmarks backup filename.
|
|
||||||
*/
|
|
||||||
getBackupFilename:
|
|
||||||
function PU_getBackupFilename(aDateObj) {
|
|
||||||
if (!aDateObj)
|
|
||||||
aDateObj = new Date();
|
|
||||||
// Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters
|
|
||||||
// and makes the alphabetical order of multiple backup files more useful.
|
|
||||||
var date = aDateObj.toLocaleFormat("%Y-%m-%d");
|
|
||||||
return "bookmarks-" + date + ".json";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ArchiveBookmarksFile()
|
|
||||||
*
|
|
||||||
* Creates a dated backup once a day in <profile>/bookmarkbackups.
|
|
||||||
* Stores the bookmarks using JSON.
|
* Stores the bookmarks using JSON.
|
||||||
* Note: any item that should not be backed up must be annotated with
|
* Note: any item that should not be backed up must be annotated with
|
||||||
* "places/excludeFromBackup".
|
* "places/excludeFromBackup".
|
||||||
*
|
*
|
||||||
* @param int aNumberOfBackups - the maximum number of backups to keep
|
* @param [optional] int aMaxBackups
|
||||||
|
* The maximum number of backups to keep.
|
||||||
*
|
*
|
||||||
* @param bool aForceArchive - forces creating an archive even if one was
|
* @param [optional] bool aForceBackup
|
||||||
* already created that day (overwrites)
|
* Forces creating a backup even if one was already
|
||||||
|
* created that day (overwrites).
|
||||||
*/
|
*/
|
||||||
archiveBookmarksFile:
|
create:
|
||||||
function PU_archiveBookmarksFile(aNumberOfBackups, aForceArchive) {
|
function PU_B_create(aMaxBackups, aForceBackup) {
|
||||||
// get/create backups directory
|
|
||||||
var dirService = Cc["@mozilla.org/file/directory_service;1"].
|
|
||||||
getService(Ci.nsIProperties);
|
|
||||||
var bookmarksBackupDir = dirService.get("ProfD", Ci.nsILocalFile);
|
|
||||||
bookmarksBackupDir.append("bookmarkbackups");
|
|
||||||
if (!bookmarksBackupDir.exists()) {
|
|
||||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
|
||||||
if (!bookmarksBackupDir.exists())
|
|
||||||
return; // unable to create directory!
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the new leafname.
|
// Construct the new leafname.
|
||||||
var date = new Date();
|
let newBackupFilename = this.getFilenameForDate();
|
||||||
var backupFilename = this.getBackupFilename(date);
|
let mostRecentBackupFile = this.getMostRecent();
|
||||||
var backupFile = null;
|
|
||||||
if (!aForceArchive) {
|
|
||||||
var backupFileNames = [];
|
|
||||||
var backupFilenamePrefix = backupFilename.substr(0, backupFilename.indexOf("-"));
|
|
||||||
|
|
||||||
// Get the localized backup filename, to clear out
|
if (!aForceBackup) {
|
||||||
// old backups with a localized name (bug 445704).
|
let numberOfBackupsToDelete = 0;
|
||||||
var localizedFilename = this.getFormattedString("bookmarksArchiveFilename", [date]);
|
if (aMaxBackups !== undefined && aMaxBackups > -1)
|
||||||
var localizedFilenamePrefix = localizedFilename.substr(0, localizedFilename.indexOf("-"));
|
numberOfBackupsToDelete = this.entries.length - aMaxBackups;
|
||||||
var rx = new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-([0-9-]+)\.(json|html)");
|
|
||||||
|
|
||||||
var entries = bookmarksBackupDir.directoryEntries;
|
|
||||||
while (entries.hasMoreElements()) {
|
|
||||||
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
|
|
||||||
var backupName = entry.leafName;
|
|
||||||
// A valid backup is any file that matches either the localized or
|
|
||||||
// not-localized filename (bug 445704).
|
|
||||||
var matches = backupName.match(rx);
|
|
||||||
if (matches) {
|
|
||||||
if (backupName == backupFilename)
|
|
||||||
backupFile = entry;
|
|
||||||
backupFileNames.push({ filename: backupName, date: matches[2] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var numberOfBackupsToDelete = 0;
|
|
||||||
if (aNumberOfBackups > -1)
|
|
||||||
numberOfBackupsToDelete = backupFileNames.length - aNumberOfBackups;
|
|
||||||
|
|
||||||
if (numberOfBackupsToDelete > 0) {
|
if (numberOfBackupsToDelete > 0) {
|
||||||
// If we don't have today's backup, remove one more so that
|
// If we don't have today's backup, remove one more so that
|
||||||
// the total backups after this operation does not exceed the
|
// the total backups after this operation does not exceed the
|
||||||
// number specified in the pref.
|
// number specified in the pref.
|
||||||
if (!backupFile)
|
if (!mostRecentBackupFile ||
|
||||||
|
mostRecentBackupFile.leafName != newBackupFilename)
|
||||||
numberOfBackupsToDelete++;
|
numberOfBackupsToDelete++;
|
||||||
backupFileNames.sort(function compare(a, b) {
|
|
||||||
return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
|
|
||||||
});
|
|
||||||
while (numberOfBackupsToDelete--) {
|
while (numberOfBackupsToDelete--) {
|
||||||
let backupFile = bookmarksBackupDir.clone();
|
let oldestBackup = this.entries.pop();
|
||||||
backupFile.append(backupFileNames[0].filename);
|
oldestBackup.remove(false);
|
||||||
backupFile.remove(false);
|
|
||||||
backupFileNames.shift();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do nothing if we either have today's backup already
|
// Do nothing if we already have this backup or we don't want backups.
|
||||||
// or the user has set the pref to zero.
|
if (aMaxBackups === 0 ||
|
||||||
if (backupFile || aNumberOfBackups == 0)
|
(mostRecentBackupFile &&
|
||||||
|
mostRecentBackupFile.leafName == newBackupFilename))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
backupFile = bookmarksBackupDir.clone();
|
let newBackupFile = this.folder.clone();
|
||||||
backupFile.append(backupFilename);
|
newBackupFile.append(newBackupFilename);
|
||||||
|
|
||||||
if (aForceArchive && backupFile.exists())
|
if (aForceBackup && newBackupFile.exists())
|
||||||
backupFile.remove(false);
|
newBackupFile.remove(false);
|
||||||
|
|
||||||
if (!backupFile.exists())
|
if (newBackupFile.exists())
|
||||||
backupFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
|
return;
|
||||||
|
|
||||||
this.backupBookmarksToFile(backupFile);
|
this.saveBookmarksToJSONFile(newBackupFile);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the most recent backup file.
|
* Restores bookmarks and tags from a JSON file.
|
||||||
* @returns nsIFile backup file
|
* WARNING: This method *removes* any bookmarks in the collection before
|
||||||
|
* restoring from the file.
|
||||||
|
*
|
||||||
|
* @param aFile
|
||||||
|
* nsIFile of bookmarks in JSON format to be restored.
|
||||||
*/
|
*/
|
||||||
getMostRecentBackup: function PU_getMostRecentBackup() {
|
restoreBookmarksFromJSONFile:
|
||||||
var dirService = Cc["@mozilla.org/file/directory_service;1"].
|
function PU_B_restoreBookmarksFromJSONFile(aFile) {
|
||||||
getService(Ci.nsIProperties);
|
let failed = false;
|
||||||
var bookmarksBackupDir = dirService.get("ProfD", Ci.nsILocalFile);
|
PlacesUtils.observerSvc.notifyObservers(null,
|
||||||
bookmarksBackupDir.append("bookmarkbackups");
|
RESTORE_BEGIN_NSIOBSERVER_TOPIC,
|
||||||
if (!bookmarksBackupDir.exists())
|
RESTORE_NSIOBSERVER_DATA);
|
||||||
return null;
|
|
||||||
|
|
||||||
var backups = [];
|
try {
|
||||||
var entries = bookmarksBackupDir.directoryEntries;
|
// open file stream
|
||||||
while (entries.hasMoreElements()) {
|
var stream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||||
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
|
createInstance(Ci.nsIFileInputStream);
|
||||||
if (!entry.isHidden() && entry.leafName.match(/^bookmarks-.+(html|json)?$/))
|
stream.init(aFile, 0x01, 0, 0);
|
||||||
backups.push(entry.leafName);
|
var converted = Cc["@mozilla.org/intl/converter-input-stream;1"].
|
||||||
|
createInstance(Ci.nsIConverterInputStream);
|
||||||
|
converted.init(stream, "UTF-8", 8192,
|
||||||
|
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||||
|
|
||||||
|
// read in contents
|
||||||
|
var str = {};
|
||||||
|
var jsonStr = "";
|
||||||
|
while (converted.readString(8192, str) != 0)
|
||||||
|
jsonStr += str.value;
|
||||||
|
converted.close();
|
||||||
|
|
||||||
|
if (jsonStr.length == 0)
|
||||||
|
return; // empty file
|
||||||
|
|
||||||
|
PlacesUtils.restoreBookmarksFromJSONString(jsonStr, true);
|
||||||
|
}
|
||||||
|
catch (exc) {
|
||||||
|
failed = true;
|
||||||
|
PlacesUtils.observerSvc.notifyObservers(null,
|
||||||
|
RESTORE_FAILED_NSIOBSERVER_TOPIC,
|
||||||
|
RESTORE_NSIOBSERVER_DATA);
|
||||||
|
Components.utils.reportError("Bookmarks JSON restore failed: " + exc);
|
||||||
|
throw exc;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (!failed) {
|
||||||
|
PlacesUtils.observerSvc.notifyObservers(null,
|
||||||
|
RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
|
||||||
|
RESTORE_NSIOBSERVER_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backups.length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
backups.sort();
|
|
||||||
var filename = backups.pop();
|
|
||||||
|
|
||||||
var backupFile = bookmarksBackupDir.clone();
|
|
||||||
backupFile.append(filename);
|
|
||||||
return backupFile;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1900,6 +1967,6 @@ var PlacesUtils = {
|
||||||
* this method is called by browser.js in delayed startup.
|
* this method is called by browser.js in delayed startup.
|
||||||
*/
|
*/
|
||||||
startPlacesDBUtils: function PU_startPlacesDBUtils() {
|
startPlacesDBUtils: function PU_startPlacesDBUtils() {
|
||||||
Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm");
|
Cu.import("resource://gre/modules/PlacesDBUtils.jsm");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -239,7 +239,7 @@ function run_test() {
|
||||||
|
|
||||||
// export json to file
|
// export json to file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
|
@ -249,7 +249,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -157,14 +157,14 @@ function run_test() {
|
||||||
test.populate();
|
test.populate();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
do_throw("couldn't export to file: " + ex);
|
do_throw("couldn't export to file: " + ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
do_throw("couldn't import the exported file: " + ex);
|
do_throw("couldn't import the exported file: " + ex);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
do_throw("couldn't import the exported file: " + ex);
|
do_throw("couldn't import the exported file: " + ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ function run_test() {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
tests.forEach(function(aTest) {
|
tests.forEach(function(aTest) {
|
||||||
|
@ -188,7 +188,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile, excludedItemsFromRestore);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile, excludedItemsFromRestore);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -108,7 +108,7 @@ function run_test() {
|
||||||
|
|
||||||
// export json to file
|
// export json to file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
|
@ -118,7 +118,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -130,7 +130,7 @@ function run_test() {
|
||||||
|
|
||||||
// export json to file
|
// export json to file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
|
@ -140,7 +140,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -151,7 +151,7 @@ function run_test() {
|
||||||
|
|
||||||
// export json to file
|
// export json to file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
|
@ -161,7 +161,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -40,17 +40,9 @@ Components.utils.import("resource://gre/modules/utils.js");
|
||||||
|
|
||||||
const NUMBER_OF_BACKUPS = 1;
|
const NUMBER_OF_BACKUPS = 1;
|
||||||
|
|
||||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
|
||||||
getService(Ci.nsIProperties);
|
|
||||||
|
|
||||||
function run_test() {
|
function run_test() {
|
||||||
// Get bookmarkBackups directory
|
// Get bookmarkBackups directory
|
||||||
var bookmarksBackupDir = dirSvc.get("ProfD", Ci.nsILocalFile);
|
var bookmarksBackupDir = PlacesUtils.backups.folder;
|
||||||
bookmarksBackupDir.append("bookmarkbackups");
|
|
||||||
if (!bookmarksBackupDir.exists()) {
|
|
||||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
|
|
||||||
do_check_true(bookmarksBackupDir.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an html dummy backup in the past
|
// Create an html dummy backup in the past
|
||||||
var htmlBackupFile = bookmarksBackupDir.clone();
|
var htmlBackupFile = bookmarksBackupDir.clone();
|
||||||
|
@ -70,15 +62,14 @@ function run_test() {
|
||||||
jsonBackupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
|
jsonBackupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
|
||||||
do_check_true(jsonBackupFile.exists());
|
do_check_true(jsonBackupFile.exists());
|
||||||
|
|
||||||
// Export bookmarks
|
// Export bookmarks to JSON.
|
||||||
var date = new Date().toLocaleFormat("%Y-%m-%d");
|
var backupFilename = PlacesUtils.backups.getFilenameForDate();
|
||||||
var backupFilename = "bookmarks-" + date + ".json";
|
|
||||||
var lastBackupFile = bookmarksBackupDir.clone();
|
var lastBackupFile = bookmarksBackupDir.clone();
|
||||||
lastBackupFile.append(backupFilename);
|
lastBackupFile.append(backupFilename);
|
||||||
if (lastBackupFile.exists())
|
if (lastBackupFile.exists())
|
||||||
lastBackupFile.remove(false);
|
lastBackupFile.remove(false);
|
||||||
do_check_false(lastBackupFile.exists());
|
do_check_false(lastBackupFile.exists());
|
||||||
PlacesUtils.archiveBookmarksFile(NUMBER_OF_BACKUPS);
|
PlacesUtils.backups.create(NUMBER_OF_BACKUPS);
|
||||||
do_check_true(lastBackupFile.exists());
|
do_check_true(lastBackupFile.exists());
|
||||||
|
|
||||||
// Check that last backup has been retained
|
// Check that last backup has been retained
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Bug 466303 code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Marco Bonardo <mak77@bonardo.net>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
Components.utils.import("resource://gre/modules/utils.js");
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
let bookmarksBackupDir = PlacesUtils.backups.folder;
|
||||||
|
// Remove all files from backups folder.
|
||||||
|
let files = bookmarksBackupDir.directoryEntries;
|
||||||
|
while (files.hasMoreElements()) {
|
||||||
|
let entry = files.getNext().QueryInterface(Ci.nsIFile);
|
||||||
|
entry.remove(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a json dummy backup in the future.
|
||||||
|
let dateObj = new Date();
|
||||||
|
dateObj.setYear(dateObj.getFullYear() + 1);
|
||||||
|
let name = PlacesUtils.backups.getFilenameForDate(dateObj);
|
||||||
|
do_check_eq(name, "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + ".json");
|
||||||
|
let futureBackupFile = bookmarksBackupDir.clone();
|
||||||
|
futureBackupFile.append(name);
|
||||||
|
if (futureBackupFile.exists())
|
||||||
|
futureBackupFile.remove(false);
|
||||||
|
do_check_false(futureBackupFile.exists());
|
||||||
|
futureBackupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
|
||||||
|
do_check_true(futureBackupFile.exists());
|
||||||
|
|
||||||
|
do_check_eq(PlacesUtils.backups.entries.length, 0);
|
||||||
|
|
||||||
|
PlacesUtils.backups.create();
|
||||||
|
|
||||||
|
// Check that a backup for today has been created.
|
||||||
|
do_check_eq(PlacesUtils.backups.entries.length, 1);
|
||||||
|
let mostRecentBackupFile = PlacesUtils.backups.getMostRecent();
|
||||||
|
do_check_neq(mostRecentBackupFile, null);
|
||||||
|
let todayName = PlacesUtils.backups.getFilenameForDate();
|
||||||
|
do_check_eq(mostRecentBackupFile.leafName, todayName);
|
||||||
|
|
||||||
|
// Check that future backup has been removed.
|
||||||
|
do_check_false(futureBackupFile.exists());
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
mostRecentBackupFile.remove(false);
|
||||||
|
do_check_false(mostRecentBackupFile.exists());
|
||||||
|
}
|
|
@ -96,7 +96,7 @@ function run_test() {
|
||||||
|
|
||||||
// export json to file
|
// export json to file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||||
|
|
||||||
// clean
|
// clean
|
||||||
|
@ -106,7 +106,7 @@ function run_test() {
|
||||||
|
|
||||||
// restore json file
|
// restore json file
|
||||||
try {
|
try {
|
||||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
PlacesUtils.backups.restoreBookmarksFromJSONFile(jsonFile);
|
||||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
|
|
|
@ -66,15 +66,7 @@ function run_test() {
|
||||||
dates.sort();
|
dates.sort();
|
||||||
|
|
||||||
// Get and cleanup the backups folder.
|
// Get and cleanup the backups folder.
|
||||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
var bookmarksBackupDir = PlacesUtils.backups.folder;
|
||||||
getService(Ci.nsIProperties);
|
|
||||||
var bookmarksBackupDir = dirSvc.get("ProfD", Ci.nsILocalFile);
|
|
||||||
bookmarksBackupDir.append("bookmarkbackups");
|
|
||||||
if (bookmarksBackupDir.exists()) {
|
|
||||||
bookmarksBackupDir.remove(true);
|
|
||||||
do_check_false(bookmarksBackupDir.exists());
|
|
||||||
}
|
|
||||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
|
|
||||||
|
|
||||||
// Fake backups are created backwards to ensure we won't consider file
|
// Fake backups are created backwards to ensure we won't consider file
|
||||||
// creation time.
|
// creation time.
|
||||||
|
@ -98,7 +90,7 @@ function run_test() {
|
||||||
return LOCALIZED_PREFIX + aValue;
|
return LOCALIZED_PREFIX + aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlacesUtils.archiveBookmarksFile(Math.floor(dates.length/2));
|
PlacesUtils.backups.create(Math.floor(dates.length/2));
|
||||||
// Add today's backup.
|
// Add today's backup.
|
||||||
dates.push(dateObj.toLocaleFormat("%Y-%m-%d"));
|
dates.push(dateObj.toLocaleFormat("%Y-%m-%d"));
|
||||||
|
|
||||||
|
@ -123,5 +115,6 @@ function run_test() {
|
||||||
// Cleanup backups folder.
|
// Cleanup backups folder.
|
||||||
bookmarksBackupDir.remove(true);
|
bookmarksBackupDir.remove(true);
|
||||||
do_check_false(bookmarksBackupDir.exists());
|
do_check_false(bookmarksBackupDir.exists());
|
||||||
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
|
// Recreate the folder.
|
||||||
|
PlacesUtils.backups.folder;
|
||||||
}
|
}
|
Загрузка…
Ссылка в новой задаче