Bug 1068671 - Remove 'read-only' folders support from places. r=mak
☠☠ backed out by 5b0fb54a17c6 ☠ ☠
authorAsaf Romano <mano@mozilla.com>
Wed, 01 Oct 2014 12:19:06 +0300
changeset 208163 b30d47d94a85ac717dbd0b8942f435b195b2c42f
parent 208162 d472f51a9ce40ff8a8cd59bd5ae0a22e0eb15376
child 208164 5a6b95c47b5523a2f99959e2428688dacbfb9c10
push id27579
push userkwierso@gmail.com
push dateWed, 01 Oct 2014 23:02:13 +0000
treeherderautoland@f771fd927304 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1068671
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1068671 - Remove 'read-only' folders support from places. r=mak
browser/components/places/PlacesUIUtils.jsm
browser/components/places/content/browserPlacesViews.js
browser/components/places/content/controller.js
browser/components/places/content/menu.xml
browser/components/places/content/treeView.js
browser/components/places/tests/browser/browser_423515.js
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/nsINavBookmarksService.idl
toolkit/components/places/nsINavHistoryService.idl
toolkit/components/places/nsLivemarkService.js
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsNavHistoryResult.h
toolkit/components/places/tests/queries/head_queries.js
toolkit/components/places/tests/queries/test_async.js
toolkit/components/places/tests/queries/test_excludeReadOnlyFolders.js
toolkit/components/places/tests/queries/test_querySerialization.js
toolkit/components/places/tests/queries/xpcshell.ini
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -39,16 +39,53 @@ let CloudSync = null;
 #ifdef MOZ_SERVICES_SYNC
 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
                                   "resource://services-sync/main.js");
 #endif
 
 // 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",
   ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
 
   LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
   DESCRIPTION_ANNO: "bookmarkProperties/description",
 
