Bug 1341541 - WebExtensions bookmarks.getRecent() results include raw tag objects, r=mak,mixedpuppy
authorBob Silverberg <bsilverberg@mozilla.com>
Tue, 28 Feb 2017 08:44:43 -0500
changeset 346185 353df4a8b88c250f2ee26c16a9808162a5047508
parent 346184 e020ee311646d64aba60b5b8f666ba67b05d18b4
child 346186 3cbd95cc577d95b7938f683feb2d5248909237e6
push id38457
push userbsilverberg@mozilla.com
push dateTue, 07 Mar 2017 13:50:39 +0000
treeherderautoland@353df4a8b88c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, mixedpuppy
bugs1341541
milestone54.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 1341541 - WebExtensions bookmarks.getRecent() results include raw tag objects, r=mak,mixedpuppy MozReview-Commit-ID: 8FabO0eoktP
browser/components/extensions/test/xpcshell/test_ext_bookmarks.js
toolkit/components/places/Bookmarks.jsm
toolkit/components/places/tests/bookmarks/test_bookmarks_getRecent.js
--- a/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_bookmarks.js
@@ -1,601 +1,658 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-function backgroundScript() {
-  let unsortedId, ourId;
-  let initialBookmarkCount = 0;
-  let createdBookmarks = new Set();
-  let createdFolderId;
-  let collectedEvents = [];
-  const nonExistentId = "000000000000";
-  const bookmarkGuids = {
-    menuGuid:    "menu________",
-    toolbarGuid: "toolbar_____",
-    unfiledGuid: "unfiled_____",
-  };
-
-  function checkOurBookmark(bookmark) {
-    browser.test.assertEq(ourId, bookmark.id, "Bookmark has the expected Id");
-    browser.test.assertTrue("parentId" in bookmark, "Bookmark has a parentId");
-    browser.test.assertEq(0, bookmark.index, "Bookmark has the expected index"); // We assume there are no other bookmarks.
-    browser.test.assertEq("http://example.org/", bookmark.url, "Bookmark has the expected url");
-    browser.test.assertEq("test bookmark", bookmark.title, "Bookmark has the expected title");
-    browser.test.assertTrue("dateAdded" in bookmark, "Bookmark has a dateAdded");
-    browser.test.assertFalse("dateGroupModified" in bookmark, "Bookmark does not have a dateGroupModified");
-    browser.test.assertFalse("unmodifiable" in bookmark, "Bookmark is not unmodifiable");
-  }
-
-  function checkBookmark(expected, bookmark) {
-    browser.test.assertEq(expected.url, bookmark.url, "Bookmark has the expected url");
-    browser.test.assertEq(expected.title, bookmark.title, "Bookmark has the expected title");
-    browser.test.assertEq(expected.index, bookmark.index, "Bookmark has expected index");
-    if ("parentId" in expected) {
-      browser.test.assertEq(expected.parentId, bookmark.parentId, "Bookmark has the expected parentId");
-    }
-  }
-
-  function expectedError() {
-    browser.test.fail("Did not get expected error");
-  }
-
-  function checkOnCreated(id, parentId, index, title, url, dateAdded) {
-    let createdData = collectedEvents.pop();
-    browser.test.assertEq("onCreated", createdData.event, "onCreated was the last event received");
-    browser.test.assertEq(id, createdData.id, "onCreated event received the expected id");
-    let bookmark = createdData.bookmark;
-    browser.test.assertEq(id, bookmark.id, "onCreated event received the expected bookmark id");
-    browser.test.assertEq(parentId, bookmark.parentId, "onCreated event received the expected bookmark parentId");
-    browser.test.assertEq(index, bookmark.index, "onCreated event received the expected bookmark index");
-    browser.test.assertEq(title, bookmark.title, "onCreated event received the expected bookmark title");
-    browser.test.assertEq(url, bookmark.url, "onCreated event received the expected bookmark url");
-    browser.test.assertEq(dateAdded, bookmark.dateAdded, "onCreated event received the expected bookmark dateAdded");
-  }
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
 
-  function checkOnChanged(id, url, title) {
-    // If both url and title are changed, then url is fired last.
-    let changedData = collectedEvents.pop();
-    browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
-    browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
-    browser.test.assertEq(url, changedData.info.url, "onChanged event received the expected url");
-    // title is fired first.
-    changedData = collectedEvents.pop();
-    browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
-    browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
-    browser.test.assertEq(title, changedData.info.title, "onChanged event received the expected title");
-  }
-
-  function checkOnMoved(id, parentId, oldParentId, index, oldIndex) {
-    let movedData = collectedEvents.pop();
-    browser.test.assertEq("onMoved", movedData.event, "onMoved was the last event received");
-    browser.test.assertEq(id, movedData.id, "onMoved event received the expected id");
-    let info = movedData.info;
-    browser.test.assertEq(parentId, info.parentId, "onMoved event received the expected parentId");
-    browser.test.assertEq(oldParentId, info.oldParentId, "onMoved event received the expected oldParentId");
-    browser.test.assertEq(index, info.index, "onMoved event received the expected index");
-    browser.test.assertEq(oldIndex, info.oldIndex, "onMoved event received the expected oldIndex");
-  }
-
-  function checkOnRemoved(id, parentId, index, url) {
-    let removedData = collectedEvents.pop();
-    browser.test.assertEq("onRemoved", removedData.event, "onRemoved was the last event received");
-    browser.test.assertEq(id, removedData.id, "onRemoved event received the expected id");
-    let info = removedData.info;
-    browser.test.assertEq(parentId, removedData.info.parentId, "onRemoved event received the expected parentId");
-    browser.test.assertEq(index, removedData.info.index, "onRemoved event received the expected index");
-    let node = info.node;
-    browser.test.assertEq(id, node.id, "onRemoved event received the expected node id");
-    browser.test.assertEq(parentId, node.parentId, "onRemoved event received the expected node parentId");
-    browser.test.assertEq(index, node.index, "onRemoved event received the expected node index");
-    browser.test.assertEq(url, node.url, "onRemoved event received the expected node url");
-  }
-
-  browser.bookmarks.onChanged.addListener((id, info) => {
-    collectedEvents.push({event: "onChanged", id, info});
-  });
-
-  browser.bookmarks.onCreated.addListener((id, bookmark) => {
-    collectedEvents.push({event: "onCreated", id, bookmark});
-  });
-
-  browser.bookmarks.onMoved.addListener((id, info) => {
-    collectedEvents.push({event: "onMoved", id, info});
-  });
-
-  browser.bookmarks.onRemoved.addListener((id, info) => {
-    collectedEvents.push({event: "onRemoved", id, info});
-  });
-
-  browser.bookmarks.get(["not-a-bookmark-guid"]).then(expectedError, invalidGuidError => {
-    browser.test.assertTrue(
-      invalidGuidError.message.includes("Invalid value for property 'guid': not-a-bookmark-guid"),
-      "Expected error thrown when trying to get a bookmark using an invalid guid"
-    );
+add_task(async function test_bookmarks() {
+  function background() {
+    let unsortedId, ourId;
+    let initialBookmarkCount = 0;
+    let createdBookmarks = new Set();
+    let createdFolderId;
+    let collectedEvents = [];
+    const nonExistentId = "000000000000";
+    const bookmarkGuids = {
+      menuGuid:    "menu________",
+      toolbarGuid: "toolbar_____",
+      unfiledGuid: "unfiled_____",
+    };
 
-    return browser.bookmarks.get([nonExistentId]).then(expectedError, nonExistentIdError => {
-      browser.test.assertTrue(
-        nonExistentIdError.message.includes("Bookmark not found"),
-        "Expected error thrown when trying to get a bookmark using a non-existent Id"
-      );
-    });
-  }).then(() => {
-    return browser.bookmarks.search({});
-  }).then(results => {
-    initialBookmarkCount = results.length;
-    return browser.bookmarks.create({title: "test bookmark", url: "http://example.org"});
-  }).then(result => {
-    ourId = result.id;
-    checkOurBookmark(result);
-    browser.test.assertEq(1, collectedEvents.length, "1 expected event received");
-    checkOnCreated(ourId, bookmarkGuids.unfiledGuid, 0, "test bookmark", "http://example.org/", result.dateAdded);
-
-    return browser.bookmarks.get(ourId);
-  }).then(results => {
-    browser.test.assertEq(results.length, 1);
-    checkOurBookmark(results[0]);
-
-    unsortedId = results[0].parentId;
-    return browser.bookmarks.get(unsortedId);
-  }).then(results => {
-    let folder = results[0];
-    browser.test.assertEq(1, results.length, "1 bookmark was returned");
-
-    browser.test.assertEq(unsortedId, folder.id, "Folder has the expected id");
-    browser.test.assertTrue("parentId" in folder, "Folder has a parentId");
-    browser.test.assertTrue("index" in folder, "Folder has an index");
-    browser.test.assertFalse("url" in folder, "Folder does not have a url");
-    browser.test.assertEq("Other Bookmarks", folder.title, "Folder has the expected title");
-    browser.test.assertTrue("dateAdded" in folder, "Folder has a dateAdded");
-    browser.test.assertTrue("dateGroupModified" in folder, "Folder has a dateGroupModified");
-    browser.test.assertFalse("unmodifiable" in folder, "Folder is not unmodifiable"); // TODO: Do we want to enable this?
-
-    return browser.bookmarks.getChildren(unsortedId);
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "The folder has one child");
-    checkOurBookmark(results[0]);
-
-    return browser.bookmarks.update(nonExistentId, {title: "new test title"}).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("No bookmarks found for the provided GUID"),
-        "Expected error thrown when trying to update a non-existent bookmark"
-      );
-
-      return browser.bookmarks.update(ourId, {title: "new test title", url: "http://example.com/"});
-    });
-  }).then(result => {
-    browser.test.assertEq("new test title", result.title, "Updated bookmark has the expected title");
-    browser.test.assertEq("http://example.com/", result.url, "Updated bookmark has the expected URL");
-    browser.test.assertEq(ourId, result.id, "Updated bookmark has the expected id");
-
-    browser.test.assertEq(2, collectedEvents.length, "2 expected events received");
-    checkOnChanged(ourId, "http://example.com/", "new test title");
+    function checkOurBookmark(bookmark) {
+      browser.test.assertEq(ourId, bookmark.id, "Bookmark has the expected Id");
+      browser.test.assertTrue("parentId" in bookmark, "Bookmark has a parentId");
+      browser.test.assertEq(0, bookmark.index, "Bookmark has the expected index"); // We assume there are no other bookmarks.
+      browser.test.assertEq("http://example.org/", bookmark.url, "Bookmark has the expected url");
+      browser.test.assertEq("test bookmark", bookmark.title, "Bookmark has the expected title");
+      browser.test.assertTrue("dateAdded" in bookmark, "Bookmark has a dateAdded");
+      browser.test.assertFalse("dateGroupModified" in bookmark, "Bookmark does not have a dateGroupModified");
+      browser.test.assertFalse("unmodifiable" in bookmark, "Bookmark is not unmodifiable");
+    }
 
-    return Promise.resolve().then(() => {
-      return browser.bookmarks.update(ourId, {url: "this is not a valid url"});
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Invalid bookmark:"),
-        "Expected error thrown when trying update with an invalid url"
-      );
-      return browser.bookmarks.getTree();
-    });
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "getTree returns one result");
-    let bookmark = results[0].children.find(bookmarkItem => bookmarkItem.id == unsortedId);
-    browser.test.assertEq(
-        "Other Bookmarks",
-        bookmark.title,
-        "Folder returned from getTree has the expected title"
-    );
-
-    return browser.bookmarks.create({parentId: "invalid"}).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Invalid bookmark"),
-        "Expected error thrown when trying to create a bookmark with an invalid parentId"
-      );
-      browser.test.assertTrue(
-          error.message.includes(`"parentGuid":"invalid"`),
-          "Expected error thrown when trying to create a bookmark with an invalid parentId"
-      );
-    });
-  }).then(() => {
-    return browser.bookmarks.remove(ourId);
-  }).then(result => {
-    browser.test.assertEq(undefined, result, "Removing a bookmark returns undefined");
-
-    browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-    checkOnRemoved(ourId, bookmarkGuids.unfiledGuid, 0, "http://example.com/");
-
-    return browser.bookmarks.get(ourId).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Bookmark not found"),
-        "Expected error thrown when trying to get a removed bookmark"
-      );
-    });
-  }).then(() => {
-    return browser.bookmarks.remove(nonExistentId).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("No bookmarks found for the provided GUID"),
-        "Expected error thrown when trying removed a non-existent bookmark"
-      );
-    });
-  }).then(() => {
-    // test bookmarks.search
-    return Promise.all([
-      browser.bookmarks.create({title: "MØzillä", url: "http://møzîllä.örg/"}),
-      browser.bookmarks.create({title: "Example", url: "http://example.org/"}),
-      browser.bookmarks.create({title: "Mozilla Folder"}),
-      browser.bookmarks.create({title: "EFF", url: "http://eff.org/"}),
-      browser.bookmarks.create({title: "Menu Item", url: "http://menu.org/", parentId: bookmarkGuids.menuGuid}),
-      browser.bookmarks.create({title: "Toolbar Item", url: "http://toolbar.org/", parentId: bookmarkGuids.toolbarGuid}),
-    ]);
-  }).then(results => {
-    browser.test.assertEq(6, collectedEvents.length, "6 expected events received");
-    checkOnCreated(results[5].id, bookmarkGuids.toolbarGuid, 0, "Toolbar Item", "http://toolbar.org/", results[5].dateAdded);
-    checkOnCreated(results[4].id, bookmarkGuids.menuGuid, 0, "Menu Item", "http://menu.org/", results[4].dateAdded);
-    checkOnCreated(results[3].id, bookmarkGuids.unfiledGuid, 0, "EFF", "http://eff.org/", results[3].dateAdded);
-    checkOnCreated(results[2].id, bookmarkGuids.unfiledGuid, 0, "Mozilla Folder", undefined, results[2].dateAdded);
-    checkOnCreated(results[1].id, bookmarkGuids.unfiledGuid, 0, "Example", "http://example.org/", results[1].dateAdded);
-    checkOnCreated(results[0].id, bookmarkGuids.unfiledGuid, 0, "MØzillä", "http://møzîllä.örg/", results[0].dateAdded);
-
-    for (let result of results) {
-      if (result.title !== "Mozilla Folder") {
-        createdBookmarks.add(result.id);
+    function checkBookmark(expected, bookmark) {
+      browser.test.assertEq(expected.url, bookmark.url, "Bookmark has the expected url");
+      browser.test.assertEq(expected.title, bookmark.title, "Bookmark has the expected title");
+      browser.test.assertEq(expected.index, bookmark.index, "Bookmark has expected index");
+      if ("parentId" in expected) {
+        browser.test.assertEq(expected.parentId, bookmark.parentId, "Bookmark has the expected parentId");
       }
     }
-    let folderResult = results[2];
-    createdFolderId = folderResult.id;
-    return Promise.all([
-      browser.bookmarks.create({title: "Mozilla", url: "http://allizom.org/", parentId: createdFolderId}),
-      browser.bookmarks.create({title: "Mozilla Corporation", url: "http://allizom.com/", parentId: createdFolderId}),
-      browser.bookmarks.create({title: "Firefox", url: "http://allizom.org/firefox/", parentId: createdFolderId}),
-    ]).then(newBookmarks => {
-      browser.test.assertEq(3, collectedEvents.length, "3 expected events received");
-      checkOnCreated(newBookmarks[2].id, createdFolderId, 0, "Firefox", "http://allizom.org/firefox/", newBookmarks[2].dateAdded);
-      checkOnCreated(newBookmarks[1].id, createdFolderId, 0, "Mozilla Corporation", "http://allizom.com/", newBookmarks[1].dateAdded);
-      checkOnCreated(newBookmarks[0].id, createdFolderId, 0, "Mozilla", "http://allizom.org/", newBookmarks[0].dateAdded);
+
+    function expectedError() {
+      browser.test.fail("Did not get expected error");
+    }
 
-      return browser.bookmarks.create({
-        title: "About Mozilla",
-        url: "http://allizom.org/about/",
-        parentId: createdFolderId,
-        index: 1,
-      });
-    }).then(result => {
-      browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnCreated(result.id, createdFolderId, 1, "About Mozilla", "http://allizom.org/about/", result.dateAdded);
+    function checkOnCreated(id, parentId, index, title, url, dateAdded) {
+      let createdData = collectedEvents.pop();
+      browser.test.assertEq("onCreated", createdData.event, "onCreated was the last event received");
+      browser.test.assertEq(id, createdData.id, "onCreated event received the expected id");
+      let bookmark = createdData.bookmark;
+      browser.test.assertEq(id, bookmark.id, "onCreated event received the expected bookmark id");
+      browser.test.assertEq(parentId, bookmark.parentId, "onCreated event received the expected bookmark parentId");
+      browser.test.assertEq(index, bookmark.index, "onCreated event received the expected bookmark index");
+      browser.test.assertEq(title, bookmark.title, "onCreated event received the expected bookmark title");
+      browser.test.assertEq(url, bookmark.url, "onCreated event received the expected bookmark url");
+      browser.test.assertEq(dateAdded, bookmark.dateAdded, "onCreated event received the expected bookmark dateAdded");
+    }
 
-      // returns all items on empty object
-      return browser.bookmarks.search({});
-    }).then(bookmarksSearchResults => {
-      browser.test.assertTrue(bookmarksSearchResults.length >= 9, "At least as many bookmarks as added were returned by search({})");
+    function checkOnChanged(id, url, title) {
+      // If both url and title are changed, then url is fired last.
+      let changedData = collectedEvents.pop();
+      browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
+      browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
+      browser.test.assertEq(url, changedData.info.url, "onChanged event received the expected url");
+      // title is fired first.
+      changedData = collectedEvents.pop();
+      browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
+      browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
+      browser.test.assertEq(title, changedData.info.title, "onChanged event received the expected title");
+    }
 
-      return Promise.resolve().then(() => {
-        return browser.bookmarks.remove(createdFolderId);
-      }).then(expectedError, error => {
-        browser.test.assertTrue(
-          error.message.includes("Cannot remove a non-empty folder"),
-          "Expected error thrown when trying to remove a non-empty folder"
-        );
-        return browser.bookmarks.getSubTree(createdFolderId);
-      });
+    function checkOnMoved(id, parentId, oldParentId, index, oldIndex) {
+      let movedData = collectedEvents.pop();
+      browser.test.assertEq("onMoved", movedData.event, "onMoved was the last event received");
+      browser.test.assertEq(id, movedData.id, "onMoved event received the expected id");
+      let info = movedData.info;
+      browser.test.assertEq(parentId, info.parentId, "onMoved event received the expected parentId");
+      browser.test.assertEq(oldParentId, info.oldParentId, "onMoved event received the expected oldParentId");
+      browser.test.assertEq(index, info.index, "onMoved event received the expected index");
+      browser.test.assertEq(oldIndex, info.oldIndex, "onMoved event received the expected oldIndex");
+    }
+
+    function checkOnRemoved(id, parentId, index, url) {
+      let removedData = collectedEvents.pop();
+      browser.test.assertEq("onRemoved", removedData.event, "onRemoved was the last event received");
+      browser.test.assertEq(id, removedData.id, "onRemoved event received the expected id");
+      let info = removedData.info;
+      browser.test.assertEq(parentId, removedData.info.parentId, "onRemoved event received the expected parentId");
+      browser.test.assertEq(index, removedData.info.index, "onRemoved event received the expected index");
+      let node = info.node;
+      browser.test.assertEq(id, node.id, "onRemoved event received the expected node id");
+      browser.test.assertEq(parentId, node.parentId, "onRemoved event received the expected node parentId");
+      browser.test.assertEq(index, node.index, "onRemoved event received the expected node index");
+      browser.test.assertEq(url, node.url, "onRemoved event received the expected node url");
+    }
+
+    browser.bookmarks.onChanged.addListener((id, info) => {
+      collectedEvents.push({event: "onChanged", id, info});
     });
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of nodes returned by getSubTree");
-    browser.test.assertEq("Mozilla Folder", results[0].title, "Folder has the expected title");
-    let children = results[0].children;
-    browser.test.assertEq(4, children.length, "Expected number of bookmarks returned by getSubTree");
-    browser.test.assertEq("Firefox", children[0].title, "Bookmark has the expected title");
-    browser.test.assertEq("About Mozilla", children[1].title, "Bookmark has the expected title");
-    browser.test.assertEq(1, children[1].index, "Bookmark has the expected index");
-    browser.test.assertEq("Mozilla Corporation", children[2].title, "Bookmark has the expected title");
-    browser.test.assertEq("Mozilla", children[3].title, "Bookmark has the expected title");
+
+    browser.bookmarks.onCreated.addListener((id, bookmark) => {
+      collectedEvents.push({event: "onCreated", id, bookmark});
+    });
 
-    // throws an error for invalid query objects
-    Promise.resolve().then(() => {
-      return browser.bookmarks.search();
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Incorrect argument types for bookmarks.search"),
-        "Expected error thrown when trying to search with no arguments"
-      );
+    browser.bookmarks.onMoved.addListener((id, info) => {
+      collectedEvents.push({event: "onMoved", id, info});
+    });
+
+    browser.bookmarks.onRemoved.addListener((id, info) => {
+      collectedEvents.push({event: "onRemoved", id, info});
     });
 
-    Promise.resolve().then(() => {
-      return browser.bookmarks.search(null);
-    }).then(expectedError, error => {
+    browser.bookmarks.get(["not-a-bookmark-guid"]).then(expectedError, invalidGuidError => {
       browser.test.assertTrue(
-        error.message.includes("Incorrect argument types for bookmarks.search"),
-        "Expected error thrown when trying to search with null as an argument"
+        invalidGuidError.message.includes("Invalid value for property 'guid': not-a-bookmark-guid"),
+        "Expected error thrown when trying to get a bookmark using an invalid guid"
       );
-    });
-
-    Promise.resolve().then(() => {
-      return browser.bookmarks.search(function() {});
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Incorrect argument types for bookmarks.search"),
-        "Expected error thrown when trying to search with a function as an argument"
-      );
-    });
 
-    Promise.resolve().then(() => {
-      return browser.bookmarks.search({banana: "banana"});
-    }).then(expectedError, error => {
-      let substr = `an unexpected "banana" property`;
-      browser.test.assertTrue(
-        error.message.includes(substr),
-        `Expected error ${JSON.stringify(error.message)} to contain ${JSON.stringify(substr)}`);
-    });
-
-    Promise.resolve().then(() => {
-      return browser.bookmarks.search({url: "spider-man vs. batman"});
-    }).then(expectedError, error => {
-      let substr = 'must match the format "url"';
-      browser.test.assertTrue(
-        error.message.includes(substr),
-        `Expected error ${JSON.stringify(error.message)} to contain ${JSON.stringify(substr)}`);
-    });
-
-    // queries the full url
-    return browser.bookmarks.search("http://example.org/");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for url search");
-    checkBookmark({title: "Example", url: "http://example.org/", index: 2}, results[0]);
+      return browser.bookmarks.get([nonExistentId]).then(expectedError, nonExistentIdError => {
+        browser.test.assertTrue(
+          nonExistentIdError.message.includes("Bookmark not found"),
+          "Expected error thrown when trying to get a bookmark using a non-existent Id"
+        );
+      });
+    }).then(() => {
+      return browser.bookmarks.search({});
+    }).then(results => {
+      initialBookmarkCount = results.length;
+      return browser.bookmarks.create({title: "test bookmark", url: "http://example.org"});
+    }).then(result => {
+      ourId = result.id;
+      checkOurBookmark(result);
+      browser.test.assertEq(1, collectedEvents.length, "1 expected event received");
+      checkOnCreated(ourId, bookmarkGuids.unfiledGuid, 0, "test bookmark", "http://example.org/", result.dateAdded);
 
-    // queries a partial url
-    return browser.bookmarks.search("example.org");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for url search");
-    checkBookmark({title: "Example", url: "http://example.org/", index: 2}, results[0]);
+      return browser.bookmarks.get(ourId);
+    }).then(results => {
+      browser.test.assertEq(results.length, 1);
+      checkOurBookmark(results[0]);
 
-    // queries the title
-    return browser.bookmarks.search("EFF");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for title search");
-    checkBookmark({title: "EFF", url: "http://eff.org/", index: 0, parentId: bookmarkGuids.unfiledGuid}, results[0]);
-
-    // finds menu items
-    return browser.bookmarks.search("Menu Item");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for menu item search");
-    checkBookmark({title: "Menu Item", url: "http://menu.org/", index: 0, parentId: bookmarkGuids.menuGuid}, results[0]);
+      unsortedId = results[0].parentId;
+      return browser.bookmarks.get(unsortedId);
+    }).then(results => {
+      let folder = results[0];
+      browser.test.assertEq(1, results.length, "1 bookmark was returned");
 
-    // finds toolbar items
-    return browser.bookmarks.search("Toolbar Item");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for toolbar item search");
-    checkBookmark({title: "Toolbar Item", url: "http://toolbar.org/", index: 0, parentId: bookmarkGuids.toolbarGuid}, results[0]);
-
-    // finds folders
-    return browser.bookmarks.search("Mozilla Folder");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of folders returned");
-    browser.test.assertEq("Mozilla Folder", results[0].title, "Folder has the expected title");
+      browser.test.assertEq(unsortedId, folder.id, "Folder has the expected id");
+      browser.test.assertTrue("parentId" in folder, "Folder has a parentId");
+      browser.test.assertTrue("index" in folder, "Folder has an index");
+      browser.test.assertFalse("url" in folder, "Folder does not have a url");
+      browser.test.assertEq("Other Bookmarks", folder.title, "Folder has the expected title");
+      browser.test.assertTrue("dateAdded" in folder, "Folder has a dateAdded");
+      browser.test.assertTrue("dateGroupModified" in folder, "Folder has a dateGroupModified");
+      browser.test.assertFalse("unmodifiable" in folder, "Folder is not unmodifiable"); // TODO: Do we want to enable this?
 
-    // is case-insensitive
-    return browser.bookmarks.search("corporation");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returnedfor case-insensitive search");
-    browser.test.assertEq("Mozilla Corporation", results[0].title, "Bookmark has the expected title");
-
-    // is case-insensitive for non-ascii
-    return browser.bookmarks.search("MøZILLÄ");
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for non-ascii search");
-    browser.test.assertEq("MØzillä", results[0].title, "Bookmark has the expected title");
+      return browser.bookmarks.getChildren(unsortedId);
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "The folder has one child");
+      checkOurBookmark(results[0]);
 
-    // returns multiple results
-    return browser.bookmarks.search("allizom");
-  }).then(results => {
-    browser.test.assertEq(4, results.length, "Expected number of multiple results returned");
-    browser.test.assertEq("Mozilla", results[0].title, "Bookmark has the expected title");
-    browser.test.assertEq("Mozilla Corporation", results[1].title, "Bookmark has the expected title");
-    browser.test.assertEq("Firefox", results[2].title, "Bookmark has the expected title");
-    browser.test.assertEq("About Mozilla", results[3].title, "Bookmark has the expected title");
-
-    // accepts a url field
-    return browser.bookmarks.search({url: "http://allizom.com/"});
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for url field");
-    checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
+      return browser.bookmarks.update(nonExistentId, {title: "new test title"}).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("No bookmarks found for the provided GUID"),
+          "Expected error thrown when trying to update a non-existent bookmark"
+        );
 
-    // normalizes urls
-    return browser.bookmarks.search({url: "http://allizom.com"});
-  }).then(results => {
-    browser.test.assertEq(results.length, 1, "Expected number of results returned for normalized url field");
-    checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
-
-    // normalizes urls even more
-    return browser.bookmarks.search({url: "http:allizom.com"});
-  }).then(results => {
-    browser.test.assertEq(results.length, 1, "Expected number of results returned for normalized url field");
-    checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
+        return browser.bookmarks.update(ourId, {title: "new test title", url: "http://example.com/"});
+      });
+    }).then(result => {
+      browser.test.assertEq("new test title", result.title, "Updated bookmark has the expected title");
+      browser.test.assertEq("http://example.com/", result.url, "Updated bookmark has the expected URL");
+      browser.test.assertEq(ourId, result.id, "Updated bookmark has the expected id");
 
-    // accepts a title field
-    return browser.bookmarks.search({title: "Mozilla"});
-  }).then(results => {
-    browser.test.assertEq(results.length, 1, "Expected number of results returned for title field");
-    checkBookmark({title: "Mozilla", url: "http://allizom.org/", index: 3}, results[0]);
-
-    // can combine title and query
-    return browser.bookmarks.search({title: "Mozilla", query: "allizom"});
-  }).then(results => {
-    browser.test.assertEq(1, results.length, "Expected number of results returned for title and query fields");
-    checkBookmark({title: "Mozilla", url: "http://allizom.org/", index: 3}, results[0]);
+      browser.test.assertEq(2, collectedEvents.length, "2 expected events received");
+      checkOnChanged(ourId, "http://example.com/", "new test title");
 
-    // uses AND conditions
-    return browser.bookmarks.search({title: "EFF", query: "allizom"});
-  }).then(results => {
-    browser.test.assertEq(
-      0,
-      results.length,
-      "Expected number of results returned for non-matching title and query fields"
-    );
-
-    // returns an empty array on item not found
-    return browser.bookmarks.search("microsoft");
-  }).then(results => {
-    browser.test.assertEq(0, results.length, "Expected number of results returned for non-matching search");
-
-    return Promise.resolve().then(() => {
-      return browser.bookmarks.getRecent("");
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Incorrect argument types for bookmarks.getRecent"),
-        "Expected error thrown when calling getRecent with an empty string"
+      return Promise.resolve().then(() => {
+        return browser.bookmarks.update(ourId, {url: "this is not a valid url"});
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Invalid bookmark:"),
+          "Expected error thrown when trying update with an invalid url"
+        );
+        return browser.bookmarks.getTree();
+      });
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "getTree returns one result");
+      let bookmark = results[0].children.find(bookmarkItem => bookmarkItem.id == unsortedId);
+      browser.test.assertEq(
+          "Other Bookmarks",
+          bookmark.title,
+          "Folder returned from getTree has the expected title"
       );
-    });
-  }).then(() => {
-    return Promise.resolve().then(() => {
-      return browser.bookmarks.getRecent(1.234);
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("Incorrect argument types for bookmarks.getRecent"),
-        "Expected error thrown when calling getRecent with a decimal number"
-      );
-    });
-  }).then(() => {
-    return Promise.all([
-      browser.bookmarks.search("corporation"),
-      browser.bookmarks.getChildren(bookmarkGuids.menuGuid),
-    ]);
-  }).then(results => {
-    let corporationBookmark = results[0][0];
-    let childCount = results[1].length;
 
-    browser.test.assertEq(2, corporationBookmark.index, "Bookmark has the expected index");
-
-    return browser.bookmarks.move(corporationBookmark.id, {index: 0}).then(result => {
-      browser.test.assertEq(0, result.index, "Bookmark has the expected index");
+      return browser.bookmarks.create({parentId: "invalid"}).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Invalid bookmark"),
+          "Expected error thrown when trying to create a bookmark with an invalid parentId"
+        );
+        browser.test.assertTrue(
+            error.message.includes(`"parentGuid":"invalid"`),
+            "Expected error thrown when trying to create a bookmark with an invalid parentId"
+        );
+      });
+    }).then(() => {
+      return browser.bookmarks.remove(ourId);
+    }).then(result => {
+      browser.test.assertEq(undefined, result, "Removing a bookmark returns undefined");
 
       browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnMoved(corporationBookmark.id, createdFolderId, createdFolderId, 0, 2);
+      checkOnRemoved(ourId, bookmarkGuids.unfiledGuid, 0, "http://example.com/");
+
+      return browser.bookmarks.get(ourId).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Bookmark not found"),
+          "Expected error thrown when trying to get a removed bookmark"
+        );
+      });
+    }).then(() => {
+      return browser.bookmarks.remove(nonExistentId).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("No bookmarks found for the provided GUID"),
+          "Expected error thrown when trying removed a non-existent bookmark"
+        );
+      });
+    }).then(() => {
+      // test bookmarks.search
+      return Promise.all([
+        browser.bookmarks.create({title: "MØzillä", url: "http://møzîllä.örg/"}),
+        browser.bookmarks.create({title: "Example", url: "http://example.org/"}),
+        browser.bookmarks.create({title: "Mozilla Folder"}),
+        browser.bookmarks.create({title: "EFF", url: "http://eff.org/"}),
+        browser.bookmarks.create({title: "Menu Item", url: "http://menu.org/", parentId: bookmarkGuids.menuGuid}),
+        browser.bookmarks.create({title: "Toolbar Item", url: "http://toolbar.org/", parentId: bookmarkGuids.toolbarGuid}),
+      ]);
+    }).then(results => {
+      browser.test.assertEq(6, collectedEvents.length, "6 expected events received");
+      checkOnCreated(results[5].id, bookmarkGuids.toolbarGuid, 0, "Toolbar Item", "http://toolbar.org/", results[5].dateAdded);
+      checkOnCreated(results[4].id, bookmarkGuids.menuGuid, 0, "Menu Item", "http://menu.org/", results[4].dateAdded);
+      checkOnCreated(results[3].id, bookmarkGuids.unfiledGuid, 0, "EFF", "http://eff.org/", results[3].dateAdded);
+      checkOnCreated(results[2].id, bookmarkGuids.unfiledGuid, 0, "Mozilla Folder", undefined, results[2].dateAdded);
+      checkOnCreated(results[1].id, bookmarkGuids.unfiledGuid, 0, "Example", "http://example.org/", results[1].dateAdded);
+      checkOnCreated(results[0].id, bookmarkGuids.unfiledGuid, 0, "MØzillä", "http://møzîllä.örg/", results[0].dateAdded);
+
+      for (let result of results) {
+        if (result.title !== "Mozilla Folder") {
+          createdBookmarks.add(result.id);
+        }
+      }
+      let folderResult = results[2];
+      createdFolderId = folderResult.id;
+      return Promise.all([
+        browser.bookmarks.create({title: "Mozilla", url: "http://allizom.org/", parentId: createdFolderId}),
+        browser.bookmarks.create({title: "Mozilla Corporation", url: "http://allizom.com/", parentId: createdFolderId}),
+        browser.bookmarks.create({title: "Firefox", url: "http://allizom.org/firefox/", parentId: createdFolderId}),
+      ]).then(newBookmarks => {
+        browser.test.assertEq(3, collectedEvents.length, "3 expected events received");
+        checkOnCreated(newBookmarks[2].id, createdFolderId, 0, "Firefox", "http://allizom.org/firefox/", newBookmarks[2].dateAdded);
+        checkOnCreated(newBookmarks[1].id, createdFolderId, 0, "Mozilla Corporation", "http://allizom.com/", newBookmarks[1].dateAdded);
+        checkOnCreated(newBookmarks[0].id, createdFolderId, 0, "Mozilla", "http://allizom.org/", newBookmarks[0].dateAdded);
+
+        return browser.bookmarks.create({
+          title: "About Mozilla",
+          url: "http://allizom.org/about/",
+          parentId: createdFolderId,
+          index: 1,
+        });
+      }).then(result => {
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnCreated(result.id, createdFolderId, 1, "About Mozilla", "http://allizom.org/about/", result.dateAdded);
+
+        // returns all items on empty object
+        return browser.bookmarks.search({});
+      }).then(bookmarksSearchResults => {
+        browser.test.assertTrue(bookmarksSearchResults.length >= 9, "At least as many bookmarks as added were returned by search({})");
 
-      return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.menuGuid});
-    }).then(result => {
-      browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
-      browser.test.assertEq(childCount, result.index, "Bookmark has the expected index");
+        return Promise.resolve().then(() => {
+          return browser.bookmarks.remove(createdFolderId);
+        }).then(expectedError, error => {
+          browser.test.assertTrue(
+            error.message.includes("Cannot remove a non-empty folder"),
+            "Expected error thrown when trying to remove a non-empty folder"
+          );
+          return browser.bookmarks.getSubTree(createdFolderId);
+        });
+      });
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of nodes returned by getSubTree");
+      browser.test.assertEq("Mozilla Folder", results[0].title, "Folder has the expected title");
+      let children = results[0].children;
+      browser.test.assertEq(4, children.length, "Expected number of bookmarks returned by getSubTree");
+      browser.test.assertEq("Firefox", children[0].title, "Bookmark has the expected title");
+      browser.test.assertEq("About Mozilla", children[1].title, "Bookmark has the expected title");
+      browser.test.assertEq(1, children[1].index, "Bookmark has the expected index");
+      browser.test.assertEq("Mozilla Corporation", children[2].title, "Bookmark has the expected title");
+      browser.test.assertEq("Mozilla", children[3].title, "Bookmark has the expected title");
+
+      // throws an error for invalid query objects
+      Promise.resolve().then(() => {
+        return browser.bookmarks.search();
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Incorrect argument types for bookmarks.search"),
+          "Expected error thrown when trying to search with no arguments"
+        );
+      });
+
+      Promise.resolve().then(() => {
+        return browser.bookmarks.search(null);
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Incorrect argument types for bookmarks.search"),
+          "Expected error thrown when trying to search with null as an argument"
+        );
+      });
 
