зеркало из https://github.com/mozilla/pjs.git
Bug 405938 - problems when import/exporting of saved searches (r=mano, a=mconnor)
This commit is contained in:
Родитель
7dcd98e254
Коммит
4e84f21e7c
|
@ -1047,8 +1047,12 @@ var PlacesUtils = {
|
|||
});
|
||||
|
||||
var batch = {
|
||||
_utils: this,
|
||||
nodes: nodes[0].children,
|
||||
runBatched: function restore_runBatched() {
|
||||
var searchIds = [];
|
||||
var folderIdMap = [];
|
||||
|
||||
this.nodes.forEach(function(node) {
|
||||
var root = node.root;
|
||||
// FIXME support folders other than known roots
|
||||
|
@ -1079,23 +1083,34 @@ var PlacesUtils = {
|
|||
if (container != this.tagsFolderId)
|
||||
this.bookmarks.removeFolderChildren(container);
|
||||
else {
|
||||
// remove tags via the tagging service
|
||||
var tags = this.tagging.allTags;
|
||||
var uris = [];
|
||||
tags.forEach(function(aTag) {
|
||||
var tagURIs = this.tagging.getURIsForTag(aTag);
|
||||
for (let i in tagURIs)
|
||||
this.tagging.untagURI(tagURIs[i], [aTag]);
|
||||
}, this);
|
||||
// remove tags via the tagging service
|
||||
var tags = this.tagging.allTags;
|
||||
var uris = [];
|
||||
tags.forEach(function(aTag) {
|
||||
var tagURIs = this.tagging.getURIsForTag(aTag);
|
||||
for (let i in tagURIs)
|
||||
this.tagging.untagURI(tagURIs[i], [aTag]);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
// insert the data into the db
|
||||
node.children.forEach(function(child) {
|
||||
var index = child.index;
|
||||
this.importJSONNode(child, container, index);
|
||||
var [folders, searches] = this.importJSONNode(child, container, index);
|
||||
folderIdMap = folderIdMap.concat(folders);
|
||||
searchIds = searchIds.concat(searches);
|
||||
}, this);
|
||||
}, PlacesUtils);
|
||||
}, this._utils);
|
||||
|
||||
// fixup imported place: uris that contain folders
|
||||
searchIds.forEach(function(aId) {
|
||||
var oldURI = this.bookmarks.getBookmarkURI(aId);
|
||||
var uri = this._fixupQuery(this.bookmarks.getBookmarkURI(aId),
|
||||
folderIdMap);
|
||||
if (!uri.equals(oldURI))
|
||||
this.bookmarks.changeBookmarkURI(aId, uri);
|
||||
}, this._utils);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1111,9 +1126,13 @@ var PlacesUtils = {
|
|||
* The container the data was dropped or pasted into
|
||||
* @param aIndex
|
||||
* The index within the container the item was dropped or pasted at
|
||||
* @returns an array containing of maps of old folder ids to new folder ids,
|
||||
* and an array of saved search ids that need to be fixed up.
|
||||
* eg: [[[oldFolder1, newFolder1]], [search1]]
|
||||
*/
|
||||
importJSONNode: function PU_importJSONNode(aData, aContainer, aIndex) {
|
||||
// create item
|
||||
var folderIdMap = [];
|
||||
var searchIds = [];
|
||||
var id = -1;
|
||||
switch (aData.type) {
|
||||
case this.TYPE_X_MOZ_PLACE_CONTAINER:
|
||||
|
@ -1122,7 +1141,7 @@ var PlacesUtils = {
|
|||
aData.children.forEach(function(aChild) {
|
||||
this.tagging.tagURI(this._uri(aChild.uri), [aData.title]);
|
||||
}, this);
|
||||
return;
|
||||
return [folderIdMap, searchIds];
|
||||
}
|
||||
}
|
||||
else if (aData.livemark && aData.annos) {
|
||||
|
@ -1146,10 +1165,13 @@ var PlacesUtils = {
|
|||
}
|
||||
else {
|
||||
id = this.bookmarks.createFolder(aContainer, aData.title, aIndex);
|
||||
folderIdMap.push([aData.id, id]);
|
||||
// process children
|
||||
if (aData.children) {
|
||||
aData.children.every(function(aChild, aIndex) {
|
||||
this.importJSONNode(aChild, id, aIndex);
|
||||
var [folderIds, searches] = this.importJSONNode(aChild, id, aIndex);
|
||||
folderIdMap = folderIdMap.concat(folderIds);
|
||||
searchIds = searchIds.concat(searches);
|
||||
return true;
|
||||
}, this);
|
||||
}
|
||||
|
@ -1166,6 +1188,8 @@ var PlacesUtils = {
|
|||
}
|
||||
if (aData.charset)
|
||||
this.history.setCharsetForURI(this._uri(aData.uri), aData.charset);
|
||||
if (aData.uri.match(/^place:/))
|
||||
searchIds.push(id);
|
||||
break;
|
||||
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
|
||||
id = this.bookmarks.insertSeparator(aContainer, aIndex);
|
||||
|
@ -1180,6 +1204,45 @@ var PlacesUtils = {
|
|||
if (aData.annos)
|
||||
this.setAnnotationsForItem(id, aData.annos);
|
||||
}
|
||||
|
||||
return [folderIdMap, searchIds];
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces imported folder ids with their local counterparts in a place: URI.
|
||||
*
|
||||
* @param aURI
|
||||
* A place: URI with folder ids.
|
||||
* @param aFolderIdMap
|
||||
* An array mapping old folder id to new folder ids.
|
||||
* @returns the fixed up URI if all matched. If some matched, it returns
|
||||
* the URI with only the matching folders included. If none matched it
|
||||
* returns the input URI unchanged.
|
||||
*/
|
||||
_fixupQuery: function PU__fixupQuery(aQueryURI, aFolderIdMap) {
|
||||
var queries = {};
|
||||
var options = {};
|
||||
this.history.queryStringToQueries(aQueryURI.spec, queries, {}, options);
|
||||
|
||||
var fixedQueries = [];
|
||||
queries.value.forEach(function(aQuery) {
|
||||
var folders = aQuery.getFolders({});
|
||||
|
||||
var newFolders = [];
|
||||
aFolderIdMap.forEach(function(aMapping) {
|
||||
if (folders.indexOf(aMapping[0]) != -1)
|
||||
newFolders.push(aMapping[1]);
|
||||
});
|
||||
|
||||
if (newFolders.length)
|
||||
aQuery.setFolders(newFolders, newFolders.length);
|
||||
fixedQueries.push(aQuery);
|
||||
});
|
||||
|
||||
var stringURI = this.history.queriesToQueryString(fixedQueries,
|
||||
fixedQueries.length,
|
||||
options.value);
|
||||
return this._uri(stringURI);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1275,7 +1338,9 @@ var PlacesUtils = {
|
|||
(concreteId != aPlacesNode.itemId && !aResolveShortcuts))) {
|
||||
aJSNode.type = self.TYPE_X_MOZ_PLACE;
|
||||
aJSNode.uri = aPlacesNode.uri;
|
||||
aJSNode.concreteId = concreteId;
|
||||
// folder shortcut
|
||||
if (aIsUICommand)
|
||||
aJSNode.concreteId = concreteId;
|
||||
return;
|
||||
}
|
||||
else if (aJSNode.id != -1) { // bookmark folder
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/* -*- 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 384370 code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dietrich Ayala <dietrich@mozilla.com> (Original Author)
|
||||
*
|
||||
* 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");
|
||||
var tests = [];
|
||||
|
||||
/*
|
||||
|
||||
Backup/restore tests example:
|
||||
|
||||
var myTest = {
|
||||
populate: function () { ... add bookmarks ... },
|
||||
validate: function () { ... query for your bookmarks ... }
|
||||
}
|
||||
|
||||
this.push(myTest);
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
test summary:
|
||||
- create folders with content
|
||||
- create a query bookmark for those folders
|
||||
- backs up bookmarks
|
||||
- restores bookmarks
|
||||
- confirms that the query has the new ids for the same folders
|
||||
|
||||
scenarios:
|
||||
- 1 folder (folder shortcut)
|
||||
- n folders (single query)
|
||||
- n folders (multiple queries)
|
||||
|
||||
*/
|
||||
|
||||
const DEFAULT_INDEX = PlacesUtils.bookmarks.DEFAULT_INDEX;
|
||||
|
||||
var test = {
|
||||
_testRootId: null,
|
||||
_testRootTitle: "test root",
|
||||
_folderIds: [],
|
||||
_bookmarkURIs: [],
|
||||
_count: 3,
|
||||
|
||||
populate: function populate() {
|
||||
// folder to hold this test
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.toolbarFolderId);
|
||||
this._testRootId =
|
||||
PlacesUtils.bookmarks.createFolder(PlacesUtils.toolbarFolderId,
|
||||
this._testRootTitle, DEFAULT_INDEX);
|
||||
|
||||
// create test folders each with a bookmark
|
||||
for (var i = 0; i < this._count; i++) {
|
||||
var folderId =
|
||||
PlacesUtils.bookmarks.createFolder(this._testRootId, "folder" + i, DEFAULT_INDEX);
|
||||
this._folderIds.push(folderId)
|
||||
|
||||
var bookmarkURI = uri("http://" + i);
|
||||
PlacesUtils.bookmarks.insertBookmark(folderId, bookmarkURI,
|
||||
DEFAULT_INDEX, "bookmark" + i);
|
||||
this._bookmarkURIs.push(bookmarkURI);
|
||||
}
|
||||
|
||||
// create a query URI with 1 folder (ie: folder shortcut)
|
||||
this._queryURI1 = uri("place:folder=" + this._folderIds[0] + "&queryType=1");
|
||||
this._queryTitle1 = "query1";
|
||||
PlacesUtils.bookmarks.insertBookmark(this._testRootId, this._queryURI1,
|
||||
DEFAULT_INDEX, this._queryTitle1);
|
||||
|
||||
// create a query URI with _count folders
|
||||
this._queryURI2 = uri("place:folder=" + this._folderIds.join("&folder=") + "&queryType=1");
|
||||
this._queryTitle2 = "query2";
|
||||
PlacesUtils.bookmarks.insertBookmark(this._testRootId, this._queryURI2,
|
||||
DEFAULT_INDEX, this._queryTitle2);
|
||||
|
||||
// create a query URI with _count queries (each with a folder)
|
||||
// first get a query object for each folder
|
||||
var queries = this._folderIds.map(function(aFolderId) {
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
query.setFolders([aFolderId], 1);
|
||||
return query;
|
||||
});
|
||||
var options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.queryType = options.QUERY_TYPE_BOOKMARKS;
|
||||
this._queryURI3 =
|
||||
uri(PlacesUtils.history.queriesToQueryString(queries, queries.length, options));
|
||||
this._queryTitle3 = "query3";
|
||||
PlacesUtils.bookmarks.insertBookmark(this._testRootId, this._queryURI3,
|
||||
DEFAULT_INDEX, this._queryTitle3);
|
||||
},
|
||||
|
||||
clean: function () {},
|
||||
|
||||
validate: function validate() {
|
||||
// Throw a wrench in the works by inserting some new bookmarks,
|
||||
// ensuring folder ids won't be the same, when restoring.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
PlacesUtils.bookmarks.
|
||||
insertBookmark(PlacesUtils.bookmarksMenuFolderId, uri("http://aaaa"+i), DEFAULT_INDEX, "");
|
||||
}
|
||||
|
||||
var toolbar =
|
||||
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId,
|
||||
false, true).root;
|
||||
do_check_true(toolbar.childCount, 1);
|
||||
|
||||
var folderNode = toolbar.getChild(0);
|
||||
do_check_eq(folderNode.type, folderNode.RESULT_TYPE_FOLDER);
|
||||
do_check_eq(folderNode.title, this._testRootTitle);
|
||||
folderNode.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
folderNode.containerOpen = true;
|
||||
|
||||
// |_count| folders + the query node
|
||||
do_check_eq(folderNode.childCount, this._count+3);
|
||||
|
||||
for (var i = 0; i < this._count; i++) {
|
||||
var subFolder = folderNode.getChild(i);
|
||||
do_check_eq(subFolder.title, "folder"+i);
|
||||
subFolder.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
subFolder.containerOpen = true;
|
||||
do_check_eq(subFolder.childCount, 1);
|
||||
var child = subFolder.getChild(0);
|
||||
do_check_eq(child.title, "bookmark"+i);
|
||||
do_check_true(uri(child.uri).equals(uri("http://" + i)))
|
||||
}
|
||||
|
||||
// validate folder shortcut
|
||||
this.validateQueryNode1(folderNode.getChild(this._count));
|
||||
|
||||
// validate folders query
|
||||
this.validateQueryNode2(folderNode.getChild(this._count + 1));
|
||||
|
||||
// validate multiple queries query
|
||||
this.validateQueryNode3(folderNode.getChild(this._count + 2));
|
||||
|
||||
// clean up
|
||||
folderNode.containerOpen = false;
|
||||
toolbar.containerOpen = false;
|
||||
},
|
||||
|
||||
validateQueryNode1: function validateQueryNode1(aNode) {
|
||||
do_check_eq(aNode.title, this._queryTitle1);
|
||||
do_check_true(PlacesUtils.nodeIsFolder(aNode));
|
||||
|
||||
aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
aNode.containerOpen = true;
|
||||
do_check_eq(aNode.childCount, 1);
|
||||
var child = aNode.getChild(0);
|
||||
do_check_true(uri(child.uri).equals(uri("http://0")))
|
||||
do_check_eq(child.title, "bookmark0")
|
||||
aNode.containerOpen = false;
|
||||
},
|
||||
|
||||
validateQueryNode2: function validateQueryNode2(aNode) {
|
||||
do_check_eq(aNode.title, this._queryTitle2);
|
||||
do_check_true(PlacesUtils.nodeIsQuery(aNode));
|
||||
|
||||
aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
aNode.containerOpen = true;
|
||||
do_check_eq(aNode.childCount, this._count);
|
||||
for (var i = 0; i < aNode.childCount; i++) {
|
||||
var child = aNode.getChild(i);
|
||||
do_check_true(uri(child.uri).equals(uri("http://" + i)))
|
||||
do_check_eq(child.title, "bookmark" + i)
|
||||
}
|
||||
aNode.containerOpen = false;
|
||||
},
|
||||
|
||||
validateQueryNode3: function validateQueryNode3(aNode) {
|
||||
do_check_eq(aNode.title, this._queryTitle3);
|
||||
do_check_true(PlacesUtils.nodeIsQuery(aNode));
|
||||
|
||||
aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
aNode.containerOpen = true;
|
||||
do_check_eq(aNode.childCount, this._count);
|
||||
for (var i = 0; i < aNode.childCount; i++) {
|
||||
var child = aNode.getChild(i);
|
||||
do_check_true(uri(child.uri).equals(uri("http://" + i)))
|
||||
do_check_eq(child.title, "bookmark" + i)
|
||||
}
|
||||
aNode.containerOpen = false;
|
||||
}
|
||||
}
|
||||
tests.push(test);
|
||||
|
||||
function run_test() {
|
||||
do_check_eq(typeof PlacesUtils, "object");
|
||||
|
||||
// make json file
|
||||
var jsonFile = dirSvc.get("ProfD", Ci.nsILocalFile);
|
||||
jsonFile.append("bookmarks.json");
|
||||
if (jsonFile.exists())
|
||||
jsonFile.remove(false);
|
||||
jsonFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
|
||||
if (!jsonFile.exists())
|
||||
do_throw("couldn't create file: bookmarks.exported.json");
|
||||
|
||||
// populate db
|
||||
tests.forEach(function(aTest) {
|
||||
aTest.populate();
|
||||
// sanity
|
||||
aTest.validate();
|
||||
});
|
||||
|
||||
// export json to file
|
||||
try {
|
||||
PlacesUtils.backupBookmarksToFile(jsonFile);
|
||||
} catch(ex) { do_throw("couldn't export to file: " + ex); }
|
||||
|
||||
// clean
|
||||
tests.forEach(function(aTest) {
|
||||
aTest.clean();
|
||||
});
|
||||
|
||||
// restore json file
|
||||
try {
|
||||
PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
|
||||
} catch(ex) { do_throw("couldn't import the exported file: " + ex); }
|
||||
|
||||
// validate
|
||||
tests.forEach(function(aTest) {
|
||||
aTest.validate();
|
||||
});
|
||||
|
||||
// clean up
|
||||
jsonFile.remove(false);
|
||||
}
|
Загрузка…
Ссылка в новой задаче