@@ -556,16 +593,84 @@ this.PlacesUIUtils = {
    */
   getItemDescription: function PUIU_getItemDescription(aItemId) {
     if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
       return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
     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
    */
   _confirmOpenInTabs:
   function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
     const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
     var reallyOpen = true;
 
     if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
@@ -957,18 +1062,16 @@ this.PlacesUIUtils = {
       create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
               // Left Pane Root Folder.
         let folderId = bs.createFolder(aParentId,
                                        queries[aFolderName].title,
                                        bs.DEFAULT_INDEX);
         // 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.
           as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
                                PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
                                0, as.EXPIRE_NEVER);
         }
         else {
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1371,18 +1371,18 @@ PlacesToolbar.prototype = {
       return null;
 
     let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
     let elt = aEvent.target;
     if (elt._placesNode && elt != this._rootElt &&
         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.
         let threshold = eltRect.width * 0.25;
         if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
           // Drop before this folder.
           dropPoint.ip =
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -195,17 +195,17 @@ PlacesController.prototype = {
     case "placesCmd_reload":
       // Livemark containers
       var selectedNode = this._view.selectedNode;
       return selectedNode && this.hasCachedLivemarkInfo(selectedNode);
     case "placesCmd_sortBy:name":
       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":
       var node = this._view.selectedNode;
       return node && PlacesUtils.nodeIsURI(node) && node.itemId == -1;
     default:
       return false;
     }
@@ -325,31 +325,17 @@ PlacesController.prototype = {
 
     for (var j = 0; j < ranges.length; j++) {
       var nodes = ranges[j];
       for (var i = 0; i < nodes.length; ++i) {
         // Disallow removing the view's root node
         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;
       }
     }
 
     return true;
   },
 
   /**
@@ -1555,83 +1541,41 @@ let PlacesControllerDragHelper = {
    *
    * @param   aUnwrappedNode
    *          A node unwrapped by PlacesUtils.unwrapNodes().
    * @return True if the node can be moved, false otherwise.
    */
   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;
   },
 
   /**
    * Determines if a node can be moved.
    *
    * @param   aNode
    *          A nsINavHistoryResultNode node.
    * @return True if the node can be moved, false otherwise.
    */
   canMoveNode:
   function PCDH_canMoveNode(aNode) {
-    // Can't move query root.
-    if (!aNode.parent)
-      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))
+    // Only bookmark items are movable.
+    if (aNode.itemId == -1)
       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);
   },
 
   /**
    * Handles the drop of one or more items onto a view.
    * @param   insertionPoint
    *          The insertion point where the items should be dropped
    */
   onDrop: Task.async(function* (insertionPoint, dt) {
@@ -1721,22 +1665,20 @@ let PlacesControllerDragHelper = {
 
   /**
    * Checks if we can insert into a container.
    * @param   aContainer
    *          The container were we are want to drop
    */
   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));
   }
 };
 
 
 XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
                                    "@mozilla.org/widget/dragservice;1",
                                    "nsIDragService");
 
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -101,19 +101,19 @@
               if (isMenu && elt.lastChild &&
                   elt.lastChild.hasAttribute("placespopup"))
                 dropPoint.folderElt = elt;
               return dropPoint;
             }
 
             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.
                 dropPoint.ip = new InsertionPoint(
                                     PlacesUtils.getConcreteItemId(resultNode),
                                     -1,
                                     Ci.nsITreeView.DROP_BEFORE,
                                     tagName,
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -1644,33 +1644,49 @@ PlacesTreeView.prototype = {
     this._result.sortingMode = newSort;
   },
 
   isEditable: function PTV_isEditable(aRow, aColumn) {
     // At this point we only support editing the title field.
     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;
   },
 
   setCellText: function PTV_setCellText(aRow, aColumn, aText) {
     // We may only get here if the cell is editable.
     let node = this._rows[aRow];
--- a/browser/components/places/tests/browser/browser_423515.js
+++ b/browser/components/places/tests/browser/browser_423515.js
@@ -22,18 +22,16 @@ function test() {
   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.canMoveNode(rootNode.getChild(0)),
          true, "can move regular folder node");
     }
   });
 
   // add a regular folder shortcut, should be moveable
   tests.push({
     populate: function() {
@@ -52,19 +50,16 @@ function test() {
 
       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.canMoveNode(shortcutNode),
          true, "can move folder shortcut node");
     }
   });
 
   // add a regular query, should be moveable
   tests.push({
     populate: function() {
@@ -78,19 +73,16 @@ function test() {
         "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.canMoveNode(queryNode),
          true, "can move query node");
     }
   });
 
   // test that special folders cannot be moved
   // test that special folders shortcuts can be moved
   tests.push({
@@ -122,33 +114,26 @@ function test() {
         node.containerOpen = false;
         ok(false, "Unable to find child node");
         return null;
       }
 
       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),
            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");
 
-        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");
       }
     }
   });
 
   // test that a tag container cannot be moved