-      browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, createdFolderId, 1, 0);
+      Promise.resolve().then(() => {
+        return browser.bookmarks.search(function() {});
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Incorrect argument types for bookmarks.search"),
+          "Expected error thrown when trying to search with a function as an argument"
+        );
+      });
+
+      Promise.resolve().then(() => {
+        return browser.bookmarks.search({banana: "banana"});
+      }).then(expectedError, error => {
+        let substr = `an unexpected "banana" property`;
+        browser.test.assertTrue(
+          error.message.includes(substr),
+          `Expected error ${JSON.stringify(error.message)} to contain ${JSON.stringify(substr)}`);
+      });
+
+      Promise.resolve().then(() => {
+        return browser.bookmarks.search({url: "spider-man vs. batman"});
+      }).then(expectedError, error => {
+        let substr = 'must match the format "url"';
+        browser.test.assertTrue(
+          error.message.includes(substr),
+          `Expected error ${JSON.stringify(error.message)} to contain ${JSON.stringify(substr)}`);
+      });
+
+      // queries the full url
+      return browser.bookmarks.search("http://example.org/");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for url search");
+      checkBookmark({title: "Example", url: "http://example.org/", index: 2}, results[0]);
+
+      // queries a partial url
+      return browser.bookmarks.search("example.org");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for url search");
+      checkBookmark({title: "Example", url: "http://example.org/", index: 2}, results[0]);
+
+      // queries the title
+      return browser.bookmarks.search("EFF");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for title search");
+      checkBookmark({title: "EFF", url: "http://eff.org/", index: 0, parentId: bookmarkGuids.unfiledGuid}, results[0]);
+
+      // finds menu items
+      return browser.bookmarks.search("Menu Item");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for menu item search");
+      checkBookmark({title: "Menu Item", url: "http://menu.org/", index: 0, parentId: bookmarkGuids.menuGuid}, results[0]);
+
+      // finds toolbar items
+      return browser.bookmarks.search("Toolbar Item");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for toolbar item search");
+      checkBookmark({title: "Toolbar Item", url: "http://toolbar.org/", index: 0, parentId: bookmarkGuids.toolbarGuid}, results[0]);
 
