Bug 1068671 - Remove 'read-only' folders support from places. r=mak

This commit is contained in:
Asaf Romano 2014-10-01 12:19:06 +03:00
Родитель 7beb1bf8c6
Коммит 896f516dd7
19 изменённых файлов: 195 добавлений и 417 удалений

Просмотреть файл

@ -44,6 +44,43 @@ XPCOMUtils.defineLazyModuleGetter(this, "Weave",
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
// This function isn't public both because it's synchronous and because it is
// going to be removed in bug 1072833.
function IsLivemark(aItemId) {
// Since this check may be done on each dragover event, it's worth maintaining
// a cache.
let self = IsLivemark;
if (!("ids" in self)) {
const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI;
let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO);
self.ids = new Set(idsVec);
let obs = Object.freeze({
QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
onItemAnnotationSet(itemId, annoName) {
if (annoName == LIVEMARK_ANNO)
self.ids.add(itemId);
},
onItemAnnotationRemoved(itemId, annoName) {
// If annoName is set to an empty string, the item is gone.
if (annoName == LIVEMARK_ANNO || annoName == "")
self.ids.delete(itemId);
},
onPageAnnotationSet() { },
onPageAnnotationRemoved() { },
});
PlacesUtils.annotations.addObserver(obs);
PlacesUtils.registerShutdownFunction(() => {
PlacesUtils.annotations.removeObserver(obs);
});
}
return self.ids.has(aItemId);
}
this.PlacesUIUtils = {
ORGANIZER_LEFTPANE_VERSION: 7,
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
@ -560,6 +597,74 @@ this.PlacesUIUtils = {
return "";
},
/**
* Check whether or not the given node represents a removable entry (either in
* history or in bookmarks).
*
* @param aNode
* a node, except the root node of a query.
* @return true if the aNode represents a removable entry, false otherwise.
*/
canUserRemove: function (aNode) {
let parentNode = aNode.parent;
if (!parentNode)
throw new Error("canUserRemove doesn't accept root nodes");
// If it's not a bookmark, we can remove it unless it's a child of a
// livemark.
if (aNode.itemId == -1) {
// Rather than executing a db query, checking the existence of the feedURI
// annotation, detect livemark children by the fact that they are the only
// direct non-bookmark children of bookmark folders.
return !PlacesUtils.nodeIsFolder(parentNode);
}
// Otherwise it has to be a child of an editable folder.
return !this.isContentsReadOnly(parentNode);
},
/**
* DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
* TO GUIDS IS COMPLETE (BUG 1071511).
*
* Check whether or not the given node or item-id points to a folder which
* should not be modified by the user (i.e. its children should be unremovable
* and unmovable, new children should be disallowed, etc).
* These semantics are not inherited, meaning that read-only folder may
* contain editable items (for instance, the places root is read-only, but all
* of its direct children aren't).
*
* You should only pass folder item ids or folder nodes for aNodeOrItemId.
* While this is only enforced for the node case (if an item id of a separator
* or a bookmark is passed, false is returned), it's considered the caller's
* job to ensure that it checks a folder.
* Also note that folder-shortcuts should only be passed as result nodes.
* Otherwise they are just treated as bookmarks (i.e. false is returned).
*
* @param aNodeOrItemId
* any item id or result node.
* @throws if aNodeOrItemId is neither an item id nor a folder result node.
* @note livemark "folders" are considered read-only (but see bug 1072833).
* @return true if aItemId points to a read-only folder, false otherwise.
*/
isContentsReadOnly: function (aNodeOrItemId) {
let itemId;
if (typeof(aNodeOrItemId) == "number") {
itemId = aNodeOrItemId;
}
else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) {
itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId);
}
else {
throw new Error("invalid value for aNodeOrItemId");
}
return itemId == this.leftPaneFolderId ||
itemId == this.allBookmarksFolderId ||
itemId == PlacesUtils.placesRootId ||
IsLivemark(itemId);
},
/**
* Gives the user a chance to cancel loading lots of tabs at once
*/
@ -962,8 +1067,6 @@ this.PlacesUIUtils = {
// We should never backup this, since it changes between profiles.
as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
0, as.EXPIRE_NEVER);
// Disallow manipulating this folder within the organizer UI.
bs.setFolderReadonly(folderId, true);
if (aIsRoot) {
// Mark as special left pane root.

Просмотреть файл

@ -1376,8 +1376,8 @@ PlacesToolbar.prototype = {
elt.localName != "menupopup") {
let eltRect = elt.getBoundingClientRect();
let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if (PlacesUIUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
// This is a folder.
// If we are in the middle of it, drop inside it.
// Otherwise, drop before it, with regards to RTL mode.

Просмотреть файл

@ -200,7 +200,7 @@ PlacesController.prototype = {
var selectedNode = this._view.selectedNode;
return selectedNode &&
PlacesUtils.nodeIsFolder(selectedNode) &&
!PlacesUtils.nodeIsReadOnly(selectedNode) &&
!PlacesUIUtils.isContentsReadOnly(selectedNode) &&
this._view.result.sortingMode ==
Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
case "placesCmd_createBookmark":
@ -330,21 +330,7 @@ PlacesController.prototype = {
if (nodes[i] == root)
return false;
if (PlacesUtils.nodeIsFolder(nodes[i]) &&
!PlacesControllerDragHelper.canMoveNode(nodes[i]))
return false;
// We don't call nodeIsReadOnly here, because nodeIsReadOnly means that
// a node has children that cannot be edited, reordered or removed. Here,
// we don't care if a node's children can't be reordered or edited, just
// that they're removable. All history results have removable children
// (based on the principle that any URL in the history table should be
// removable), but some special bookmark folders may have non-removable
// children, e.g. live bookmark folder children. It doesn't make sense
// to delete a child of a live bookmark folder, since when the folder
// refreshes, the child will return.
var parent = nodes[i].parent || root;
if (PlacesUtils.isReadonlyFolder(parent))
if (!PlacesUIUtils.canUserRemove(nodes[i]))
return false;
}
}
@ -1560,10 +1546,9 @@ let PlacesControllerDragHelper = {
canMoveUnwrappedNode: function (aUnwrappedNode) {
return aUnwrappedNode.id > 0 &&
!PlacesUtils.isRootItem(aUnwrappedNode.id) &&
aUnwrappedNode.parent != PlacesUtils.placesRootId &&
!PlacesUIUtils.isContentsReadOnly(aUnwrappedNode.parent) ||
aUnwrappedNode.parent != PlacesUtils.tagsFolderId &&
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId &&
!aUnwrappedNode.parentReadOnly;
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId;
},
/**
@ -1575,58 +1560,17 @@ let PlacesControllerDragHelper = {
*/
canMoveNode:
function PCDH_canMoveNode(aNode) {
// Can't move query root.
if (!aNode.parent)
// Only bookmark items are movable.
if (aNode.itemId == -1)
return false;
let parentId = PlacesUtils.getConcreteItemId(aNode.parent);
let concreteId = PlacesUtils.getConcreteItemId(aNode);
// Can't move children of tag containers.
if (PlacesUtils.nodeIsTagQuery(aNode.parent))
return false;
// Can't move children of read-only containers.
if (PlacesUtils.nodeIsReadOnly(aNode.parent))
return false;
// Check for special folders, etc.
if (PlacesUtils.nodeIsContainer(aNode) &&
!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.
* @return 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;
// Once tags and bookmarked are divorced, the tag-query check should be
// removed.
let parentNode = aNode.parent;
return parentNode != null &&
!(PlacesUtils.nodeIsFolder(parentNode) &&
PlacesUIUtils.isContentsReadOnly(parentNode)) &&
!PlacesUtils.nodeIsTagQuery(parentNode);
},
/**
@ -1726,12 +1670,10 @@ let PlacesControllerDragHelper = {
*/
disallowInsertion: function(aContainer) {
NS_ASSERT(aContainer, "empty container");
// Allow dropping into Tag containers.
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders.
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
// Allow dropping into Tag containers and editable folders.
return !PlacesUtils.nodeIsTagQuery(aContainer) &&
(!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUIUtils.isContentsReadOnly(aContainer));
}
};

Просмотреть файл

@ -106,9 +106,9 @@
let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
elt._placesNode.title : null;
if ((PlacesUtils.nodeIsFolder(elt._placesNode) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) &&
!PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
!PlacesUIUtils.isContentsReadOnly(elt._placesNode) ||
PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
// This is a folder or a tag container.
if (eventY - eltY < eltHeight * 0.20) {
// If mouse is in the top part of the element, drop above folder.

Просмотреть файл

@ -1649,23 +1649,39 @@ PlacesTreeView.prototype = {
if (aColumn.index != 0)
return false;
// Only bookmark-nodes are editable, and those are never built lazily
let node = this._rows[aRow];
if (!node || node.itemId == -1)
if (!node) {
Cu.reportError("isEditable called for an unbuilt row.");
return false;
}
let itemId = node.itemId;
// Only bookmark-nodes are editable. Fortunately, this checks also takes
// care of livemark children.
if (itemId == -1)
return false;
// The following items are never editable:
// * Read-only items.
// The following items are also not editable, even though they are bookmark
// items.
// * places-roots
// * the left pane special folders and queries (those are place: uri
// bookmarks)
// * separators
if (PlacesUtils.nodeIsReadOnly(node) ||
PlacesUtils.nodeIsSeparator(node))
//
// Note that concrete itemIds aren't used intentionally. For example, we
// have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
// except for the one under All Bookmarks.
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemId))
return false;
if (PlacesUtils.nodeIsFolder(node)) {
let itemId = PlacesUtils.getConcreteItemId(node);
if (PlacesUtils.isRootItem(itemId))
return false;
let parentId = node.parent.itemId;
if (parentId == PlacesUIUtils.leftPaneFolderId ||
parentId == PlacesUIUtils.allBallBookmarksFolderId) {
// Note that the for the time being this is the check that actually
// blocks renaming places "roots", and not the isRootItem check above.
// That's because places root are only exposed through folder shortcuts
// descendants of the left pane folder.
return false;
}
return true;

Просмотреть файл

@ -27,8 +27,6 @@ function test() {
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.canMoveNode(rootNode.getChild(0)),
true, "can move regular folder node");
}
@ -57,9 +55,6 @@ function test() {
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.canMoveNode(shortcutNode),
true, "can move folder shortcut node");
}
@ -83,9 +78,6 @@ function test() {
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.canMoveNode(queryNode),
true, "can move query node");
}
@ -127,9 +119,6 @@ function test() {
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 = getRootChildNode(id);
isnot(node, null, "Node found");
is(PlacesControllerDragHelper.canMoveNode(node),
@ -140,10 +129,6 @@ function test() {
is(shortcutNode.itemId, shortcutId, "shortcut id and shortcut node item id match");
dump("can move shortcut id?\n");
is(PlacesControllerDragHelper.canMoveContainer(shortcutId),
true, "should be able to move special folder shortcut id");
dump("can move shortcut node?\n");
is(PlacesControllerDragHelper.canMoveNode(shortcutNode),
true, "should be able to move special folder shortcut node");
@ -169,46 +154,13 @@ function test() {
is(tagsNode.childCount, 1, "has new tag");
var tagNode = tagsNode.getChild(0);
is(PlacesControllerDragHelper.canMoveNode(tagNode),
false, "should not be able to move tag container node");
tagsNode.containerOpen = false;
}
});
// 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.canMoveNode(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.canMoveNode(childFolder),
false, "cannot move a child node of a read-only folder node");
readOnlyFolder.containerOpen = false;
}
});
tests.forEach(function(aTest) {
PlacesUtils.bookmarks.removeFolderChildren(rootId);
aTest.populate();

Просмотреть файл

@ -206,35 +206,8 @@ this.PlacesUtils = {
}
},
/**
* Cache array of read-only item IDs.
*
* The first time this property is called:
* - the cache is filled with all ids with the RO annotation
* - an annotation observer is added
* - a shutdown observer is added
*
* When the annotation observer detects annotations added or
* removed that are the RO annotation name, it adds/removes
* the ids from the cache.
*
* At shutdown, the annotation and shutdown observers are removed.
*/
get _readOnly() {
// Add annotations observer.
this.annotations.addObserver(this, false);
this.registerShutdownFunction(function () {
this.annotations.removeObserver(this);
});
var readOnly = this.annotations.getItemsWithAnnotation(this.READ_ONLY_ANNO);
this.__defineGetter__("_readOnly", function() readOnly);
return this._readOnly;
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIAnnotationObserver
, Ci.nsIObserver
Ci.nsIObserver
, Ci.nsITransactionListener
]),
@ -273,24 +246,6 @@ this.PlacesUtils = {
}
},
//////////////////////////////////////////////////////////////////////////////
//// nsIAnnotationObserver
onItemAnnotationSet: function PU_onItemAnnotationSet(aItemId, aAnnotationName)
{
if (aAnnotationName == this.READ_ONLY_ANNO &&
this._readOnly.indexOf(aItemId) == -1)
this._readOnly.push(aItemId);
},
onItemAnnotationRemoved:
function PU_onItemAnnotationRemoved(aItemId, aAnnotationName)
{
var index = this._readOnly.indexOf(aItemId);
if (aAnnotationName == this.READ_ONLY_ANNO && index > -1)
delete this._readOnly[index];
},
onPageAnnotationSet: function() {},
onPageAnnotationRemoved: function() {},
@ -340,27 +295,6 @@ this.PlacesUtils = {
willMerge: function PU_willMerge() {},
didMerge: function PU_didMerge() {},
/**
* Determines if a node is read only (children cannot be inserted, sometimes
* they cannot be removed depending on the circumstance)
* @param aNode
* A result node
* @returns true if the node is readonly, false otherwise
*/
nodeIsReadOnly: function PU_nodeIsReadOnly(aNode) {
let itemId = aNode.itemId;
if (itemId != -1) {
return this._readOnly.indexOf(itemId) != -1;
}
if (this.nodeIsQuery(aNode) &&
asQuery(aNode).queryOptions.resultType !=
Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS)
return aNode.childrenReadOnly;
return false;
},
/**
* Determines whether or not a ResultNode is a host container.
* @param aNode
@ -431,17 +365,6 @@ this.PlacesUtils = {
this.nodeIsHost(aNode));
},
/**
* Determines whether or not a node is a readonly folder.
* @param aNode
* The node to test.
* @returns true if the node is a readonly folder.
*/
isReadonlyFolder: function(aNode) {
return this.nodeIsFolder(aNode) &&
this._readOnly.indexOf(asQuery(aNode).folderItemId) != -1;
},
/**
* Gets the concrete item-id for the given node. Generally, this is just
* node.itemId, but for folder-shortcuts that's node.folderItemId.
@ -1111,10 +1034,9 @@ this.PlacesUtils = {
if (guid) {
aJSNode.itemGuid = guid;
var parent = aPlacesNode.parent;
if (parent) {
if (parent)
aJSNode.parent = parent.itemId;
aJSNode.parentReadOnly = PlacesUtils.nodeIsReadOnly(parent);
}
var dateAdded = aPlacesNode.dateAdded;
if (dateAdded)
aJSNode.dateAdded = dateAdded;

Просмотреть файл

@ -223,7 +223,7 @@ interface nsINavBookmarkObserver : nsISupports
* folders. A URI in history can be contained in one or more such folders.
*/
[scriptable, uuid(A78EA368-E28E-462E-897A-26606D4DDCE6)]
[scriptable, uuid(4C309044-B6DA-4511-AF57-E8940DB00045)]
interface nsINavBookmarksService : nsISupports
{
/**
@ -466,30 +466,6 @@ interface nsINavBookmarksService : nsISupports
*/
unsigned short getItemType(in long long aItemId);
/**
* Checks whether a folder is marked as read-only.
* If this is set to true, UI will not allow the user to add, remove,
* or reorder children in this folder. The default for all folders is false.
* Note: This does not restrict API calls, only UI actions.
*
* @param aItemId
* the item-id of the folder.
*/
boolean getFolderReadonly(in long long aItemId);
/**
* Sets or unsets the readonly flag from a folder.
* If this is set to true, UI will not allow the user to add, remove,
* or reorder children in this folder. The default for all folders is false.
* Note: This does not restrict API calls, only UI actions.
*
* @param aFolder
* the item-id of the folder.
* @param aReadOnly
* the read-only state (boolean).
*/
void setFolderReadonly(in long long aFolder, in boolean aReadOnly);
/**
* Returns true if the given URI is in any bookmark folder. If you want the
* results to be redirect-aware, use getBookmarkedURIFor()

Просмотреть файл

@ -175,7 +175,7 @@ interface nsINavHistoryResultNode : nsISupports
* Bookmark folders and places queries will be QueryResultNodes which extends
* these items.
*/
[scriptable, uuid(5bac9734-c0ff-44eb-8d19-da88462ff6da)]
[scriptable, uuid(3E9CC95F-0D93-45F1-894F-908EEB9866D7)]
interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
{
@ -256,15 +256,6 @@ interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
in PRTime aTime,
in long long aItemId,
in boolean aRecursive);
/**
* Returns false if this node's list of children can be modified
* (adding or removing children, or reordering children), or true if
* the UI should not allow the list of children to be modified.
* This is false for bookmark folder nodes unless setFolderReadOnly() has
* been called to override it, and true for non-folder nodes.
*/
readonly attribute boolean childrenReadOnly;
};
@ -275,7 +266,7 @@ interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
* generated this node, this item will report it has no children and never try
* to populate itself.
*/
[scriptable, uuid(a4144c3e-8125-46d5-a719-831bec8095f4)]
[scriptable, uuid(91AC5E59-3F5C-4ACD-AB3B-325FC425A5A1)]
interface nsINavHistoryQueryResultNode : nsINavHistoryContainerResultNode
{
/**
@ -1118,12 +1109,9 @@ interface nsINavHistoryQueryOptions : nsISupports
attribute boolean excludeQueries;
/**
* Set to true to exclude read-only folders from the query results. This is
* designed for cases where you want to give the user the option of filing
* something into a list of folders. It only affects cases where the actual
* folder result node would appear in its parent folder and filters it out.
* It doesn't affect the query at all, and doesn't affect more complex
* queries (such as "folders with annotation X").
* DO NOT USE THIS API. IT'LL BE REMOVED IN BUG 1072833.
*
* Set to true to exclude live bookmarks from the query results.
*/
attribute boolean excludeReadOnlyFolders;

Просмотреть файл

@ -547,7 +547,6 @@ function Livemark(aLivemarkInfo)
aLivemarkInfo.title,
aLivemarkInfo.index,
aLivemarkInfo.guid);
PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
this.writeFeedURI(aLivemarkInfo.feedURI);
if (aLivemarkInfo.siteURI) {
this.writeSiteURI(aLivemarkInfo.siteURI);

Просмотреть файл

@ -60,7 +60,7 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService)
#define BOOKMARKS_ANNO_PREFIX "bookmarks/"
#define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
#define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
#define FEED_URI_ANNO NS_LITERAL_CSTRING("livemark/feedURI")
namespace {
@ -790,46 +790,18 @@ nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName,
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetFolderReadonly(int64_t aFolder, bool* aResult)
bool nsNavBookmarks::IsLivemark(int64_t aFolderId)
{
NS_ENSURE_ARG_MIN(aFolder, 1);
NS_ENSURE_ARG_POINTER(aResult);
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
NS_ENSURE_TRUE(annosvc, false);
bool isLivemark;
nsresult rv = annosvc->ItemHasAnnotation(aFolderId,
FEED_URI_ANNO,
&isLivemark);
NS_ENSURE_SUCCESS(rv, false);
return isLivemark;
}
NS_IMETHODIMP
nsNavBookmarks::SetFolderReadonly(int64_t aFolder, bool aReadOnly)
{
NS_ENSURE_ARG_MIN(aFolder, 1);
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
if (aReadOnly) {
rv = annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1, 0,
nsAnnotationService::EXPIRE_NEVER);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
bool hasAnno;
rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno);
NS_ENSURE_SUCCESS(rv, rv);
if (hasAnno) {
rv = annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
nsresult
nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
int64_t aParent,
@ -1866,11 +1838,10 @@ nsNavBookmarks::ProcessFolderNodeRow(
}
}
else if (itemType == TYPE_FOLDER) {
// ExcludeReadOnlyFolders currently means "ExcludeLivemarks" (to be fixed in
// bug 1072833)
if (aOptions->ExcludeReadOnlyFolders()) {
// If the folder is read-only, skip it.
bool readOnly = false;
GetFolderReadonly(id, &readOnly);
if (readOnly)
if (IsLivemark(id))
return NS_OK;
}

Просмотреть файл

@ -234,6 +234,15 @@ private:
~nsNavBookmarks();
/**
* Checks whether or not aFolderId points to a live bookmark.
*
* @param aFolderId
* the item-id of the folder to check.
* @return true if aFolderId points to live bookmarks, false otherwise.
*/
bool IsLivemark(int64_t aFolderId);
/**
* Locates the root items in the bookmarks folder hierarchy assigning folder
* ids to the root properties that are exposed through the service interface.

Просмотреть файл

@ -322,13 +322,12 @@ NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode)
nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly,
const nsACString& aIconURI, uint32_t aContainerType,
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI),
mResult(nullptr),
mContainerType(aContainerType),
mExpanded(false),
mChildrenReadOnly(aReadOnly),
mOptions(aOptions),
mAsyncCanceledState(NOT_CANCELED)
{
@ -337,13 +336,12 @@ nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
PRTime aTime,
const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly,
const nsACString& aIconURI, uint32_t aContainerType,
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryResultNode(aURI, aTitle, 0, aTime, aIconURI),
mResult(nullptr),
mContainerType(aContainerType),
mExpanded(false),
mChildrenReadOnly(aReadOnly),
mOptions(aOptions),
mAsyncCanceledState(NOT_CANCELED)
{
@ -1716,16 +1714,6 @@ nsNavHistoryContainerResultNode::FindNodeByDetails(const nsACString& aURIString,
return NS_OK;
}
/**
* @note Overridden for folders to query the bookmarks service directly.
*/
NS_IMETHODIMP
nsNavHistoryContainerResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
{
*aChildrenReadOnly = mChildrenReadOnly;
return NS_OK;
}
/**
* HOW QUERY UPDATING WORKS
*
@ -1753,7 +1741,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
const nsACString& aQueryURI) :
nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, nullptr),
nullptr),
mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
mHasSearchTerms(false),
mContentsValid(false),
@ -1767,7 +1755,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, aOptions),
aOptions),
mQueries(aQueries),
mContentsValid(false),
mBatchChanges(0),
@ -1800,7 +1788,7 @@ nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
nsNavHistoryQueryOptions* aOptions) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI,
nsNavHistoryResultNode::RESULT_TYPE_QUERY,
true, aOptions),
aOptions),
mQueries(aQueries),
mContentsValid(false),
mBatchChanges(0),
@ -2988,7 +2976,7 @@ nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode(
int64_t aFolderId) :
nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(),
nsNavHistoryResultNode::RESULT_TYPE_FOLDER,
false, aOptions),
aOptions),
mContentsValid(false),
mQueryItemId(-1),
mIsRegisteredFolderObserver(false)
@ -3090,25 +3078,6 @@ nsNavHistoryFolderResultNode::GetItemId(int64_t* aItemId)
return NS_OK;
}
/**
* Here, we override the getter and ignore the value stored in our object.
* The bookmarks service can tell us whether this folder should be read-only
* or not.
*
* It would be nice to put this code in the folder constructor, but the
* database was complaining. I believe it is because most folders are created
* while enumerating the bookmarks table and having a statement open, and doing
* another statement might make it unhappy in some cases.
*/
NS_IMETHODIMP
nsNavHistoryFolderResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
{
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);
return bookmarks->GetFolderReadonly(mItemId, aChildrenReadOnly);
}
NS_IMETHODIMP
nsNavHistoryFolderResultNode::GetFolderItemId(int64_t* aItemId)
{

Просмотреть файл

@ -401,7 +401,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_II
// derived classes each provide their own implementation of has children and
// forward the rest to us using this macro
#define NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY \
#define NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN \
NS_IMETHOD GetState(uint16_t* _state) \
{ return nsNavHistoryContainerResultNode::GetState(_state); } \
NS_IMETHOD GetContainerOpen(bool *aContainerOpen) \
@ -430,12 +430,12 @@ public:
nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
const nsACString& aIconURI, uint32_t aContainerType,
bool aReadOnly, nsNavHistoryQueryOptions* aOptions);
nsNavHistoryQueryOptions* aOptions);
nsNavHistoryContainerResultNode(
const nsACString& aURI, const nsACString& aTitle,
PRTime aTime,
const nsACString& aIconURI, uint32_t aContainerType,
bool aReadOnly, nsNavHistoryQueryOptions* aOptions);
nsNavHistoryQueryOptions* aOptions);
virtual nsresult Refresh();
@ -479,8 +479,6 @@ public:
// Filled in by the result type generator in nsNavHistory.
nsCOMArray<nsNavHistoryResultNode> mChildren;
bool mChildrenReadOnly;
nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
void FillStats();
@ -644,10 +642,8 @@ public:
NS_IMETHOD GetType(uint32_t* type)
{ *type = nsNavHistoryResultNode::RESULT_TYPE_QUERY; return NS_OK; }
NS_IMETHOD GetUri(nsACString& aURI); // does special lazy creation
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN
NS_IMETHOD GetHasChildren(bool* aHasChildren);
NS_IMETHOD GetChildrenReadOnly(bool *aChildrenReadOnly)
{ return nsNavHistoryContainerResultNode::GetChildrenReadOnly(aChildrenReadOnly); }
NS_DECL_NSINAVHISTORYQUERYRESULTNODE
bool CanExpand();
@ -725,9 +721,8 @@ public:
return NS_OK;
}
NS_IMETHOD GetUri(nsACString& aURI);
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY
NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN
NS_IMETHOD GetHasChildren(bool* aHasChildren);
NS_IMETHOD GetChildrenReadOnly(bool *aChildrenReadOnly);
NS_IMETHOD GetItemId(int64_t *aItemId);
NS_DECL_NSINAVHISTORYQUERYRESULTNODE