@@ -164,56 +149,23 @@ function test() {
       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.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();
     aTest.validate();
   });
 
   rootNode.containerOpen = false;
   PlacesUtils.bookmarks.removeItem(rootId);
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -201,45 +201,18 @@ this.PlacesUtils = {
   nodeAncestors: function PU_nodeAncestors(aNode) {
     let node = aNode.parent;
     while (node) {
       yield node;
       node = node.parent;
     }
   },
 
-  /**
-   * 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
   ]),
 
   _shutdownFunctions: [],
   registerShutdownFunction: function PU_registerShutdownFunction(aFunc)
   {
     // If this is the first registered function, add the shutdown observer.
     if (this._shutdownFunctions.length == 0) {
@@ -268,34 +241,16 @@ this.PlacesUtils = {
         while (this._bookmarksServiceObserversQueue.length > 0) {
           let observerInfo = this._bookmarksServiceObserversQueue.shift();
           this.bookmarks.addObserver(observerInfo.observer, observerInfo.weak);
         }
         break;
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// 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() {},
 
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsITransactionListener
 
   didDo: function PU_didDo(aManager, aTransaction, aDoResult)
@@ -335,37 +290,16 @@ this.PlacesUtils = {
   willUndo: function PU_willUndo() {},
   willRedo: function PU_willRedo() {},
   willBeginBatch: function PU_willBeginBatch() {},
   willEndBatch: function PU_willEndBatch() {},
   didEndBatch: function PU_didEndBatch() {},
   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
    *          A result node
    * @returns true if the node is a host container, false otherwise
    */
   nodeIsHost: function PU_nodeIsHost(aNode) {
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
@@ -427,27 +361,16 @@ this.PlacesUtils = {
               Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY ||
             resultType == Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY ||
             resultType == Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY ||
             this.nodeIsDay(aNode) ||
             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.
    */
   getConcreteItemId: function PU_getConcreteItemId(aNode) {
     if (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
       return asQuery(aNode).folderItemId;
     else if (PlacesUtils.nodeIsTagQuery(aNode)) {
       // RESULTS_AS_TAG_CONTENTS queries are similar to folder shortcuts
@@ -1106,20 +1029,19 @@ this.PlacesUtils = {
   _serializeNodeAsJSONToOutputStream: function (aNode, aStream) {
     function addGenericProperties(aPlacesNode, aJSNode) {
       aJSNode.title = aPlacesNode.title;
       aJSNode.id = aPlacesNode.itemId;
       let guid = aPlacesNode.bookmarkGuid;
       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;
         var lastModified = aPlacesNode.lastModified;
         if (lastModified)
           aJSNode.lastModified = lastModified;
 
         // XXX need a hasAnnos api
--- a/toolkit/components/places/nsINavBookmarksService.idl
+++ b/toolkit/components/places/nsINavBookmarksService.idl
@@ -218,17 +218,17 @@ interface nsINavBookmarkObserver : nsISu
 };
 
 /**
  * The BookmarksService interface provides methods for managing bookmarked
  * history items.  Bookmarks consist of a set of user-customizable
  * 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
 {
   /**
    * The item ID of the Places root.
    */
   readonly attribute long long placesRoot;
 
   /**
@@ -462,40 +462,16 @@ interface nsINavBookmarksService : nsISu
 
   /**
    * Get an item's type (bookmark, separator, folder).
    * The type is one of the TYPE_* constants defined above.
    */
   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()
    */
   boolean isBookmarked(in nsIURI aURI);
 
   /**
    * Used to see if the given URI is bookmarked, or any page that redirected to
    * it is bookmarked. For example, if I bookmark "mozilla.org" by manually
--- a/toolkit/components/places/nsINavHistoryService.idl
+++ b/toolkit/components/places/nsINavHistoryService.idl
@@ -170,17 +170,17 @@ interface nsINavHistoryResultNode : nsIS
 };
 
 
 /**
  * Base class for container results. This includes all types of groupings.
  * 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
 {
 
   /**
    * Set this to allow descent into the container. When closed, attempting
    * to call getChildren or childCount will result in an error. You should
    * set this to false when you are done reading.
    *
@@ -251,36 +251,27 @@ interface nsINavHistoryContainerResultNo
    * @throws NS_ERROR_NOT_AVAILABLE if this container is closed.
    * @return a result node that matches the given details if any, null
    *         otherwise.
    */
   nsINavHistoryResultNode findNodeByDetails(in AUTF8String aURIString,
                                             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;
 };
 
 
 /**
  * Used for places queries and as a base for bookmark folders.
  *
  * Note that if you request places to *not* be expanded in the options that
  * 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
 {
   /**
    * Get the queries which build this node's children.
    * Only valid for RESULT_TYPE_QUERY nodes.
    */
   void getQueries([optional] out unsigned long queryCount,
                   [retval,array,size_is(queryCount)] out nsINavHistoryQuery queries);
@@ -1113,22 +1104,19 @@ interface nsINavHistoryQueryOptions : ns
   /**
    * Set to true to exclude queries ("place:" URIs) from the query results.
    * Simple folder queries (bookmark folder symlinks) will still be included.
    * Defaults to false.
    */
   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;
 
   /**
    * When set, allows items with "place:" URIs to appear as containers,
    * with the container's contents filled in from the stored query.
    * If not set, these will appear as normal items. Doesn't do anything if
    * excludeQueries is set. Defaults to false.
--- a/toolkit/components/places/nsLivemarkService.js
+++ b/toolkit/components/places/nsLivemarkService.js
@@ -542,17 +542,16 @@ function Livemark(aLivemarkInfo)
     this.lastModified = aLivemarkInfo.lastModified;
   }
   else {
     // Create a new livemark.
     this.id = PlacesUtils.bookmarks.createFolder(aLivemarkInfo.parentId,
                                                  aLivemarkInfo.title,
                                                  aLivemarkInfo.index,
                                                  aLivemarkInfo.guid);
-    PlacesUtils.bookmarks.setFolderReadonly(this.id, true);
     this.writeFeedURI(aLivemarkInfo.feedURI);
     if (aLivemarkInfo.siteURI) {
       this.writeSiteURI(aLivemarkInfo.siteURI);
     }
     // Last modified time must be the last change.
     if (aLivemarkInfo.lastModified) {
       this.lastModified = aLivemarkInfo.lastModified;
       PlacesUtils.bookmarks.setItemLastModified(this.id, this.lastModified);
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -55,17 +55,17 @@ const int32_t nsNavBookmarks::kGetChildr
 const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 18;
 
 using namespace mozilla::places;
 
 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 {
 
 struct keywordSearchData
 {
   int64_t itemId;
   nsString keyword;
@@ -785,56 +785,28 @@ nsNavBookmarks::CreateFolder(int64_t aPa
   // will cause notifications to be sent to bookmark observers.
   int32_t localIndex = aIndex;
   nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex,
                                       aGUID, aNewFolder);
   NS_ENSURE_SUCCESS(rv, rv);
   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_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;
+  NS_ENSURE_TRUE(annosvc, false);
+  bool isLivemark;
+  nsresult rv = annosvc->ItemHasAnnotation(aFolderId,
+                                           FEED_URI_ANNO,
+                                           &isLivemark);
+  NS_ENSURE_SUCCESS(rv, false);
+  return isLivemark;
 }
 
-
 nsresult
 nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
                                       int64_t aParent,
                                       const nsACString& aTitle,
                                       bool aIsBookmarkFolder,
                                       int32_t* aIndex,
                                       const nsACString& aGUID,
                                       int64_t* aNewFolder)
@@ -1861,21 +1833,20 @@ nsNavBookmarks::ProcessFolderNodeRow(
          aOptions->ExcludeQueries()) ||
         (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
          nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT &&
          aOptions->ExcludeItems())) {
       return NS_OK;
     }
   }
   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;
     }
 
     nsAutoCString title;
     rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title);
     NS_ENSURE_SUCCESS(rv, rv);
 
     node = new nsNavHistoryFolderResultNode(title, aOptions, id);
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -230,16 +230,25 @@ public:
                                 nsTArray<int64_t>& aDescendantFoldersArray);
 
 private:
   static nsNavBookmarks* gBookmarksService;
 
   ~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.
    */
   nsresult ReadRoots();
 
   nsresult AdjustIndices(int64_t aFolder,
                          int32_t aStartIndex,
                          int32_t aEndIndex,
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -317,38 +317,36 @@ NS_IMPL_RELEASE_INHERITED(nsNavHistoryCo
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode)
 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)
 {
 }
 
 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)
 {
 }
 
 
 nsNavHistoryContainerResultNode::~nsNavHistoryContainerResultNode()
 {
@@ -1712,26 +1710,16 @@ nsNavHistoryContainerResultNode::FindNod
       }
     }
   }
   NS_IF_ADDREF(*_retval);
   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
  *
  * Queries are different than bookmark folders in that we can not always do
  * dynamic updates (easily) and updates are more expensive.  Therefore, we do
  * NOT query if we are not open and want to see if we have any children (for
  * drawing a twisty) and always assume we will.
  *
  * When the container is opened, we execute the query and register the
@@ -1748,31 +1736,31 @@ NS_IMPL_ISUPPORTS_INHERITED(nsNavHistory
                             nsNavHistoryContainerResultNode,
                             nsINavHistoryQueryResultNode)
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
     const nsACString& aTitle, const nsACString& aIconURI,
     const nsACString& aQueryURI) :
   nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
-                                  true, nullptr),
+                                  nullptr),
   mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
   mHasSearchTerms(false),
   mContentsValid(false),
   mBatchChanges(0)
 {
 }
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
     const nsACString& aTitle, const nsACString& aIconURI,
     const nsCOMArray<nsNavHistoryQuery>& aQueries,
     nsNavHistoryQueryOptions* aOptions) :
   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
-                                  true, aOptions),
+                                  aOptions),
   mQueries(aQueries),
   mContentsValid(false),
   mBatchChanges(0),
   mTransitions(mQueries[0]->Transitions())
 {
   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
@@ -1795,17 +1783,17 @@ nsNavHistoryQueryResultNode::nsNavHistor
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
     const nsACString& aTitle, const nsACString& aIconURI,
     PRTime aTime,
     const nsCOMArray<nsNavHistoryQuery>& aQueries,
     nsNavHistoryQueryOptions* aOptions) :
   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