-      return browser.bookmarks.move(corporationBookmark.id, {index: 0});
-    }).then(result => {
-      browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
-      browser.test.assertEq(0, result.index, "Bookmark has the expected index");
+      // finds folders
+      return browser.bookmarks.search("Mozilla Folder");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of folders returned");
+      browser.test.assertEq("Mozilla Folder", results[0].title, "Folder has the expected title");
+
+      // is case-insensitive
+      return browser.bookmarks.search("corporation");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returnedfor case-insensitive search");
+      browser.test.assertEq("Mozilla Corporation", results[0].title, "Bookmark has the expected title");
+
+      // is case-insensitive for non-ascii
+      return browser.bookmarks.search("MøZILLÄ");
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for non-ascii search");
+      browser.test.assertEq("MØzillä", results[0].title, "Bookmark has the expected title");
+
+      // returns multiple results
+      return browser.bookmarks.search("allizom");
+    }).then(results => {
+      browser.test.assertEq(4, results.length, "Expected number of multiple results returned");
+      browser.test.assertEq("Mozilla", results[0].title, "Bookmark has the expected title");
+      browser.test.assertEq("Mozilla Corporation", results[1].title, "Bookmark has the expected title");
+      browser.test.assertEq("Firefox", results[2].title, "Bookmark has the expected title");
+      browser.test.assertEq("About Mozilla", results[3].title, "Bookmark has the expected title");
+
+      // accepts a url field
+      return browser.bookmarks.search({url: "http://allizom.com/"});
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for url field");
+      checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
+
+      // normalizes urls
+      return browser.bookmarks.search({url: "http://allizom.com"});
+    }).then(results => {
+      browser.test.assertEq(results.length, 1, "Expected number of results returned for normalized url field");
+      checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
+
+      // normalizes urls even more
+      return browser.bookmarks.search({url: "http:allizom.com"});
+    }).then(results => {
+      browser.test.assertEq(results.length, 1, "Expected number of results returned for normalized url field");
+      checkBookmark({title: "Mozilla Corporation", url: "http://allizom.com/", index: 2}, results[0]);
+
+      // accepts a title field
+      return browser.bookmarks.search({title: "Mozilla"});
+    }).then(results => {
+      browser.test.assertEq(results.length, 1, "Expected number of results returned for title field");
+      checkBookmark({title: "Mozilla", url: "http://allizom.org/", index: 3}, results[0]);
+
+      // can combine title and query
+      return browser.bookmarks.search({title: "Mozilla", query: "allizom"});
+    }).then(results => {
+      browser.test.assertEq(1, results.length, "Expected number of results returned for title and query fields");
+      checkBookmark({title: "Mozilla", url: "http://allizom.org/", index: 3}, results[0]);
+
+      // uses AND conditions
+      return browser.bookmarks.search({title: "EFF", query: "allizom"});
+    }).then(results => {
+      browser.test.assertEq(
+        0,
+        results.length,
+        "Expected number of results returned for non-matching title and query fields"
+      );
+
+      // returns an empty array on item not found
+      return browser.bookmarks.search("microsoft");
+    }).then(results => {
+      browser.test.assertEq(0, results.length, "Expected number of results returned for non-matching search");
 
-      browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, bookmarkGuids.menuGuid, 0, 1);
+      return Promise.resolve().then(() => {
+        return browser.bookmarks.getRecent("");
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Incorrect argument types for bookmarks.getRecent"),
+          "Expected error thrown when calling getRecent with an empty string"
+        );
+      });
+    }).then(() => {
+      return Promise.resolve().then(() => {
+        return browser.bookmarks.getRecent(1.234);
+      }).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("Incorrect argument types for bookmarks.getRecent"),
+          "Expected error thrown when calling getRecent with a decimal number"
+        );
+      });
+    }).then(() => {
+      return Promise.all([
+        browser.bookmarks.search("corporation"),
+        browser.bookmarks.getChildren(bookmarkGuids.menuGuid),
+      ]);
+    }).then(results => {
+      let corporationBookmark = results[0][0];
+      let childCount = results[1].length;
+
+      browser.test.assertEq(2, corporationBookmark.index, "Bookmark has the expected index");
+
+      return browser.bookmarks.move(corporationBookmark.id, {index: 0}).then(result => {
+        browser.test.assertEq(0, result.index, "Bookmark has the expected index");
+
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnMoved(corporationBookmark.id, createdFolderId, createdFolderId, 0, 2);
+
+        return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.menuGuid});
+      }).then(result => {
+        browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
+        browser.test.assertEq(childCount, result.index, "Bookmark has the expected index");
+
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, createdFolderId, 1, 0);
+
+        return browser.bookmarks.move(corporationBookmark.id, {index: 0});
+      }).then(result => {
+        browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
+        browser.test.assertEq(0, result.index, "Bookmark has the expected index");
 
-      return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.toolbarGuid, index: 1});
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, bookmarkGuids.menuGuid, 0, 1);
+
+        return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.toolbarGuid, index: 1});
+      }).then(result => {
+        browser.test.assertEq(bookmarkGuids.toolbarGuid, result.parentId, "Bookmark has the expected parent");
+        browser.test.assertEq(1, result.index, "Bookmark has the expected index");
+
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnMoved(corporationBookmark.id, bookmarkGuids.toolbarGuid, bookmarkGuids.menuGuid, 1, 0);
+
+        createdBookmarks.add(corporationBookmark.id);
+      });
+    }).then(() => {
+      return browser.bookmarks.getRecent(4);
+    }).then(results => {
+      browser.test.assertEq(4, results.length, "Expected number of results returned by getRecent");
+      let prevDate = results[0].dateAdded;
+      for (let bookmark of results) {
+        browser.test.assertTrue(bookmark.dateAdded <= prevDate, "The recent bookmarks are sorted by dateAdded");
+        prevDate = bookmark.dateAdded;
+      }
+      let bookmarksByTitle = results.sort((a, b) => {
+        return a.title.localeCompare(b.title);
+      });
+      browser.test.assertEq("About Mozilla", bookmarksByTitle[0].title, "Bookmark has the expected title");
+      browser.test.assertEq("Firefox", bookmarksByTitle[1].title, "Bookmark has the expected title");
+      browser.test.assertEq("Mozilla", bookmarksByTitle[2].title, "Bookmark has the expected title");
+      browser.test.assertEq("Mozilla Corporation", bookmarksByTitle[3].title, "Bookmark has the expected title");
+
+      return browser.bookmarks.search({});
+    }).then(results => {
+      let startBookmarkCount = results.length;
+
+      return browser.bookmarks.search({title: "Mozilla Folder"}).then(result => {
+        return browser.bookmarks.removeTree(result[0].id);
+      }).then(() => {
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnRemoved(createdFolderId, bookmarkGuids.unfiledGuid, 1);
+
+        return browser.bookmarks.search({}).then(searchResults => {
+          browser.test.assertEq(
+            startBookmarkCount - 4,
+            searchResults.length,
+            "Expected number of results returned after removeTree");
+        });
+      });
+    }).then(() => {
+      return browser.bookmarks.create({title: "Empty Folder"});
     }).then(result => {
-      browser.test.assertEq(bookmarkGuids.toolbarGuid, result.parentId, "Bookmark has the expected parent");
-      browser.test.assertEq(1, result.index, "Bookmark has the expected index");
+      let emptyFolderId = result.id;
 
       browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnMoved(corporationBookmark.id, bookmarkGuids.toolbarGuid, bookmarkGuids.menuGuid, 1, 0);
+      checkOnCreated(emptyFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, result.dateAdded);
+
+      browser.test.assertEq("Empty Folder", result.title, "Folder has the expected title");
+      return browser.bookmarks.remove(emptyFolderId).then(() => {
+        browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
+        checkOnRemoved(emptyFolderId, bookmarkGuids.unfiledGuid, 3);
 
-      createdBookmarks.add(corporationBookmark.id);
-    });
-  }).then(() => {
-    return browser.bookmarks.getRecent(4);
-  }).then(results => {
-    browser.test.assertEq(4, results.length, "Expected number of results returned by getRecent");
-    let prevDate = results[0].dateAdded;
-    for (let bookmark of results) {
-      browser.test.assertTrue(bookmark.dateAdded <= prevDate, "The recent bookmarks are sorted by dateAdded");
-      prevDate = bookmark.dateAdded;
-    }
-    let bookmarksByTitle = results.sort((a, b) => {
-      return a.title.localeCompare(b.title);
-    });
-    browser.test.assertEq("About Mozilla", bookmarksByTitle[0].title, "Bookmark has the expected title");
-    browser.test.assertEq("Firefox", bookmarksByTitle[1].title, "Bookmark has the expected title");
-    browser.test.assertEq("Mozilla", bookmarksByTitle[2].title, "Bookmark has the expected title");
-    browser.test.assertEq("Mozilla Corporation", bookmarksByTitle[3].title, "Bookmark has the expected title");
-
-    return browser.bookmarks.search({});
-  }).then(results => {
-    let startBookmarkCount = results.length;
-
-    return browser.bookmarks.search({title: "Mozilla Folder"}).then(result => {
-      return browser.bookmarks.removeTree(result[0].id);
+        return browser.bookmarks.get(emptyFolderId).then(expectedError, error => {
+          browser.test.assertTrue(
+            error.message.includes("Bookmark not found"),
+            "Expected error thrown when trying to get a removed folder"
+          );
+        });
+      });
     }).then(() => {
-      browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnRemoved(createdFolderId, bookmarkGuids.unfiledGuid, 1);
-
-      return browser.bookmarks.search({}).then(searchResults => {
-        browser.test.assertEq(
-          startBookmarkCount - 4,
-          searchResults.length,
-          "Expected number of results returned after removeTree");
+      return browser.bookmarks.getChildren(nonExistentId).then(expectedError, error => {
+        browser.test.assertTrue(
+          error.message.includes("root is null"),
+          "Expected error thrown when trying to getChildren for a non-existent folder"
+        );
       });
-    });
-  }).then(() => {
-    return browser.bookmarks.create({title: "Empty Folder"});
-  }).then(result => {
-    let emptyFolderId = result.id;
-
-    browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-    checkOnCreated(emptyFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, result.dateAdded);
-
-    browser.test.assertEq("Empty Folder", result.title, "Folder has the expected title");
-    return browser.bookmarks.remove(emptyFolderId).then(() => {
-      browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
-      checkOnRemoved(emptyFolderId, bookmarkGuids.unfiledGuid, 3);
-
-      return browser.bookmarks.get(emptyFolderId).then(expectedError, error => {
+    }).then(() => {
+      return Promise.resolve().then(() => {
+        return browser.bookmarks.move(nonExistentId, {});
+      }).then(expectedError, error => {
         browser.test.assertTrue(
-          error.message.includes("Bookmark not found"),
-          "Expected error thrown when trying to get a removed folder"
+          error.message.includes("No bookmarks found for the provided GUID"),
+          "Expected error thrown when calling move with a non-existent bookmark"
         );
       });
-    });
-  }).then(() => {
-    return browser.bookmarks.getChildren(nonExistentId).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("root is null"),
-        "Expected error thrown when trying to getChildren for a non-existent folder"
-      );
+    }).then(() => {
+      // remove all created bookmarks
+      let promises = Array.from(createdBookmarks, guid => browser.bookmarks.remove(guid));
+      return Promise.all(promises);
+    }).then(() => {
+      browser.test.assertEq(createdBookmarks.size, collectedEvents.length, "expected number of events received");
+
+      return browser.bookmarks.search({});
+    }).then(results => {
+      browser.test.assertEq(initialBookmarkCount, results.length, "All created bookmarks have been removed");
+
+      return browser.test.notifyPass("bookmarks");
+    }).catch(error => {
+      browser.test.fail(`Error: ${String(error)} :: ${error.stack}`);
+      browser.test.notifyFail("bookmarks");
     });
-  }).then(() => {
-    return Promise.resolve().then(() => {
-      return browser.bookmarks.move(nonExistentId, {});
-    }).then(expectedError, error => {
-      browser.test.assertTrue(
-        error.message.includes("No bookmarks found for the provided GUID"),
-        "Expected error thrown when calling move with a non-existent bookmark"
-      );
+  }
+
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      permissions: ["bookmarks"],
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitFinish("bookmarks");
+  await extension.unload();
+});
+
+add_task(async function test_get_recent_with_tag_and_query() {
+  function background() {
+    browser.bookmarks.getRecent(100).then(bookmarks => {
+      browser.test.sendMessage("bookmarks", bookmarks);
     });
-  }).then(() => {
-    // remove all created bookmarks
-    let promises = Array.from(createdBookmarks, guid => browser.bookmarks.remove(guid));
-    return Promise.all(promises);
-  }).then(() => {
-    browser.test.assertEq(createdBookmarks.size, collectedEvents.length, "expected number of events received");
+  }
 
-    return browser.bookmarks.search({});
-  }).then(results => {
-    browser.test.assertEq(initialBookmarkCount, results.length, "All created bookmarks have been removed");
+  let extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      permissions: ["bookmarks"],
+    },
+  });
+
+  // Start with an empty bookmarks database.
+  await PlacesUtils.bookmarks.eraseEverything();
 
