Bug 818593 - Add file size to bookmarks restore UI. r=mak

This commit is contained in:
Raymond Lee 2013-07-31 22:51:09 +08:00
parent b168795293
commit 7b07fe5baf
15 changed files with 326 additions and 86 deletions

View File

@ -11,6 +11,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils",
"resource://gre/modules/BookmarkJSONUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
"resource://gre/modules/PlacesBackups.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm");
var PlacesOrganizer = {
_places: null,
@ -411,6 +413,21 @@ var PlacesOrganizer = {
// Populate menu with backups.
for (let i = 0; i < backupFiles.length; i++) {
let [size, unit] = DownloadUtils.convertByteUnits(backupFiles[i].fileSize);
let sizeString = PlacesUtils.getFormattedString("backupFileSizeText",
[size, unit]);
let sizeInfo;
let bookmarkCount = PlacesBackups.getBookmarkCountForFile(backupFiles[i]);
if (bookmarkCount != null) {
sizeInfo = " (" + sizeString + " - " +
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
bookmarkCount,
[bookmarkCount]) +
")";
} else {
sizeInfo = " (" + sizeString + ")";
}
let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
let m = restorePopup.insertBefore(document.createElement("menuitem"),
document.getElementById("restoreFromFile"));
@ -419,7 +436,8 @@ var PlacesOrganizer = {
Ci.nsIScriptableDateFormat.dateFormatLong,
backupDate.getFullYear(),
backupDate.getMonth() + 1,
backupDate.getDate()));
backupDate.getDate()) +
sizeInfo);
m.setAttribute("value", backupFiles[i].leafName);
m.setAttribute("oncommand",
"PlacesOrganizer.onRestoreMenuItemClick(this);");
@ -516,7 +534,7 @@ var PlacesOrganizer = {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) {
if (aResult != Ci.nsIFilePicker.returnCancel) {
BookmarkJSONUtils.exportToFile(fp.file);
PlacesBackups.saveBookmarksToJSONFile(fp.file);
}
};

View File