Просмотреть файл

@ -152,8 +152,6 @@ function task_populateDB(aArray)
let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
qdata.title,
qdata.index);
if (qdata.readOnly)
PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
}
if (qdata.isLivemark) {
@ -246,7 +244,6 @@ function queryData(obj) {
this.dateAdded = obj.dateAdded ? obj.dateAdded : today;
this.keyword = obj.keyword ? obj.keyword : "";
this.visitCount = obj.visitCount ? obj.visitCount : 0;
this.readOnly = obj.readOnly ? obj.readOnly : false;
this.isSeparator = obj.hasOwnProperty("isSeparator") && obj.isSeparator;
// And now, the attribute for whether or not this object should appear in the

Просмотреть файл

@ -313,7 +313,6 @@ let DataHelper = {
case "folder":
return {
isFolder: true,
readOnly: false,
parentFolder: dat.parent,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
title: dat.title,

Просмотреть файл

@ -1,48 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// The test data for our database, note that the ordering of the results that
// will be returned by the query (the isInQuery: true objects) is IMPORTANT.
// see compareArrayToResult in head_queries.js for more info.
var testData = [
// Normal folder
{ isInQuery: true, isFolder: true, title: "Folder 1",
parentFolder: PlacesUtils.toolbarFolderId },
// Read only folder
{ isInQuery: false, isFolder: true, title: "Folder 2 RO",
parentFolder: PlacesUtils.toolbarFolderId, readOnly: true }
];
function run_test()
{
run_next_test();
}
add_task(function test_excludeReadOnlyFolders()
{
yield task_populateDB(testData);
var query = PlacesUtils.history.getNewQuery();
query.setFolders([PlacesUtils.toolbarFolderId], 1);
// Options
var options = PlacesUtils.history.getNewQueryOptions();
options.excludeQueries = true;
options.excludeReadOnlyFolders = true;
// Results
var result = PlacesUtils.history.executeQuery(query, options);
var root = result.root;
root.containerOpen = true;
displayResultSet(root);
// The readonly folder should not be in our result set.
do_check_eq(1, root.childCount);
do_check_eq("Folder 1", root.getChild(0).title);
root.containerOpen = false;
});

Просмотреть файл

@ -414,17 +414,6 @@ const queryOptionSwitches = [
}
]
},
// excludeReadOnlyFolders
{
property: "excludeReadOnlyFolders",
desc: "nsINavHistoryQueryOptions.excludeReadOnlyFolders",
matches: simplePropertyMatches,
runs: [
function (aQuery, aQueryOptions) {
aQueryOptions.excludeReadOnlyFolders = true;
}
]
},
// expandQueries
{
property: "expandQueries",

Просмотреть файл

@ -7,7 +7,6 @@ tail =
[test_abstime-annotation-uri.js]
[test_async.js]
[test_containersQueries_sorting.js]
[test_excludeReadOnlyFolders.js]
[test_history_queries_tags_liveUpdate.js]
[test_history_queries_titles_liveUpdate.js]
[test_onlyBookmarked.js]