зеркало из https://github.com/mozilla/gecko-dev.git
Bug 423515 - If Unfiled Bookmarks is moved to Bookmarks Menu, unfiled bookmarks are invisible until browser is restarted (r=mano, a=mconnor)
This commit is contained in:
Родитель
98b5bbd918
Коммит
ff1f851a4f
|
@ -264,9 +264,9 @@ PlacesController.prototype = {
|
|||
* is a policy decision that a removable item not be placed inside a non-
|
||||
* removable item.
|
||||
* @param aIsMoveCommand
|
||||
* True if thecommand for which this method is called only moves the
|
||||
* True if the command for which this method is called only moves the
|
||||
* selected items to another container, false otherwise.
|
||||
* @returns true if the there's a selection which has no nodes that cannot be removed,
|
||||
* @returns true if all nodes in the selection can be removed,
|
||||
* false otherwise.
|
||||
*/
|
||||
_hasRemovableSelection: function PC__hasRemovableSelection(aIsMoveCommand) {
|
||||
|
@ -278,17 +278,8 @@ PlacesController.prototype = {
|
|||
if (nodes[i] == root)
|
||||
return false;
|
||||
|
||||
// Disallow removing shortcuts from the left pane
|
||||
var nodeItemId = nodes[i].itemId;
|
||||
if (PlacesUtils.annotations
|
||||
.itemHasAnnotation(nodeItemId, ORGANIZER_QUERY_ANNO))
|
||||
return false;
|
||||
|
||||
// Disallow removing the toolbar, menu and unfiled-bookmarks folders
|
||||
if (!aIsMoveCommand &&
|
||||
(nodeItemId == PlacesUtils.toolbarFolderId ||
|
||||
nodeItemId == PlacesUtils.unfiledBookmarksFolderId ||
|
||||
nodeItemId == PlacesUtils.bookmarksMenuFolderId))
|
||||
if (PlacesUtils.nodeIsFolder(nodes[i]) &&
|
||||
!PlacesControllerDragHelper.canMoveContainerNode(nodes[i]))
|
||||
return false;
|
||||
|
||||
// We don't call nodeIsReadOnly here, because nodeIsReadOnly means that
|
||||
|
@ -1006,6 +997,7 @@ PlacesController.prototype = {
|
|||
* elsewhere.
|
||||
*/
|
||||
getTransferData: function PC_getTransferData(dragAction) {
|
||||
var copy = dragAction == Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
var result = this._view.getResult();
|
||||
var oldViewer = result.viewer;
|
||||
try {
|
||||
|
@ -1025,7 +1017,7 @@ PlacesController.prototype = {
|
|||
var data = new TransferData();
|
||||
function addData(type, overrideURI) {
|
||||
data.addDataForFlavour(type, PlacesUIUtils._wrapString(
|
||||
PlacesUtils.wrapNode(node, type, overrideURI)));
|
||||
PlacesUtils.wrapNode(node, type, overrideURI, copy)));
|
||||
}
|
||||
|
||||
function addURIData(overrideURI) {
|
||||
|
@ -1093,7 +1085,8 @@ PlacesController.prototype = {
|
|||
uri) + suffix);
|
||||
|
||||
var placeSuffix = i < (nodes.length - 1) ? "," : "";
|
||||
return PlacesUtils.wrapNode(node, type, overrideURI) + placeSuffix;
|
||||
var resolveShortcuts = !PlacesControllerDragHelper.canMoveContainerNode(node);
|
||||
return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix;
|
||||
}
|
||||
|
||||
// all items wrapped as TYPE_X_MOZ_PLACE
|
||||
|
@ -1317,6 +1310,71 @@ var PlacesControllerDragHelper = {
|
|||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if a container node can be moved.
|
||||
*
|
||||
* @param aNode
|
||||
* A bookmark folder node.
|
||||
* @param [optional] aInsertionPoint
|
||||
* The insertion point of the drop target.
|
||||
* @returns True if the container can be moved.
|
||||
*/
|
||||
canMoveContainerNode:
|
||||
function PCDH_canMoveContainerNode(aNode, aInsertionPoint) {
|
||||
// can't move query root
|
||||
if (!aNode.parent)
|
||||
return false;
|
||||
|
||||
var targetId = aInsertionPoint ? aInsertionPoint.itemId : -1;
|
||||
var parentId = PlacesUtils.getConcreteItemId(aNode.parent);
|
||||
var concreteId = PlacesUtils.getConcreteItemId(aNode);
|
||||
|
||||
// can't move tag containers
|
||||
if (PlacesUtils.nodeIsTagQuery(aNode))
|
||||
return false;
|
||||
|
||||
// check is child of a read-only container
|
||||
if (PlacesUtils.nodeIsReadOnly(aNode.parent))
|
||||
return false;
|
||||
|
||||
// check for special folders, etc
|
||||
if (!this.canMoveContainer(aNode.itemId, parentId))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if a container node can be moved.
|
||||
*
|
||||
* @param aId
|
||||
* A bookmark folder id.
|
||||
* @param [optional] aParentId
|
||||
* The parent id of the folder.
|
||||
* @returns True if the container can be moved to the target.
|
||||
*/
|
||||
canMoveContainer:
|
||||
function PCDH_canMoveContainer(aId, aParentId) {
|
||||
if (aId == -1)
|
||||
return false;
|
||||
|
||||
// Disallow moving of roots and special folders
|
||||
const ROOTS = [PlacesUtils.placesRootId, PlacesUtils.bookmarksMenuFolderId,
|
||||
PlacesUtils.tagsFolderId, PlacesUtils.unfiledBookmarksFolderId,
|
||||
PlacesUtils.toolbarFolderId];
|
||||
if (ROOTS.indexOf(aId) != -1)
|
||||
return false;
|
||||
|
||||
// Get parent id if necessary
|
||||
if (aParentId == null || aParentId == -1)
|
||||
aParentId = PlacesUtils.bookmarks.getFolderIdForItem(aId);
|
||||
|
||||
if(PlacesUtils.bookmarks.getFolderReadonly(aParentId))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a Transferable object that can be filled with data of types
|
||||
* supported by a view.
|
||||
|
@ -1343,6 +1401,8 @@ var PlacesControllerDragHelper = {
|
|||
*/
|
||||
onDrop: function PCDH_onDrop(insertionPoint) {
|
||||
var session = this.getSession();
|
||||
// XXX dragAction is not valid, so we also set copy below by checking
|
||||
// whether the dropped item is moveable, before creating the transaction
|
||||
var copy = session.dragAction & Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
var transactions = [];
|
||||
var xferable = this._initTransferable(session);
|
||||
|
@ -1360,13 +1420,13 @@ var PlacesControllerDragHelper = {
|
|||
// There's only ever one in the D&D case.
|
||||
var unwrapped = PlacesUtils.unwrapNodes(data.value.data,
|
||||
flavor.value)[0];
|
||||
|
||||
var index = insertionPoint.index;
|
||||
|
||||
// Adjust insertion index to prevent reversal of dragged items. When you
|
||||
// drag multiple elts upward: need to increment index or each successive
|
||||
// elt will be inserted at the same index, each above the previous.
|
||||
if ((index != -1) && ((index < unwrapped.index) ||
|
||||
(unwrapped.folder && (index < unwrapped.folder.index)))) {
|
||||
if (index != -1 && index < unwrapped.index) {
|
||||
index = index + movedCount;
|
||||
movedCount++;
|
||||
}
|
||||
|
@ -1378,6 +1438,12 @@ var PlacesControllerDragHelper = {
|
|||
transactions.push(PlacesUIUtils.ptm.tagURI(uri,[tagItemId]));
|
||||
}
|
||||
else {
|
||||
if (!this.canMoveContainer(unwrapped.id, null))
|
||||
copy = true;
|
||||
else if (unwrapped.concreteId &&
|
||||
!this.canMoveContainer(unwrapped.concreteId, null))
|
||||
copy = true;
|
||||
|
||||
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
|
||||
flavor.value, insertionPoint.itemId,
|
||||
index, copy));
|
||||
|
|
|
@ -189,9 +189,10 @@
|
|||
<parameter name="aXferData"/>
|
||||
<parameter name="aDragAction"/>
|
||||
<body><![CDATA[
|
||||
// Force a copy action if parent node is a query
|
||||
// Force a copy action if parent node is a query or not-removable
|
||||
if (aEvent.ctrlKey ||
|
||||
PlacesUtils.nodeIsQuery(aEvent.target.node.parent))
|
||||
PlacesUtils.nodeIsQuery(aEvent.target.node.parent) ||
|
||||
PlacesControllerDragHelper.canMoveContainerNode(aEvent.target.node))
|
||||
aDragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
|
||||
// activate the view and cache the dragged node
|
||||
|
|
|
@ -673,8 +673,9 @@
|
|||
// If this node is part of a readonly container (e.g. a livemark) it
|
||||
// cannot be moved, only copied, so we must change the action used
|
||||
// by the drag session.
|
||||
if (PlacesUtils.nodeIsReadOnly(parent) ||
|
||||
PlacesUtils.nodeIsTagQuery(parent)) {
|
||||
if (PlacesUtils.nodeIsTagQuery(parent) ||
|
||||
!PlacesControllerDragHelper.canMoveContainerNode(node)) {
|
||||
// XXX DOES NOTHING! dragAction doesn't persist
|
||||
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ const LMANNO_FEEDURI = "livemark/feedURI";
|
|||
const LMANNO_SITEURI = "livemark/siteURI";
|
||||
const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
|
||||
const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
|
||||
const ORGANIZER_LEFTPANE_VERSION = 3;
|
||||
const ORGANIZER_LEFTPANE_VERSION = 4;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// On Mac OSX, the transferable system converts "\r\n" to "\n\n", where we
|
||||
|
@ -1169,6 +1169,8 @@ var PlacesUIUtils = {
|
|||
|
||||
// Left Pane Root Folder
|
||||
leftPaneRoot = PlacesUtils.bookmarks.createFolder(PlacesUtils.placesRootId, "", -1);
|
||||
// ensure immediate children can't be removed
|
||||
PlacesUtils.bookmarks.setFolderReadonly(leftPaneRoot, true);
|
||||
|
||||
// History Query
|
||||
let uri = PlacesUtils._uri("place:sort=4&");
|
||||
|
|
|
@ -45,6 +45,7 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_TEST_FILES = \
|
||||
browser_425884.js \
|
||||
browser_423515.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
/* 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 Places test 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>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
function test() {
|
||||
// sanity check
|
||||
ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
|
||||
ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
|
||||
ok(PlacesControllerDragHelper, "checking PlacesControllerDragHelper, running in chrome context?");
|
||||
|
||||
const IDX = PlacesUtils.bookmarks.DEFAULT_INDEX;
|
||||
|
||||
// setup
|
||||
var rootId = PlacesUtils.bookmarks.createFolder(PlacesUtils.toolbarFolderId, "", IDX);
|
||||
var rootNode = PlacesUtils.getFolderContents(rootId, false, true).root;
|
||||
is(rootNode.childCount, 0, "confirm test root is empty");
|
||||
|
||||
var tests = [];
|
||||
|
||||
// add a regular folder, should be moveable
|
||||
tests.push({
|
||||
populate: function() {
|
||||
this.id =
|
||||
PlacesUtils.bookmarks.createFolder(rootId, "", IDX);
|
||||
},
|
||||
validate: function() {
|
||||
is(rootNode.childCount, 1,
|
||||
"populate added data to the test root");
|
||||
is(PlacesControllerDragHelper.canMoveContainer(this.id),
|
||||
true, "can move regular folder id");
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(rootNode.getChild(0)),
|
||||
true, "can move regular folder node");
|
||||
}
|
||||
});
|
||||
|
||||
// add a regular folder shortcut, should be moveable
|
||||
tests.push({
|
||||
populate: function() {
|
||||
this.folderId =
|
||||
PlacesUtils.bookmarks.createFolder(rootId, "foo", IDX);
|
||||
this.shortcutId =
|
||||
PlacesUtils.bookmarks.insertBookmark(rootId, makeURI("place:folder="+this.folderId), IDX, "bar");
|
||||
},
|
||||
validate: function() {
|
||||
is(rootNode.childCount, 2,
|
||||
"populated data to the test root");
|
||||
|
||||
var folderNode = rootNode.getChild(0);
|
||||
is(folderNode.type, 6, "node is folder");
|
||||
is(this.folderId, folderNode.itemId, "folder id and folder node item id match");
|
||||
|
||||
var shortcutNode = rootNode.getChild(1);
|
||||
is(shortcutNode.type, 9, "node is folder shortcut");
|
||||
is(this.shortcutId, shortcutNode.itemId, "shortcut id and shortcut node item id match");
|
||||
|
||||
var concreteId = PlacesUtils.getConcreteItemId(shortcutNode);
|
||||
is(concreteId, folderNode.itemId, "shortcut node id and concrete id match");
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainer(this.shortcutId),
|
||||
true, "can move folder shortcut id");
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(shortcutNode),
|
||||
true, "can move folder shortcut node");
|
||||
}
|
||||
});
|
||||
|
||||
// add a regular query, should be moveable
|
||||
tests.push({
|
||||
populate: function() {
|
||||
this.bookmarkId =
|
||||
PlacesUtils.bookmarks.insertBookmark(rootId, makeURI("http://foo.com"), IDX, "foo");
|
||||
this.queryId =
|
||||
PlacesUtils.bookmarks.insertBookmark(rootId, makeURI("place:terms=foo"), IDX, "bar");
|
||||
},
|
||||
validate: function() {
|
||||
is(rootNode.childCount, 2,
|
||||
"populated data to the test root");
|
||||
|
||||
var bmNode = rootNode.getChild(0);
|
||||
is(bmNode.itemId, this.bookmarkId, "bookmark id and bookmark node item id match");
|
||||
|
||||
var queryNode = rootNode.getChild(1);
|
||||
is(queryNode.itemId, this.queryId, "query id and query node item id match");
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainer(this.queryId),
|
||||
true, "can move query id");
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(queryNode),
|
||||
true, "can move query node");
|
||||
}
|
||||
});
|
||||
|
||||
// test that special folders cannot be moved
|
||||
// test that special folders shortcuts can be moved
|
||||
tests.push({
|
||||
folders: [PlacesUtils.bookmarksMenuFolderId,
|
||||
PlacesUtils.tagsFolderId, PlacesUtils.unfiledBookmarksFolderId,
|
||||
PlacesUtils.toolbarFolderId],
|
||||
shortcuts: {},
|
||||
populate: function() {
|
||||
for (var i = 0; i < this.folders.length; i++) {
|
||||
var id = this.folders[i];
|
||||
this.shortcuts[id] =
|
||||
PlacesUtils.bookmarks.insertBookmark(rootId, makeURI("place:folder=" + id), IDX, "");
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
// test toolbar shortcut node
|
||||
is(rootNode.childCount, this.folders.length,
|
||||
"populated data to the test root");
|
||||
|
||||
function getRootChildNode(aId) {
|
||||
var node = PlacesUtils.getFolderContents(PlacesUtils.placesRootId, false, true).root;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
var child = node.getChild(i);
|
||||
if (child.itemId == aId)
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.folders.length; i++) {
|
||||
var id = this.folders[i];
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainer(id),
|
||||
false, "shouldn't be able to move special folder id");
|
||||
|
||||
//var node = PlacesUtils.getFolderContents(id, false, true).root;
|
||||
var node = getRootChildNode(id);
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(node),
|
||||
false, "shouldn't be able to move special folder node");
|
||||
|
||||
var shortcutId = this.shortcuts[id];
|
||||
var shortcutNode = rootNode.getChild(i);
|
||||
|
||||
is(shortcutNode.itemId, shortcutId, "shortcut id and shortcut node item id match");
|
||||
|
||||
LOG("can move shortcut id?");
|
||||
is(PlacesControllerDragHelper.canMoveContainer(shortcutId),
|
||||
true, "should be able to move special folder shortcut id");
|
||||
|
||||
LOG("can move shortcut node?");
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(shortcutNode),
|
||||
true, "should be able to move special folder shortcut node");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// test that a tag container cannot be moved
|
||||
tests.push({
|
||||
populate: function() {
|
||||
// tag a uri
|
||||
this.uri = makeURI("http://foo.com");
|
||||
PlacesUtils.tagging.tagURI(this.uri, ["bar"]);
|
||||
},
|
||||
validate: function() {
|
||||
// get tag root
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
var options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY;
|
||||
var tagsNode = PlacesUtils.history.executeQuery(query, options).root;
|
||||
|
||||
tagsNode.containerOpen = true;
|
||||
is(tagsNode.childCount, 1, "has new tag");
|
||||
|
||||
var tagNode = tagsNode.getChild(0);
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(tagNode),
|
||||
false, "should not be able to move tag container node");
|
||||
}
|
||||
});
|
||||
|
||||
// test that any child of a read-only node cannot be moved
|
||||
tests.push({
|
||||
populate: function() {
|
||||
this.id =
|
||||
PlacesUtils.bookmarks.createFolder(rootId, "foo", IDX);
|
||||
PlacesUtils.bookmarks.createFolder(this.id, "bar", IDX);
|
||||
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
|
||||
},
|
||||
validate: function() {
|
||||
is(rootNode.childCount, 1,
|
||||
"populate added data to the test root");
|
||||
var readOnlyFolder = rootNode.getChild(0);
|
||||
|
||||
// test that we can move the read-only folder
|
||||
is(PlacesControllerDragHelper.canMoveContainer(this.id),
|
||||
true, "can move read-only folder id");
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(readOnlyFolder),
|
||||
true, "can move read-only folder node");
|
||||
|
||||
// test that we cannot move the child of a read-only folder
|
||||
readOnlyFolder.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
readOnlyFolder.containerOpen = true;
|
||||
var childFolder = readOnlyFolder.getChild(0);
|
||||
|
||||
is(PlacesControllerDragHelper.canMoveContainer(childFolder.itemId),
|
||||
false, "cannot move a child of a read-only folder");
|
||||
is(PlacesControllerDragHelper.canMoveContainerNode(childFolder),
|
||||
false, "cannot move a child node of a read-only folder node");
|
||||
}
|
||||
});
|
||||
|
||||
tests.forEach(function(aTest) {
|
||||
PlacesUtils.bookmarks.removeFolderChildren(rootId);
|
||||
aTest.populate();
|
||||
aTest.validate();
|
||||
});
|
||||
|
||||
PlacesUtils.bookmarks.removeItem(rootId);
|
||||
}
|
|
@ -50,6 +50,7 @@ var Cc = Components.classes;
|
|||
var Cr = Components.results;
|
||||
|
||||
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
|
||||
const READ_ONLY_ANNO = "placesInternal/READ_ONLY";
|
||||
const LMANNO_FEEDURI = "livemark/feedURI";
|
||||
const LMANNO_SITEURI = "livemark/siteURI";
|
||||
|
||||
|
@ -439,9 +440,11 @@ var PlacesUtils = {
|
|||
* Used instead of the node's URI if provided.
|
||||
* This is useful for wrapping a container as TYPE_X_MOZ_URL,
|
||||
* TYPE_HTML or TYPE_UNICODE.
|
||||
* @param aForceCopy
|
||||
* Does a full copy, resolving folder shortcuts.
|
||||
* @returns A string serialization of the node
|
||||
*/
|
||||
wrapNode: function PU_wrapNode(aNode, aType, aOverrideURI) {
|
||||
wrapNode: function PU_wrapNode(aNode, aType, aOverrideURI, aForceCopy) {
|
||||
var self = this;
|
||||
|
||||
// when wrapping a node, we want all the items, even if the original
|
||||
|
@ -449,8 +452,10 @@ var PlacesUtils = {
|
|||
// this can happen when copying from the left hand pane of the bookmarks
|
||||
// organizer
|
||||
function convertNode(cNode) {
|
||||
if (self.nodeIsFolder(cNode) && asQuery(cNode).queryOptions.excludeItems)
|
||||
return self.getFolderContents(cNode.itemId, false, true).root;
|
||||
if (self.nodeIsFolder(cNode) && asQuery(cNode).queryOptions.excludeItems) {
|
||||
var concreteId = self.getConcreteItemId(cNode);
|
||||
return self.getFolderContents(concreteId, false, true).root;
|
||||
}
|
||||
return cNode;
|
||||
}
|
||||
|
||||
|
@ -464,7 +469,7 @@ var PlacesUtils = {
|
|||
this.value += aStr;
|
||||
}
|
||||
};
|
||||
self.serializeNodeAsJSONToOutputStream(convertNode(aNode), writer, true);
|
||||
self.serializeNodeAsJSONToOutputStream(convertNode(aNode), writer, true, aForceCopy);
|
||||
return writer.value;
|
||||
case this.TYPE_X_MOZ_URL:
|
||||
function gatherDataUrl(bNode) {
|
||||
|
@ -490,7 +495,7 @@ var PlacesUtils = {
|
|||
return s;
|
||||
}
|
||||
// escape out potential HTML in the title
|
||||
var escapedTitle = htmlEscape(bNode.title);
|
||||
var escapedTitle = bNode.title ? htmlEscape(bNode.title) : "";
|
||||
if (self.nodeIsLivemarkContainer(bNode)) {
|
||||
var siteURI = self.livemarks.getSiteURI(bNode.itemId).spec;
|
||||
return "<A HREF=\"" + siteURI + "\">" + escapedTitle + "</A>" + NEWLINE;
|
||||
|
@ -1190,9 +1195,11 @@ var PlacesUtils = {
|
|||
* @param aIsUICommand
|
||||
* Boolean - If true, modifies serialization so that each node self-contained.
|
||||
* For Example, tags are serialized inline with each bookmark.
|
||||
* @param aResolveShortcuts
|
||||
* Converts folder shortcuts into actual folders.
|
||||
*/
|
||||
serializeNodeAsJSONToOutputStream:
|
||||
function PU_serializeNodeAsJSONToOutputStream(aNode, aStream, aIsUICommand) {
|
||||
function PU_serializeNodeAsJSONToOutputStream(aNode, aStream, aIsUICommand, aResolveShortcuts) {
|
||||
var self = this;
|
||||
|
||||
function addGenericProperties(aPlacesNode, aJSNode) {
|
||||
|
@ -1219,8 +1226,12 @@ var PlacesUtils = {
|
|||
// backup/restore of non-whitelisted annos
|
||||
// XXX causes JSON encoding errors, so utf-8 encode
|
||||
//anno.value = unescape(encodeURIComponent(anno.value));
|
||||
if (anno.name == "livemark/feedURI")
|
||||
if (anno.name == LMANNO_FEEDURI)
|
||||
aJSNode.livemark = 1;
|
||||
if (anno.name == READ_ONLY_ANNO && aResolveShortcuts) {
|
||||
// When copying a read-only node, remove the read-only annotation.
|
||||
return false;
|
||||
}
|
||||
return anno.name != "placesInternal/GUID";
|
||||
});
|
||||
} catch(ex) {
|
||||
|
@ -1259,14 +1270,17 @@ var PlacesUtils = {
|
|||
|
||||
function addContainerProperties(aPlacesNode, aJSNode) {
|
||||
// saved queries
|
||||
if (aJSNode.id != -1 &&
|
||||
self.bookmarks.getItemType(aJSNode.id) == self.bookmarks.TYPE_BOOKMARK) {
|
||||
var concreteId = PlacesUtils.getConcreteItemId(aPlacesNode);
|
||||
if (aJSNode.id != -1 && (PlacesUtils.nodeIsQuery(aPlacesNode) ||
|
||||
(concreteId != aPlacesNode.itemId && !aResolveShortcuts))) {
|
||||
aJSNode.type = self.TYPE_X_MOZ_PLACE;
|
||||
aJSNode.uri = aPlacesNode.uri;
|
||||
aJSNode.concreteId = PlacesUtils.getConcreteItemId(aPlacesNode);
|
||||
aJSNode.concreteId = concreteId;
|
||||
return;
|
||||
}
|
||||
else if (aJSNode.id != -1) { // bookmark folder
|
||||
if (concreteId != aPlacesNode.itemId)
|
||||
aJSNode.type = self.TYPE_X_MOZ_PLACE;
|
||||
aJSNode.type = self.TYPE_X_MOZ_PLACE_CONTAINER;
|
||||
// mark special folders
|
||||
if (aJSNode.id == self.bookmarks.placesRoot)
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- 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>
|
||||
*
|
||||
* 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");
|
||||
|
||||
const DEFAULT_INDEX = PlacesUtils.bookmarks.DEFAULT_INDEX;
|
||||
|
||||
function run_test() {
|
||||
/*
|
||||
- create folder A
|
||||
- add a bookmark to it
|
||||
- create a bookmark that's a place: folder shortcut to the new folder
|
||||
- serialize it to JSON, forcing copy
|
||||
- import JSON
|
||||
- confirm that the newly imported folder is a full copy and not a shortcut
|
||||
*/
|
||||
|
||||
var folderA =
|
||||
PlacesUtils.bookmarks.createFolder(PlacesUtils.toolbarFolderId,
|
||||
"test folder", DEFAULT_INDEX);
|
||||
var bookmarkURI = uri("http://test");
|
||||
PlacesUtils.bookmarks.insertBookmark(folderA, bookmarkURI,
|
||||
DEFAULT_INDEX, "");
|
||||
|
||||
// create the query
|
||||
var queryURI = uri("place:folder=" + folderA);
|
||||
var queryTitle = "test query";
|
||||
var queryId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId,
|
||||
queryURI, DEFAULT_INDEX, queryTitle);
|
||||
LOG("queryId: " + queryId);
|
||||
|
||||
// create a query that's *not* a folder shortcut
|
||||
var queryURI2 = uri("place:");
|
||||
var queryTitle2 = "non-folder test query";
|
||||
var queryId2 =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId,
|
||||
queryURI2, DEFAULT_INDEX, queryTitle2);
|
||||
|
||||
// check default state
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
query.setFolders([PlacesUtils.toolbarFolderId], 1);
|
||||
var options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.expandQueries = true;
|
||||
var result = PlacesUtils.history.executeQuery(query, options);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
|
||||
// check folder query node
|
||||
var queryNode = root.getChild(root.childCount-2);
|
||||
do_check_eq(queryNode.type, queryNode.RESULT_TYPE_FOLDER_SHORTCUT);
|
||||
do_check_eq(queryNode.title, queryTitle);
|
||||
do_check_true(queryURI.equals(uri(queryNode.uri)));
|
||||
queryNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
queryNode.containerOpen = true;
|
||||
do_check_eq(queryNode.childCount, 1);
|
||||
var bookmark = queryNode.getChild(0);
|
||||
do_check_true(bookmarkURI.equals(uri(bookmark.uri)));
|
||||
queryNode.containerOpen = false;
|
||||
|
||||
// check non-folder query node
|
||||
var queryNode2 = root.getChild(root.childCount-1);
|
||||
do_check_eq(queryNode2.type, queryNode2.RESULT_TYPE_QUERY);
|
||||
do_check_eq(queryNode2.title, queryTitle2);
|
||||
do_check_true(queryURI2.equals(uri(queryNode2.uri)));
|
||||
queryNode2.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
queryNode2.containerOpen = true;
|
||||
do_check_eq(queryNode2.childCount, 0);
|
||||
queryNode2.containerOpen = false;
|
||||
|
||||
// clean up
|
||||
root.containerOpen = false;
|
||||
|
||||
// serialize
|
||||
var stream = {
|
||||
_str: "",
|
||||
write: function(aData, aLen) {
|
||||
this._str += aData;
|
||||
}
|
||||
};
|
||||
PlacesUtils.serializeNodeAsJSONToOutputStream(queryNode, stream, false, true);
|
||||
|
||||
LOG("SERIALIZED: " + stream._str);
|
||||
|
||||
PlacesUtils.bookmarks.removeItem(queryId);
|
||||
|
||||
// import
|
||||
PlacesUtils.importJSONNode(stream._str, PlacesUtils.toolbarFolderId, -1);
|
||||
|
||||
// query for node
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
query.setFolders([PlacesUtils.toolbarFolderId], 1);
|
||||
var options = PlacesUtils.history.getNewQueryOptions();
|
||||
var result = PlacesUtils.history.executeQuery(query, options);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
|
||||
// check folder node (no longer shortcut)
|
||||
var queryNode = root.getChild(root.childCount-2);
|
||||
do_check_eq(queryNode.type, queryNode.RESULT_TYPE_FOLDER);
|
||||
queryNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
queryNode.containerOpen = true;
|
||||
do_check_eq(queryNode.childCount, 1);
|
||||
var child = queryNode.getChild(0);
|
||||
do_check_true(bookmarkURI.equals(uri(child.uri)));
|
||||
|
||||
var queryNode2 = root.getChild(root.childCount-1);
|
||||
do_check_eq(queryNode2.type, queryNode2.RESULT_TYPE_QUERY);
|
||||
queryNode2.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
queryNode2.containerOpen = true;
|
||||
do_check_eq(queryNode2.childCount, 0);
|
||||
}
|
Загрузка…
Ссылка в новой задаче