-                                  true, aOptions),
+                                  aOptions),
   mQueries(aQueries),
   mContentsValid(false),
   mBatchChanges(0),
   mTransitions(mQueries[0]->Transitions())
 {
   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
@@ -2983,17 +2971,17 @@ NS_IMPL_ISUPPORTS_INHERITED(nsNavHistory
                             nsNavHistoryContainerResultNode,
                             nsINavHistoryQueryResultNode)
 
 nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode(
     const nsACString& aTitle, nsNavHistoryQueryOptions* aOptions,
     int64_t aFolderId) :
   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(),
                                   nsNavHistoryResultNode::RESULT_TYPE_FOLDER,
-                                  false, aOptions),
+                                  aOptions),
   mContentsValid(false),
   mQueryItemId(-1),
   mIsRegisteredFolderObserver(false)
 {
   mItemId = aFolderId;
 }
 
 nsNavHistoryFolderResultNode::~nsNavHistoryFolderResultNode()
@@ -3085,35 +3073,16 @@ nsNavHistoryFolderResultNode::GetHasChil
  */
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::GetItemId(int64_t* aItemId)
 {
   *aItemId = mQueryItemId == -1 ? mItemId : mQueryItemId;
   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)
 {
   *aItemId = mItemId;
   return NS_OK;
 }
 
 /**
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -396,17 +396,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 //
 //    This is the base class for all nodes that can have children. It is
 //    overridden for nodes that are dynamically populated such as queries and
 //    folders. It is used directly for simple containers such as host groups
 //    in history views.
 
 // 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) \
     { return nsNavHistoryContainerResultNode::GetContainerOpen(aContainerOpen); } \
   NS_IMETHOD SetContainerOpen(bool aContainerOpen) \
     { return nsNavHistoryContainerResultNode::SetContainerOpen(aContainerOpen); } \
   NS_IMETHOD GetChildCount(uint32_t *aChildCount) \
     { return nsNavHistoryContainerResultNode::GetChildCount(aChildCount); } \
@@ -425,22 +425,22 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 
 class nsNavHistoryContainerResultNode : public nsNavHistoryResultNode,
                                         public nsINavHistoryContainerResultNode
 {
 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();
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYCONTAINERRESULTNODE_IID)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE
@@ -474,18 +474,16 @@ public:
 
   // When there are children, this stores the open state in the tree
   // this is set to the default in the constructor.
   bool mExpanded;
 
   // Filled in by the result type generator in nsNavHistory.
   nsCOMArray<nsNavHistoryResultNode> mChildren;
 
-  bool mChildrenReadOnly;
-
   nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
 
   void FillStats();
   nsresult ReverseUpdateStats(int32_t aAccessCountChange);
 
   // Sorting methods.
   typedef nsCOMArray<nsNavHistoryResultNode>::nsCOMArrayComparatorFunc SortComparator;
   virtual uint16_t GetSortType();
@@ -639,20 +637,18 @@ public:
                               const nsCOMArray<nsNavHistoryQuery>& aQueries,
                               nsNavHistoryQueryOptions* aOptions);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE
   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();
   bool IsContainersQuery();
 
   virtual nsresult OpenContainer();
 
   NS_DECL_BOOKMARK_HISTORY_OBSERVER_INTERNAL
@@ -720,19 +716,18 @@ public:
     if (mQueryItemId != -1) {
       *type = nsNavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT;
     } else {
       *type = nsNavHistoryResultNode::RESULT_TYPE_FOLDER;
     }
     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
 
   virtual nsresult OpenContainer();
 
   virtual nsresult OpenContainerAsync();
   NS_DECL_ASYNCSTATEMENTCALLBACK
 
--- a/toolkit/components/places/tests/queries/head_queries.js
+++ b/toolkit/components/places/tests/queries/head_queries.js
@@ -147,18 +147,16 @@ function task_populateDB(aArray)
                                                         qdata.annoExpiration);
             }
           }
 
           if (qdata.isFolder) {
             let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
                                                               qdata.title,
                                                               qdata.index);
-            if (qdata.readOnly)
-              PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
           }
 
           if (qdata.isLivemark) {
             PlacesUtils.livemarks.addLivemark({ title: qdata.title
                                               , parentId: qdata.parentFolder
                                               , index: qdata.index
                                               , feedURI: uri(qdata.feedURI)
                                               , siteURI: uri(qdata.uri)
@@ -241,17 +239,16 @@ function queryData(obj) {
   this.feedURI = obj.feedURI ? obj.feedURI : "";
   this.index = obj.index ? obj.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
   this.isFolder = obj.isFolder ? obj.isFolder : false;
   this.contractId = obj.contractId ? obj.contractId : "";
   this.lastModified = obj.lastModified ? obj.lastModified : today;
   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
   // resulting query
   this.isInQuery = obj.isInQuery ? obj.isInQuery : false;
 }
 
 // All attributes are set in the constructor above
--- a/toolkit/components/places/tests/queries/test_async.js
+++ b/toolkit/components/places/tests/queries/test_async.js
@@ -308,17 +308,16 @@ let DataHelper = {
           isSeparator: true,
           parentFolder: dat.parent,
           index: PlacesUtils.bookmarks.DEFAULT_INDEX,
           isInQuery: true
         };
       case "folder":
         return {
           isFolder: true,
-          readOnly: false,
           parentFolder: dat.parent,
           index: PlacesUtils.bookmarks.DEFAULT_INDEX,
           title: dat.title,
           isInQuery: true
         };
       default:
         do_throw("Unknown data type when populating DB: " + type);
       }
deleted file mode 100644
--- a/toolkit/components/places/tests/queries/test_excludeReadOnlyFolders.js
+++ /dev/null
@@ -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;
-});
--- a/toolkit/components/places/tests/queries/test_querySerialization.js
+++ b/toolkit/components/places/tests/queries/test_querySerialization.js
@@ -409,27 +409,16 @@ const queryOptionSwitches = [
     desc:     "nsINavHistoryQueryOptions.excludeQueries",
     matches:  simplePropertyMatches,
     runs:     [
       function (aQuery, aQueryOptions) {
         aQueryOptions.excludeQueries = true;
       }
     ]
   },
-  // excludeReadOnlyFolders
-  {
-    property: "excludeReadOnlyFolders",
-    desc:     "nsINavHistoryQueryOptions.excludeReadOnlyFolders",
-    matches:  simplePropertyMatches,
-    runs:     [
-      function (aQuery, aQueryOptions) {
-        aQueryOptions.excludeReadOnlyFolders = true;
-      }
-    ]
-  },
   // expandQueries
   {
     property: "expandQueries",
     desc:     "nsINavHistoryQueryOptions.expandQueries",
     matches:  simplePropertyMatches,
     runs:     [
       function (aQuery, aQueryOptions) {
         aQueryOptions.expandQueries = true;
--- a/toolkit/components/places/tests/queries/xpcshell.ini
+++ b/toolkit/components/places/tests/queries/xpcshell.ini
@@ -2,17 +2,16 @@
 head = head_queries.js
 tail =
 
 [test_415716.js]
 [test_abstime-annotation-domain.js]
 [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]
 [test_querySerialization.js]
 [test_redirects.js]
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_results-as-tag-contents-query.js]