diff --git a/browser/components/places/content/tree.xml b/browser/components/places/content/tree.xml index 50c5dfa4b9ab..1f85323139d7 100644 --- a/browser/components/places/content/tree.xml +++ b/browser/components/places/content/tree.xml @@ -402,11 +402,24 @@ canDrop: function VO_canDrop(index, orientation) { var root = this._self.getResult(); var node = index != -1 ? root.nodeForTreeIndex(index) : root; + const DROP_ON = Ci.nsINavHistoryResultViewObserver.DROP_ON; // Cannot drop before fixed items in the list. if (node.parent == root && PlacesController.getIndexOfNode(node) < this._self.firstDropIndex && - orientation != Ci.nsINavHistoryResultViewObserver.DROP_ON) + orientation != DROP_ON) return false; + + // Cannot drop into a read-only container + if (orientation == DROP_ON) { + if (node.containerReadOnly) { + return false; + } + } else { + if (node.parent && node.parent.containerReadOnly) { + return false; + } + } + return PlacesControllerDragHelper.canDrop(this._self, orientation); }, diff --git a/browser/components/places/public/nsINavBookmarksService.idl b/browser/components/places/public/nsINavBookmarksService.idl index f87779181d95..1ebb04aac0fe 100644 --- a/browser/components/places/public/nsINavBookmarksService.idl +++ b/browser/components/places/public/nsINavBookmarksService.idl @@ -274,6 +274,14 @@ interface nsINavBookmarksService : nsISupports */ AString getFolderTitle(in PRInt64 folder); + /** + * Marks a folder as having read-only children. + * If this is set to true, UI should not allow the user to add, remove, + * or reorder children in this folder. The default for all folders is false. + */ + void setFolderReadonly(in PRInt64 folder, in boolean readOnly); + boolean getFolderReadonly(in PRInt64 folder); + /** * Returns true if the given URI is in any bookmark folder. */ diff --git a/browser/components/places/public/nsINavHistory.idl b/browser/components/places/public/nsINavHistory.idl index 9fc535cf98a6..9ba4887f1675 100644 --- a/browser/components/places/public/nsINavHistory.idl +++ b/browser/components/places/public/nsINavHistory.idl @@ -146,6 +146,15 @@ interface nsINavHistoryResultNode : nsISupports */ readonly attribute PRInt32 childCount; nsINavHistoryResultNode getChild(in PRInt32 index); + + /** + * 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; }; diff --git a/browser/components/places/public/nsINavHistoryService.idl b/browser/components/places/public/nsINavHistoryService.idl index 9fc535cf98a6..9ba4887f1675 100644 --- a/browser/components/places/public/nsINavHistoryService.idl +++ b/browser/components/places/public/nsINavHistoryService.idl @@ -146,6 +146,15 @@ interface nsINavHistoryResultNode : nsISupports */ readonly attribute PRInt32 childCount; nsINavHistoryResultNode getChild(in PRInt32 index); + + /** + * 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; }; diff --git a/browser/components/places/src/nsNavBookmarks.cpp b/browser/components/places/src/nsNavBookmarks.cpp index 9050b1603f06..0e2dab0f534c 100644 --- a/browser/components/places/src/nsNavBookmarks.cpp +++ b/browser/components/places/src/nsNavBookmarks.cpp @@ -42,6 +42,7 @@ #include "mozStorageHelper.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" +#include "nsIAnnotationService.h" const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ItemChild = 0; const PRInt32 nsNavBookmarks::kFindBookmarksIndex_FolderChild = 1; @@ -59,6 +60,9 @@ const PRInt32 nsNavBookmarks::kGetChildrenIndex_FolderTitle = 10; nsNavBookmarks* nsNavBookmarks::sInstance = nsnull; +#define BOOKMARKS_ANNO_PREFIX "bookmarks/" +#define ANNO_FOLDER_READONLY BOOKMARKS_ANNO_PREFIX "readonly" + struct UpdateBatcher { UpdateBatcher() { nsNavBookmarks::GetBookmarksService()->BeginUpdateBatch(); } @@ -933,6 +937,64 @@ nsNavBookmarks::GetFolderTitle(PRInt64 aFolder, nsAString &aTitle) return mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Title, aTitle); } +NS_IMETHODIMP +nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, PRBool *aResult) +{ + // We store readonly-ness as an annotation on the place: URI of the folder + nsresult rv; + nsCOMPtr anno = + do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // For efficiency, we construct the URI here rather than going through + // query object construction. + nsCAutoString spec; + spec.AssignLiteral("place:folder="); + spec.AppendInt(aFolder); + spec.AppendLiteral("&group=3"); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), spec); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 readonly; + rv = anno->GetAnnotationInt32(uri, + NS_LITERAL_CSTRING(ANNO_FOLDER_READONLY), + &readonly); + *aResult = (NS_SUCCEEDED(rv) && readonly); + return NS_OK; +} + +NS_IMETHODIMP +nsNavBookmarks::SetFolderReadonly(PRInt64 aFolder, PRBool aReadonly) +{ + // We store readonly-ness as an annotation on the place: URI of the folder + nsresult rv; + nsCOMPtr anno = + do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // For efficiency, we construct the URI here rather than going through + // query object construction. + nsCAutoString spec; + spec.AssignLiteral("place:folder="); + spec.AppendInt(aFolder); + spec.AppendLiteral("&group=3"); + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), spec); + NS_ENSURE_SUCCESS(rv, rv); + + if (aReadonly) { + rv = anno->SetAnnotationInt32(uri, + NS_LITERAL_CSTRING(ANNO_FOLDER_READONLY), 1, + 0, nsIAnnotationService::EXPIRE_NEVER); + } else { + rv = anno->RemoveAnnotation(uri, + NS_LITERAL_CSTRING(ANNO_FOLDER_READONLY)); + } + + return rv; +} + nsresult nsNavBookmarks::ResultNodeForFolder(PRInt64 aID, nsINavHistoryQuery *aQuery, diff --git a/browser/components/places/src/nsNavHistory.h b/browser/components/places/src/nsNavHistory.h index 161af2843e72..d3469244171b 100644 --- a/browser/components/places/src/nsNavHistory.h +++ b/browser/components/places/src/nsNavHistory.h @@ -260,6 +260,7 @@ public: NS_IMETHOD GetQueries(PRUint32 *aQueryCount, nsINavHistoryQuery ***aQueries); NS_IMETHOD GetQueryOptions(nsINavHistoryQueryOptions **aOptions); + NS_IMETHOD GetChildrenReadOnly(PRBool *aResult); NS_DECL_BOOKMARK_HISTORY_OBSERVER diff --git a/browser/components/places/src/nsNavHistoryResult.cpp b/browser/components/places/src/nsNavHistoryResult.cpp index 7fd97567582a..ca6b23a5cb16 100755 --- a/browser/components/places/src/nsNavHistoryResult.cpp +++ b/browser/components/places/src/nsNavHistoryResult.cpp @@ -187,6 +187,13 @@ NS_IMETHODIMP nsNavHistoryResultNode::GetChild(PRInt32 aIndex, return NS_OK; } +/* readonly attribute boolean childrenReadOnly; */ +NS_IMETHODIMP nsNavHistoryResultNode::GetChildrenReadOnly(PRBool *aResult) +{ + *aResult = PR_TRUE; + return NS_OK; +} + // nsINavBookmarkObserver implementation /* void onBeginUpdateBatch(); */ @@ -508,6 +515,19 @@ nsNavHistoryQueryNode::GetWantAllDetails(PRBool *aResult) return NS_OK; } +NS_IMETHODIMP +nsNavHistoryQueryNode::GetChildrenReadOnly(PRBool *aResult) +{ + PRInt64 folderId = GetFolderId(); + if (folderId == 0) { + *aResult = PR_TRUE; + return NS_OK; + } + + return nsNavBookmarks::GetBookmarksService()->GetFolderReadonly(folderId, + aResult); +} + nsresult nsNavHistoryQueryNode::CreateNode(nsIURI *aBookmark, nsNavHistoryResultNode **aNode) diff --git a/browser/components/places/tests/testbookmarks.js b/browser/components/places/tests/testbookmarks.js index 835360f233a6..c881bd1b0f6d 100755 --- a/browser/components/places/tests/testbookmarks.js +++ b/browser/components/places/tests/testbookmarks.js @@ -264,6 +264,9 @@ if (bmsvc.indexOfItem(root, uri("http://google.com/")) != 1) { if (bmsvc.indexOfFolder(root, workFolder) != 4) { dump("indexOfFolder FAILED\n"); } +bmsvc.setFolderReadonly(workFolder, true); +bmsvc.setFolderReadonly(homeFolder, true); +bmsvc.setFolderReadonly(homeFolder, false); /// EXPECTED TABLE RESULTS /// moz_bookmarks: @@ -312,6 +315,8 @@ if (bmsvc.indexOfFolder(root, workFolder) != 4) { /// 15 http://developer.mozilla.org/devnews/ /// 16 http://espn.com/ /// 17 place: // Google Sites +/// 18 place:folder=5&group=3 +/// 19 place:folder=6&group=3 /// /// moz_bookmarks_folders: /// id name @@ -322,4 +327,8 @@ if (bmsvc.indexOfFolder(root, workFolder) != 4) { /// 4 Firefox and Mozilla Links /// 5 Work /// 6 Home - +/// +/// moz_anno: +/// id page name content expiration +/// -- ---- ------------------ ------- ---------- +/// 1 18 bookmarks/readonly 1 4