@ -42,6 +42,7 @@ function run_test() {
create_bookmarks_html("bookmarks.glue.html");
// Create our JSON backup copying bookmarks.glue.json to the profile folder.
remove_all_JSON_backups();
create_JSON_backup("bookmarks.glue.json");
// Remove current database file.

View File

@ -275,6 +275,7 @@ function run_test()
{
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
remove_all_JSON_backups();
// Create our JSON backup from bookmarks.glue.json.
create_JSON_backup("bookmarks.glue.json");

View File

@ -43,6 +43,7 @@ function run_test() {
// Create our JSON backup copying bookmarks.glue.json to the profile
// folder. It will be ignored.
remove_all_JSON_backups();
create_JSON_backup("bookmarks.glue.json");
// Remove current database file.

View File

@ -29,6 +29,8 @@ let tests = [];
tests.push({
description: "Export to bookmarks.html if autoExportHTML is true.",
exec: function() {
remove_all_JSON_backups();
// Sanity check: we should have bookmarks on the toolbar.
do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
@ -43,7 +45,7 @@ tests.push({
// Check bookmarks.html has been created.
check_bookmarks_html();
// Check JSON backup has been created.
check_JSON_backup();
check_JSON_backup(true);
// Check preferences have not been reverted.
do_check_true(ps.getBoolPref(PREF_AUTO_EXPORT_HTML));
@ -128,16 +130,10 @@ tests.push({
//------------------------------------------------------------------------------
function finish_test() {
do_test_finished();
}
var testIndex = 0;
function next_test() {
// Remove bookmarks.html from profile.
remove_bookmarks_html();
// Remove JSON backups from profile.
remove_all_JSON_backups();
// Execute next test.
let test = tests.shift();

View File

@ -19,8 +19,8 @@ const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
let EXPECTED_NOTIFICATIONS = [
"places-shutdown"
, "places-will-close-connection"
, "places-expiration-finished"
, "places-will-close-connection"
, "places-connection-closed"
];

View File

@ -7,6 +7,7 @@ this.EXPORTED_SYMBOLS = [ "BookmarkJSONUtils" ];
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
@ -513,6 +514,7 @@ BookmarkExporter.prototype = {
createInstance(Ci.nsIFileOutputStream);
safeFileOut.init(aLocalFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
FileUtils.MODE_TRUNCATE, parseInt("0600", 8), 0);
let nodeCount;
try {
// We need a buffered output stream for performance. See bug 202477.
@ -525,7 +527,7 @@ BookmarkExporter.prototype = {
createInstance(Ci.nsIConverterOutputStream);
this._converterOut.init(bufferedOut, "utf-8", 0, 0);
try {
yield this._writeContentToFile();
nodeCount = yield this._writeContentToFile();
// Flush the buffer and retain the target file on success only.
bufferedOut.QueryInterface(Ci.nsISafeOutputStream).finish();
@ -539,27 +541,34 @@ BookmarkExporter.prototype = {
} finally {
safeFileOut.close();
}
throw new Task.Result(nodeCount);
},
_writeContentToFile: function BE__writeContentToFile() {
// Weep over stream interface variance.
let streamProxy = {
converter: this._converterOut,
write: function(aData, aLen) {
this.converter.writeString(aData);
}
};
return Task.spawn(function() {
// Weep over stream interface variance.
let streamProxy = {
converter: this._converterOut,
write: function(aData, aLen) {
this.converter.writeString(aData);
}
};
// Get list of itemIds that must be excluded from the backup.
let excludeItems = PlacesUtils.annotations.getItemsWithAnnotation(
PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
let root = PlacesUtils.getFolderContents(PlacesUtils.placesRootId, false,
false).root;
// Get list of itemIds that must be excluded from the backup.
let excludeItems = PlacesUtils.annotations.getItemsWithAnnotation(
PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
let root = PlacesUtils.getFolderContents(PlacesUtils.placesRootId, false,
false).root;
// Serialize to JSON and write to stream.
let nodeCount = yield BookmarkNode.serializeAsJSONToOutputStream(root,
streamProxy,
false,
false,
excludeItems);
root.containerOpen = false;
// Serialize to JSON and write to stream.
yield BookmarkNode.serializeAsJSONToOutputStream(root, streamProxy, false, false,
excludeItems);
root.containerOpen = false;
throw new Task.Result(nodeCount);
}.bind(this));
}
}
@ -582,6 +591,7 @@ let BookmarkNode = {
* @param aExcludeItems
* An array of item ids that should not be written to the backup.
* @returns Task promise
* @resolves the number of serialized uri nodes.
*/
serializeAsJSONToOutputStream: function BN_serializeAsJSONToOutputStream(
aNode, aStream, aIsUICommand, aResolveShortcuts, aExcludeItems) {
@ -589,20 +599,25 @@ let BookmarkNode = {
return Task.spawn(function() {
// Serialize to stream
let array = [];
if (yield this._appendConvertedNode(aNode, null, array, aIsUICommand,
aResolveShortcuts, aExcludeItems)) {
let result = yield this._appendConvertedNode(aNode, null, array,
aIsUICommand,
aResolveShortcuts,
aExcludeItems);
if (result.appendedNode) {
let json = JSON.stringify(array[0]);
aStream.write(json, json.length);
} else {
throw Cr.NS_ERROR_UNEXPECTED;
}
}.bind(this));
throw new Task.Result(result.nodeCount);
}.bind(this));
},
_appendConvertedNode: function BN__appendConvertedNode(
bNode, aIndex, aArray, aIsUICommand, aResolveShortcuts, aExcludeItems) {
return Task.spawn(function() {
let node = {};
let nodeCount = 0;
// Set index in order received
// XXX handy shortcut, but are there cases where we don't want
@ -618,7 +633,7 @@ let BookmarkNode = {
if (PlacesUtils.nodeIsURI(bNode)) {
// Tag root accept only folder nodes
if (parent && parent.itemId == PlacesUtils.tagsFolderId)
throw new Task.Result(false);
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
// Check for url validity, since we can't halt while writing a backup.
// This will throw if we try to serialize an invalid url and it does
@ -626,14 +641,15 @@ let BookmarkNode = {
try {
NetUtil.newURI(bNode.uri);
} catch (ex) {
throw new Task.Result(false);
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
}
yield this._addURIProperties(bNode, node, aIsUICommand);
nodeCount++;
} else if (PlacesUtils.nodeIsContainer(bNode)) {
// Tag containers accept only uri nodes
if (grandParent && grandParent.itemId == PlacesUtils.tagsFolderId)
throw new Task.Result(false);
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
this._addContainerProperties(bNode, node, aIsUICommand,
aResolveShortcuts);
@ -642,19 +658,24 @@ let BookmarkNode = {
// Tag containers accept only uri nodes
if ((parent && parent.itemId == PlacesUtils.tagsFolderId) ||
(grandParent && grandParent.itemId == PlacesUtils.tagsFolderId))
throw new Task.Result(false);
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
this._addSeparatorProperties(bNode, node);
}
if (!node.feedURI && node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
throw new Task.Result(yield this._appendConvertedComplexNode(node, bNode, aArray, aIsUICommand,
aResolveShortcuts, aExcludeItems));
nodeCount += yield this._appendConvertedComplexNode(node,
bNode,
aArray,
aIsUICommand,
aResolveShortcuts,
aExcludeItems)
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
}
aArray.push(node);
throw new Task.Result(true);
}.bind(this));
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
}.bind(this));
},
_addGenericProperties: function BN__addGenericProperties(
@ -765,6 +786,7 @@ let BookmarkNode = {
aExcludeItems) {
return Task.spawn(function() {
let repr = {};
let nodeCount = 0;
for (let [name, value] in Iterator(aNode))
repr[name] = value;
@ -781,16 +803,17 @@ let BookmarkNode = {
let childNode = aSourceNode.getChild(i);
if (aExcludeItems && aExcludeItems.indexOf(childNode.itemId) != -1)
continue;
yield this._appendConvertedNode(aSourceNode.getChild(i), i, children,
aIsUICommand, aResolveShortcuts,
aExcludeItems);
let result = yield this._appendConvertedNode(aSourceNode.getChild(i), i, children,
aIsUICommand, aResolveShortcuts,
aExcludeItems);
nodeCount += result.nodeCount;
}
if (!wasOpen)
aSourceNode.containerOpen = false;
}
aArray.push(repr);
throw new Task.Result(true);
throw new Task.Result(nodeCount);
}.bind(this));
}
}

View File

@ -9,11 +9,17 @@ this.EXPORTED_SYMBOLS = ["PlacesBackups"];
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
this.PlacesBackups = {
get _filenamesRegex() {
// Get the localized backup filename, will be used to clear out
@ -24,7 +30,7 @@ this.PlacesBackups = {
localizedFilename.substr(0, localizedFilename.indexOf("-"));
delete this._filenamesRegex;
return this._filenamesRegex =
new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-([0-9-]+)\.(json|html)");
new RegExp("^(bookmarks|" + localizedFilenamePrefix + ")-([0-9-]+)(_[0-9]+)*\.(json|html)");
},
get folder() {
@ -127,6 +133,7 @@ this.PlacesBackups = {
* @param aFile
* nsIFile where to save JSON backup.
* @return {Promise}
* @resolves the number of serialized uri nodes.
*/
saveBookmarksToJSONFile: function PB_saveBookmarksToJSONFile(aFile) {
return Task.spawn(function() {
@ -136,7 +143,7 @@ this.PlacesBackups = {
throw new Error("Unable to create bookmarks backup file: " + aFile.leafName);
}
yield BookmarkJSONUtils.exportToFile(aFile);
let nodeCount = yield BookmarkJSONUtils.exportToFile(aFile);
if (aFile.parent.equals(this.folder)) {
// Update internal cache.
@ -146,21 +153,25 @@ this.PlacesBackups = {
// 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.
let latestBackup = this.getMostRecent("json");
if (!latestBackup || latestBackup != aFile) {
let name = this.getFilenameForDate();
let name = this.getFilenameForDate();
let newFilename = this._appendMetaDataToFilename(name,
{ nodeCount: nodeCount });
let backupFile = yield this._getBackupFileForSameDate(name);
if (backupFile) {
backupFile.remove(false);
} else {
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);
file.append(newFilename);
// Update internal cache if we are not replacing an existing
// backup file.
this.entries.push(file);
}
aFile.copyTo(this.folder, newFilename);
}
throw new Task.Result(nodeCount);
}.bind(this));
},
@ -193,7 +204,8 @@ this.PlacesBackups = {
// the total backups after this operation does not exceed the
// number specified in the pref.
if (!mostRecentBackupFile ||
mostRecentBackupFile.leafName != newBackupFilename)
!this._isFilenameWithSameDate(mostRecentBackupFile.leafName,
newBackupFilename))
numberOfBackupsToDelete++;
while (numberOfBackupsToDelete--) {
@ -205,20 +217,89 @@ this.PlacesBackups = {
// Do nothing if we already have this backup or we don't want backups.
if (aMaxBackups === 0 ||
(mostRecentBackupFile &&
mostRecentBackupFile.leafName == newBackupFilename))
this._isFilenameWithSameDate(mostRecentBackupFile.leafName,
newBackupFilename)))
return;
}
let backupFile = yield this._getBackupFileForSameDate(newBackupFilename);
if (backupFile) {
if (aForceBackup)
backupFile.remove(false);
else
return;
}
// Save bookmarks to a backup file.
let newBackupFile = this.folder.clone();
newBackupFile.append(newBackupFilename);
let nodeCount = yield this.saveBookmarksToJSONFile(newBackupFile);
if (aForceBackup && newBackupFile.exists())
newBackupFile.remove(false);
// Rename the filename with metadata.
let newFilenameWithMetaData = this._appendMetaDataToFilename(
newBackupFile.leafName,
{ nodeCount: nodeCount });
newBackupFile.moveTo(this.folder, newFilenameWithMetaData);
if (newBackupFile.exists())
return;
yield this.saveBookmarksToJSONFile(newBackupFile);
// Update internal cache.
let newFileWithMetaData = this.folder.clone();
newFileWithMetaData.append(newFilenameWithMetaData);
this.entries.pop();
this.entries.push(newFileWithMetaData);
}.bind(this));
}
},
_appendMetaDataToFilename:
function PB__appendMetaDataToFilename(aFilename, aMetaData) {
let matches = aFilename.match(this._filenamesRegex);
let newFilename = matches[1] + "-" + matches[2] + "_" +
aMetaData.nodeCount + "." + matches[4];
return newFilename;
},
/**
* Gets the bookmark count for backup file.
*
* @param aFile
* String The backup file.
*
* @return the bookmark count or null.
*/
getBookmarkCountForFile: function PB_getBookmarkCountForFile(aFile) {
let count = null;
let matches = aFile.leafName.match(this._filenamesRegex);
if (matches && matches[3])
count = matches[3].replace(/_/g, "");
return count;
},
_isFilenameWithSameDate:
function PB__isFilenameWithSameDate(aSourceName, aTargetName) {
let sourceMatches = aSourceName.match(this._filenamesRegex);
let targetMatches = aTargetName.match(this._filenamesRegex);
return (sourceMatches && targetMatches &&
sourceMatches[1] == targetMatches[1] &&
sourceMatches[2] == targetMatches[2] &&
sourceMatches[4] == targetMatches[4]);
},
_getBackupFileForSameDate:
function PB__getBackupFileForSameDate(aFilename) {
return Task.spawn(function() {
let iterator = new OS.File.DirectoryIterator(this.folder.path);
let backupFile;
yield iterator.forEach(function(aEntry) {
if (this._isFilenameWithSameDate(aEntry.name, aFilename)) {
backupFile = new FileUtils.File(aEntry.path);
return iterator.close();
}
}.bind(this));
yield iterator.close();
throw new Task.Result(backupFile);
}.bind(this));
}
}

View File

@ -32,14 +32,23 @@ function run_test() {
// Export bookmarks to JSON.
var backupFilename = PlacesBackups.getFilenameForDate();
var lastBackupFile = bookmarksBackupDir.clone();
lastBackupFile.append(backupFilename);
if (lastBackupFile.exists())
lastBackupFile.remove(false);
do_check_false(lastBackupFile.exists());
var rx = new RegExp("^" + backupFilename.replace(/\.json/, "") + "(_[0-9]+){0,1}\.json$");
var files = bookmarksBackupDir.directoryEntries;
var entry;
while (files.hasMoreElements()) {
entry = files.getNext().QueryInterface(Ci.nsIFile);
if (entry.leafName.match(rx))
entry.remove(false);
}
Task.spawn(function() {
yield PlacesBackups.create(NUMBER_OF_BACKUPS);
files = bookmarksBackupDir.directoryEntries;
while (files.hasMoreElements()) {
entry = files.getNext().QueryInterface(Ci.nsIFile);
if (entry.leafName.match(rx))
lastBackupFile = entry;
}
do_check_true(lastBackupFile.exists());
// Check that last backup has been retained

View File

@ -20,11 +20,16 @@ function run_test() {
dateObj.setYear(dateObj.getFullYear() + 1);
let name = PlacesBackups.getFilenameForDate(dateObj);
do_check_eq(name, "bookmarks-" + dateObj.toLocaleFormat("%Y-%m-%d") + ".json");
let rx = new RegExp("^" + name.replace(/\.json/, "") + "(_[0-9]+){0,1}\.json$");
let files = bookmarksBackupDir.directoryEntries;
while (files.hasMoreElements()) {
let entry = files.getNext().QueryInterface(Ci.nsIFile);
if (entry.leafName.match(rx))
entry.remove(false);
}
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());
@ -36,8 +41,9 @@ function run_test() {
do_check_eq(PlacesBackups.entries.length, 1);
let mostRecentBackupFile = PlacesBackups.getMostRecent();
do_check_neq(mostRecentBackupFile, null);
let todayName = PlacesBackups.getFilenameForDate();
do_check_eq(mostRecentBackupFile.leafName, todayName);
let todayFilename = PlacesBackups.getFilenameForDate();
rx = new RegExp("^" + todayFilename.replace(/\.json/, "") + "(_[0-9]+){0,1}\.json$");
do_check_true(mostRecentBackupFile.leafName.match(rx).length > 0);
// Check that future backup has been removed.
do_check_false(futureBackupFile.exists());

View File

@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* To confirm that metadata i.e. bookmark count is set and retrieved for
* automatic backups.
*/
function run_test() {
run_next_test();
}
add_task(function test_saveBookmarksToJSONFile_and_create()
{
// Add a bookmark
let uri = NetUtil.newURI("http://getfirefox.com/");
let bookmarkId =
PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId, uri,
PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!");
// Test saveBookmarksToJSONFile()
let backupFile = FileUtils.getFile("TmpD", ["bookmarks.json"]);
backupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, parseInt("0600", 8));
let nodeCount = yield PlacesBackups.saveBookmarksToJSONFile(backupFile, true);
do_check_true(nodeCount > 0);
do_check_true(backupFile.exists());
do_check_eq(backupFile.leafName, "bookmarks.json");
// Ensure the backup would be copied to our backups folder when the original
// backup is saved somewhere else.
let recentBackup = PlacesBackups.getMostRecent();
let todayFilename = PlacesBackups.getFilenameForDate();
do_check_eq(recentBackup.leafName,
todayFilename.replace(/\.json/, "_" + nodeCount + ".json"));
// Clear all backups in our backups folder.
yield PlacesBackups.create(0);
do_check_eq(PlacesBackups.entries.length, 0);
// Test create() which saves bookmarks with metadata on the filename.
yield PlacesBackups.create();
do_check_eq(PlacesBackups.entries.length, 1);
mostRecentBackupFile = PlacesBackups.getMostRecent();
do_check_neq(mostRecentBackupFile, null);
let rx = new RegExp("^" + todayFilename.replace(/\.json/, "") + "_([0-9]+)\.json$");
let matches = mostRecentBackupFile.leafName.match(rx);
do_check_true(matches.length > 0 && parseInt(matches[1]) == nodeCount);
// Cleanup
backupFile.remove(false);
yield PlacesBackups.create(0);
PlacesUtils.bookmarks.removeItem(bookmarkId);
});

View File

@ -29,3 +29,4 @@ tail =
[test_675416.js]
[test_711914.js]
[test_protectRoots.js]
[test_818593-store-backup-metadata.js]

View File

@ -470,18 +470,22 @@ function check_bookmarks_html() {
function create_JSON_backup(aFilename) {
if (!aFilename)
do_throw("you must pass a filename to create_JSON_backup function");
remove_all_JSON_backups();
let bookmarksBackupDir = gProfD.clone();
bookmarksBackupDir.append("bookmarkbackups");
if (!bookmarksBackupDir.exists()) {
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
do_check_true(bookmarksBackupDir.exists());
}
let profileBookmarksJSONFile = bookmarksBackupDir.clone();
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
if (profileBookmarksJSONFile.exists()) {
profileBookmarksJSONFile.remove();
}
let bookmarksJSONFile = gTestDir.clone();
bookmarksJSONFile.append(aFilename);
do_check_true(bookmarksJSONFile.exists());
bookmarksJSONFile.copyTo(bookmarksBackupDir, FILENAME_BOOKMARKS_JSON);
let profileBookmarksJSONFile = bookmarksBackupDir.clone();
profileBookmarksJSONFile = bookmarksBackupDir.clone();
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
do_check_true(profileBookmarksJSONFile.exists());
return profileBookmarksJSONFile;
@ -504,12 +508,30 @@ function remove_all_JSON_backups() {
/**
* Check a JSON backup file for today exists in the profile folder.
*
* @param aIsAutomaticBackup The boolean indicates whether it's an automatic
* backup.
* @return nsIFile object for the file.
*/
function check_JSON_backup() {
let profileBookmarksJSONFile = gProfD.clone();
profileBookmarksJSONFile.append("bookmarkbackups");
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
function check_JSON_backup(aIsAutomaticBackup) {
let profileBookmarksJSONFile;
if (aIsAutomaticBackup) {
let bookmarksBackupDir = gProfD.clone();
bookmarksBackupDir.append("bookmarkbackups");
let files = bookmarksBackupDir.directoryEntries;
let backup_date = new Date().toLocaleFormat("%Y-%m-%d");
let rx = new RegExp("^bookmarks-" + backup_date + "_[0-9]+\.json$");
while (files.hasMoreElements()) {
let entry = files.getNext().QueryInterface(Ci.nsIFile);
if (entry.leafName.match(rx)) {
profileBookmarksJSONFile = entry;
break;
}
}
} else {
profileBookmarksJSONFile = gProfD.clone();
profileBookmarksJSONFile.append("bookmarkbackups");
profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
}
do_check_true(profileBookmarksJSONFile.exists());
return profileBookmarksJSONFile;
}

View File

@ -66,23 +66,40 @@ function run_test() {
for (var i = 0; i < dates.length; i++) {
let backupFilename;
let shouldExist;
let backupFile;
if (i > Math.floor(dates.length/2)) {
backupFilename = PREFIX + dates[i] + SUFFIX;
let files = bookmarksBackupDir.directoryEntries;
let rx = new RegExp("^" + PREFIX + dates[i] + "(_[0-9]+){0,1}" + SUFFIX + "$");
while (files.hasMoreElements()) {
let entry = files.getNext().QueryInterface(Ci.nsIFile);
if (entry.leafName.match(rx)) {
backupFilename = entry.leafName;
backupFile = entry;
break;
}
}
shouldExist = true;
}
else {
backupFilename = LOCALIZED_PREFIX + dates[i] + SUFFIX;
backupFile = bookmarksBackupDir.clone();
backupFile.append(backupFilename);
shouldExist = false;
}
var backupFile = bookmarksBackupDir.clone();
backupFile.append(backupFilename);
if (backupFile.exists() != shouldExist)
do_throw("Backup should " + (shouldExist ? "" : "not") + " exist: " + backupFilename);
}
// Cleanup backups folder.
bookmarksBackupDir.remove(true);
do_check_false(bookmarksBackupDir.exists());
// XXX: Can't use bookmarksBackupDir.remove(true) because file lock happens
// on WIN XP.
let files = bookmarksBackupDir.directoryEntries;
while (files.hasMoreElements()) {
let entry = files.getNext().QueryInterface(Ci.nsIFile);
entry.remove(false);
}
do_check_false(bookmarksBackupDir.directoryEntries.hasMoreElements());
// Recreate the folder.
PlacesBackups.folder;

View File

@ -30,3 +30,10 @@ localhost=(local files)
# before bug 445704 was fixed. It will be removed
# in a subsequent release.
bookmarksArchiveFilename=bookmarks-%S.json
# LOCALIZATION NOTE
# The string is used for showing file size of each backup in the "fileRestorePopup" popup
# %1$S is the file size
# %2$S is the file size unit
backupFileSizeText=%1$S %2$S