-    return browser.test.notifyPass("bookmarks");
-  }).catch(error => {
-    browser.test.fail(`Error: ${String(error)} :: ${error.stack}`);
-    browser.test.notifyFail("bookmarks");
-  });
-}
+  let createdBookmarks = [];
+  for (let i = 0; i < 3; i++) {
+    let bookmark = {
+      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
+      url: `http://example.com/${i}`,
+      title: `My bookmark ${i}`,
+      parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    };
+    createdBookmarks.unshift(bookmark);
+    await PlacesUtils.bookmarks.insert(bookmark);
+  }
 
-let extensionData = {
-  background: `(${backgroundScript})()`,
-  manifest: {
-    permissions: ["bookmarks"],
-  },
-};
+  // Add a tag to the most recent url to prove it doesn't get returned.
+  PlacesUtils.tagging.tagURI(NetUtil.newURI("http://example.com/${i}"), ["Test Tag"]);
+
+  // Add a query bookmark.
+  let queryURL = `place:folder=${PlacesUtils.bookmarksMenuFolderId}&queryType=1`;
+  await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    url: queryURL,
+    title: "a test query"});
 
-add_task(function* test_contentscript() {
-  let extension = ExtensionTestUtils.loadExtension(extensionData);
-  yield extension.startup();
-  yield extension.awaitFinish("bookmarks");
-  yield extension.unload();
+  await extension.startup();
+  let receivedBookmarks = await extension.awaitMessage("bookmarks");
+
+  equal(receivedBookmarks.length, 3, "The expected number of bookmarks was returned.");
+  for (let i = 0; i < 3; i++) {
+    let actual = receivedBookmarks[i];
+    let expected = createdBookmarks[i];
+    equal(actual.url, expected.url, "Bookmark has the expected url.");
+    equal(actual.title, expected.title, "Bookmark has the expected title.");
+    equal(actual.parentId, expected.parentGuid, "Bookmark has the expected parentId.");
+  }
+
+  await extension.unload();
 });
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -495,16 +495,17 @@ var Bookmarks = Object.freeze({
             `, { folderGuid, time, syncChangeDelta });
         }
       }.bind(this))
     );
   },
 
   /**
    * Returns a list of recently bookmarked items.
+   * Only includes actual bookmarks. Excludes folders, separators and queries.
    *
    * @param {integer} numberOfItems
    *        The maximum number of bookmark items to return.
    *
    * @return {Promise} resolved when the listing is complete.
    * @resolves to an array of recent bookmark-items.
    * @rejects if an error happens while querying.
    */
@@ -1191,19 +1192,26 @@ function fetchRecentBookmarks(numberOfIt
       `SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
               b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
               NULL AS _id, NULL AS _parentId, NULL AS _childCount, NULL AS _grandParentId,
               NULL AS _syncStatus
        FROM moz_bookmarks b
        LEFT JOIN moz_bookmarks p ON p.id = b.parent
        LEFT JOIN moz_places h ON h.id = b.fk
        WHERE p.parent <> :tags_folder
+       AND b.type = :type
+       AND url_hash NOT BETWEEN hash("place", "prefix_lo")
+                            AND hash("place", "prefix_hi")
        ORDER BY b.dateAdded DESC, b.ROWID DESC
        LIMIT :numberOfItems
-      `, { tags_folder: PlacesUtils.tagsFolderId, numberOfItems });
+      `, {
+        tags_folder: PlacesUtils.tagsFolderId,
+        type: Bookmarks.TYPE_BOOKMARK,
+        numberOfItems,
+      });
 
     return rows.length ? rowsToItemsArray(rows) : [];
   }));
 }
 
 function fetchBookmarksByParent(info) {
   return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: fetchBookmarksByParent",
     Task.async(function*(db) {
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_getRecent.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_getRecent.js
@@ -26,19 +26,42 @@ add_task(function* getRecent_returns_rec
   let bm4 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
                                                  url: "http://example.net/path",
                                                  title: "yet another bookmark" });
   checkBookmarkObject(bm1);
   checkBookmarkObject(bm2);
   checkBookmarkObject(bm3);
   checkBookmarkObject(bm4);
 
-  let results = yield PlacesUtils.bookmarks.getRecent(3);
-  Assert.equal(results.length, 3);
+  // Add a tag to the most recent url to prove it doesn't get returned.
+  PlacesUtils.tagging.tagURI(uri(bm4.url), ["Test Tag"]);
+
+  // Add a separator.
+  PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId,
+                                        PlacesUtils.bookmarks.DEFAULT_INDEX);
+
+  // Add a query bookmark.
+  let queryURL = `place:folder=${PlacesUtils.bookmarksMenuFolderId}&queryType=1`;
+  let bm5 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+                                                 url: queryURL,
+                                                 title: "a test query" });
+  checkBookmarkObject(bm5);
+
+  // Verify that getRecent only returns actual bookmarks.
+  let results = yield PlacesUtils.bookmarks.getRecent(100);
+  Assert.equal(results.length, 4, "The expected number of bookmarks was returned.");
   checkBookmarkObject(results[0]);
-  Assert.deepEqual(bm4, results[0]);
+  Assert.deepEqual(bm4, results[0], "The first result is the expected bookmark.");
   checkBookmarkObject(results[1]);
-  Assert.deepEqual(bm3, results[1]);
+  Assert.deepEqual(bm3, results[1], "The second result is the expected bookmark.");
   checkBookmarkObject(results[2]);
-  Assert.deepEqual(bm2, results[2]);
+  Assert.deepEqual(bm2, results[2], "The third result is the expected bookmark.");
+  checkBookmarkObject(results[3]);
+  Assert.deepEqual(bm1, results[3], "The fourth result is the expected bookmark.");
+
+  // Verify that getRecent utilizes the numberOfItems argument.
+  results = yield PlacesUtils.bookmarks.getRecent(1);
+  Assert.equal(results.length, 1, "The expected number of bookmarks was returned.");
+  checkBookmarkObject(results[0]);
+  Assert.deepEqual(bm4, results[0], "The first result is the expected bookmark.");
 
   yield PlacesUtils.bookmarks.eraseEverything();
 });