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:
dietrich@mozilla.com 2008-04-25 14:36:40 -07:00
parent 98b5bbd918
commit ff1f851a4f
8 changed files with 507 additions and 32 deletions

View File

@ -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));

View File

@ -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

View File

@ -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;
}

View File

@ -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&");

View File

@ -45,6 +45,7 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_425884.js \
browser_423515.js \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}