summaryrefslogtreecommitdiffstats
path: root/browser/components/places/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/places/tests/unit')
-rw-r--r--browser/components/places/tests/unit/.eslintrc.js7
-rw-r--r--browser/components/places/tests/unit/bookmarks.glue.html16
-rw-r--r--browser/components/places/tests/unit/bookmarks.glue.json1
-rw-r--r--browser/components/places/tests/unit/corruptDB.sqlitebin0 -> 32772 bytes
-rw-r--r--browser/components/places/tests/unit/distribution.ini27
-rw-r--r--browser/components/places/tests/unit/head_bookmarks.js133
-rw-r--r--browser/components/places/tests/unit/test_421483.js103
-rw-r--r--browser/components/places/tests/unit/test_PUIU_makeTransaction.js361
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js33
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt.js59
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js52
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js55
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_distribution.js125
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_migrate.js70
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_prefs.js240
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_restore.js62
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js285
-rw-r--r--browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js150
-rw-r--r--browser/components/places/tests/unit/test_clearHistory_shutdown.js181
-rw-r--r--browser/components/places/tests/unit/test_leftpane_corruption_handling.js174
-rw-r--r--browser/components/places/tests/unit/xpcshell.ini25
21 files changed, 2159 insertions, 0 deletions
diff --git a/browser/components/places/tests/unit/.eslintrc.js b/browser/components/places/tests/unit/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/browser/components/places/tests/unit/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/browser/components/places/tests/unit/bookmarks.glue.html b/browser/components/places/tests/unit/bookmarks.glue.html
new file mode 100644
index 000000000..07b22e9b3
--- /dev/null
+++ b/browser/components/places/tests/unit/bookmarks.glue.html
@@ -0,0 +1,16 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+ It will be read and overwritten.
+ DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks Menu</H1>
+
+<DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ <DT><H3 ADD_DATE="1233157910" LAST_MODIFIED="1233157972" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+ <DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ </DL><p>
+</DL><p>
diff --git a/browser/components/places/tests/unit/bookmarks.glue.json b/browser/components/places/tests/unit/bookmarks.glue.json
new file mode 100644
index 000000000..95900e176
--- /dev/null
+++ b/browser/components/places/tests/unit/bookmarks.glue.json
@@ -0,0 +1 @@
+{"title":"","id":1,"dateAdded":1233157910552624,"lastModified":1233157955206833,"type":"text/x-moz-place-container","root":"placesRoot","children":[{"title":"Bookmarks Menu","id":2,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157993171424,"type":"text/x-moz-place-container","root":"bookmarksMenuFolder","children":[{"title":"examplejson","id":27,"parent":2,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":1,"title":"Bookmarks Toolbar","id":3,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157972101126,"annos":[{"name":"bookmarkProperties/description","flags":0,"expires":4,"mimeType":null,"type":3,"value":"Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar"}],"type":"text/x-moz-place-container","root":"toolbarFolder","children":[{"title":"examplejson","id":26,"parent":3,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":2,"title":"Tags","id":4,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157910582667,"type":"text/x-moz-place-container","root":"tagsFolder","children":[]},{"index":3,"title":"Other Bookmarks","id":5,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157911033315,"type":"text/x-moz-place-container","root":"unfiledBookmarksFolder","children":[]}]}
diff --git a/browser/components/places/tests/unit/corruptDB.sqlite b/browser/components/places/tests/unit/corruptDB.sqlite
new file mode 100644
index 000000000..b234246ca
--- /dev/null
+++ b/browser/components/places/tests/unit/corruptDB.sqlite
Binary files differ
diff --git a/browser/components/places/tests/unit/distribution.ini b/browser/components/places/tests/unit/distribution.ini
new file mode 100644
index 000000000..93e73cb5c
--- /dev/null
+++ b/browser/components/places/tests/unit/distribution.ini
@@ -0,0 +1,27 @@
+# Distribution Configuration File
+# Bug 516444 demo
+
+[Global]
+id=516444
+version=1.0
+about=Test distribution file
+
+[BookmarksToolbar]
+item.1.title=Toolbar Link Before
+item.1.link=https://example.org/toolbar/before/
+item.1.keyword=e:t:b
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
+item.2.type=default
+item.3.title=Toolbar Link After
+item.3.link=https://example.org/toolbar/after/
+item.3.keyword=e:t:a
+
+[BookmarksMenu]
+item.1.title=Menu Link Before
+item.1.link=https://example.org/menu/before/
+item.1.icon=https://example.org/favicon.png
+item.1.iconData=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==
+item.2.type=default
+item.3.title=Menu Link After
+item.3.link=https://example.org/menu/after/
diff --git a/browser/components/places/tests/unit/head_bookmarks.js b/browser/components/places/tests/unit/head_bookmarks.js
new file mode 100644
index 000000000..460295f96
--- /dev/null
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -0,0 +1,133 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/LoadContextInfo.jsm");
+
+// Import common head.
+var commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false);
+if (commonFile) {
+ let uri = Services.io.newFileURI(commonFile);
+ Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
+ Cu.import("resource:///modules/PlacesUIUtils.jsm");
+ return PlacesUIUtils;
+});
+
+const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
+const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
+
+// Needed by some test that relies on having an app registered.
+Cu.import("resource://testing-common/AppInfo.jsm", this);
+updateAppInfo({
+ name: "PlacesTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
+ version: "1",
+ platformVersion: "",
+});
+
+// Smart bookmarks constants.
+const SMART_BOOKMARKS_VERSION = 8;
+const SMART_BOOKMARKS_ON_TOOLBAR = 1;
+const SMART_BOOKMARKS_ON_MENU = 2; // Takes into account the additional separator.
+
+// Default bookmarks constants.
+const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
+const DEFAULT_BOOKMARKS_ON_MENU = 1;
+
+const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
+
+function checkItemHasAnnotation(guid, name) {
+ return PlacesUtils.promiseItemId(guid).then(id => {
+ let hasAnnotation = PlacesUtils.annotations.itemHasAnnotation(id, name);
+ Assert.ok(hasAnnotation, `Expected annotation ${name}`);
+ });
+}
+
+var createCorruptDB = Task.async(function* () {
+ let dbPath = OS.Path.join(OS.Constants.Path.profileDir, "places.sqlite");
+ yield OS.File.remove(dbPath);
+
+ // Create a corrupt database.
+ let dir = yield OS.File.getCurrentDirectory();
+ let src = OS.Path.join(dir, "corruptDB.sqlite");
+ yield OS.File.copy(src, dbPath);
+
+ // Check there's a DB now.
+ Assert.ok((yield OS.File.exists(dbPath)), "should have a DB now");
+});
+
+/**
+ * Rebuilds smart bookmarks listening to console output to report any message or
+ * exception generated.
+ *
+ * @return {Promise}
+ * Resolved when done.
+ */
+function rebuildSmartBookmarks() {
+ let consoleListener = {
+ observe(aMsg) {
+ if (aMsg.message.startsWith("[JavaScript Warning:")) {
+ // TODO (Bug 1300416): Ignore spurious strict warnings.
+ return;
+ }
+ do_throw("Got console message: " + aMsg.message);
+ },
+ QueryInterface: XPCOMUtils.generateQI([ Ci.nsIConsoleListener ]),
+ };
+ Services.console.reset();
+ Services.console.registerListener(consoleListener);
+ do_register_cleanup(() => {
+ try {
+ Services.console.unregisterListener(consoleListener);
+ } catch (ex) { /* will likely fail */ }
+ });
+ Cc["@mozilla.org/browser/browserglue;1"]
+ .getService(Ci.nsIObserver)
+ .observe(null, "browser-glue-test", "smart-bookmarks-init");
+ return promiseTopicObserved("test-smart-bookmarks-done").then(() => {
+ Services.console.unregisterListener(consoleListener);
+ });
+}
+
+const SINGLE_TRY_TIMEOUT = 100;
+const NUMBER_OF_TRIES = 30;
+
+/**
+ * Similar to waitForConditionPromise, but poll for an asynchronous value
+ * every SINGLE_TRY_TIMEOUT ms, for no more than tryCount times.
+ *
+ * @param promiseFn
+ * A function to generate a promise, which resolves to the expected
+ * asynchronous value.
+ * @param timeoutMsg
+ * The reason to reject the returned promise with.
+ * @param [optional] tryCount
+ * Maximum times to try before rejecting the returned promise with
+ * timeoutMsg, defaults to NUMBER_OF_TRIES.
+ * @return {Promise}
+ * @resolves to the asynchronous value being polled.
+ * @rejects if the asynchronous value is not available after tryCount attempts.
+ */
+var waitForResolvedPromise = Task.async(function* (promiseFn, timeoutMsg, tryCount=NUMBER_OF_TRIES) {
+ let tries = 0;
+ do {
+ try {
+ let value = yield promiseFn();
+ return value;
+ } catch (ex) {}
+ yield new Promise(resolve => do_timeout(SINGLE_TRY_TIMEOUT, resolve));
+ } while (++tries <= tryCount);
+ throw new Error(timeoutMsg);
+});
diff --git a/browser/components/places/tests/unit/test_421483.js b/browser/components/places/tests/unit/test_421483.js
new file mode 100644
index 000000000..a0d138372
--- /dev/null
+++ b/browser/components/places/tests/unit/test_421483.js
@@ -0,0 +1,103 @@
+/* -*- 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/. */
+
+
+const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
+
+var gluesvc = Cc["@mozilla.org/browser/browserglue;1"].
+ getService(Ci.nsIObserver);
+// Avoid default bookmarks import.
+gluesvc.observe(null, "initial-migration-will-import-default-bookmarks", "");
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* smart_bookmarks_disabled() {
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
+ yield rebuildSmartBookmarks();
+
+ let smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, 0);
+
+ do_print("check that pref has not been bumped up");
+ Assert.equal(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
+});
+
+add_task(function* create_smart_bookmarks() {
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+ yield rebuildSmartBookmarks();
+
+ let smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.notEqual(smartBookmarkItemIds.length, 0);
+
+ do_print("check that pref has been bumped up");
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+});
+
+add_task(function* remove_smart_bookmark_and_restore() {
+ let smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ let smartBookmarksCount = smartBookmarkItemIds.length;
+ do_print("remove one smart bookmark and restore");
+
+ let guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
+ yield PlacesUtils.bookmarks.remove(guid);
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+
+ yield rebuildSmartBookmarks();
+ smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
+
+ do_print("check that pref has been bumped up");
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+});
+
+add_task(function* move_smart_bookmark_rename_and_restore() {
+ let smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ let smartBookmarksCount = smartBookmarkItemIds.length;
+ do_print("smart bookmark should be restored in place");
+
+ let guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
+ let bm = yield PlacesUtils.bookmarks.fetch(guid);
+ let oldTitle = bm.title;
+
+ // create a subfolder and move inside it
+ let subfolder = yield PlacesUtils.bookmarks.insert({
+ parentGuid: bm.parentGuid,
+ title: "test",
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER
+ });
+
+ // change title and move into new subfolder
+ yield PlacesUtils.bookmarks.update({
+ guid: guid,
+ parentGuid: subfolder.guid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ title: "new title"
+ });
+
+ // restore
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+ yield rebuildSmartBookmarks();
+
+ smartBookmarkItemIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
+
+ guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
+ bm = yield PlacesUtils.bookmarks.fetch(guid);
+ Assert.equal(bm.parentGuid, subfolder.guid);
+ Assert.equal(bm.title, oldTitle);
+
+ do_print("check that pref has been bumped up");
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+});
diff --git a/browser/components/places/tests/unit/test_PUIU_makeTransaction.js b/browser/components/places/tests/unit/test_PUIU_makeTransaction.js
new file mode 100644
index 000000000..c0626f53b
--- /dev/null
+++ b/browser/components/places/tests/unit/test_PUIU_makeTransaction.js
@@ -0,0 +1,361 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function waitForBookmarkNotification(aNotification, aCallback, aProperty)
+{
+ PlacesUtils.bookmarks.addObserver({
+ validate: function (aMethodName, aData)
+ {
+ if (aMethodName == aNotification &&
+ (!aProperty || aProperty == aData.property)) {
+ PlacesUtils.bookmarks.removeObserver(this);
+ aCallback(aData);
+ }
+ },
+
+ // nsINavBookmarkObserver
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]),
+ onBeginUpdateBatch: function onBeginUpdateBatch() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onEndUpdateBatch: function onEndUpdateBatch() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType,
+ aURI, aTitle)
+ {
+ return this.validate(arguments.callee.name, { id: aItemId,
+ index: aIndex,
+ type: aItemType,
+ url: aURI ? aURI.spec : null,
+ title: aTitle });
+ },
+ onItemRemoved: function onItemRemoved() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemChanged: function onItemChanged(id, property, aIsAnno,
+ aNewValue, aLastModified, type)
+ {
+ return this.validate(arguments.callee.name,
+ { id,
+ get index() {
+ return PlacesUtils.bookmarks.getItemIndex(this.id);
+ },
+ type,
+ property,
+ get url() {
+ return type == PlacesUtils.bookmarks.TYPE_BOOKMARK ?
+ PlacesUtils.bookmarks.getBookmarkURI(this.id).spec :
+ null;
+ },
+ get title() {
+ return PlacesUtils.bookmarks.getItemTitle(this.id);
+ },
+ });
+ },
+ onItemVisited: function onItemVisited() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemMoved: function onItemMoved(aItemId, aOldParentId, aOldIndex,
+ aNewParentId, aNewIndex, aItemType)
+ {
+ this.validate(arguments.callee.name, { id: aItemId,
+ index: aNewIndex,
+ type: aItemType });
+ }
+ }, false);
+}
+
+function wrapNodeByIdAndParent(aItemId, aParentId)
+{
+ let wrappedNode;
+ let root = PlacesUtils.getFolderContents(aParentId, false, false).root;
+ for (let i = 0; i < root.childCount; ++i) {
+ let node = root.getChild(i);
+ if (node.itemId == aItemId) {
+ let type;
+ if (PlacesUtils.nodeIsContainer(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
+ }
+ else if (PlacesUtils.nodeIsURI(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE;
+ }
+ else if (PlacesUtils.nodeIsSeparator(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR;
+ }
+ else {
+ do_throw("Unknown node type");
+ }
+ wrappedNode = PlacesUtils.wrapNode(node, type);
+ }
+ }
+ root.containerOpen = false;
+ return JSON.parse(wrappedNode);
+}
+
+add_test(function test_text_paste()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ do_check_eq(aData.title, TEST_TITLE);
+ do_check_eq(aData.url, TEST_URL);
+ do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aData.index, 0);
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ { title: TEST_TITLE, uri: TEST_URL },
+ PlacesUtils.TYPE_X_MOZ_URL,
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true // Unused for text.
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+});
+
+add_test(function test_container()
+{
+ const TEST_TITLE = "Places folder"
+
+ waitForBookmarkNotification("onItemChanged", function(aChangedData)
+ {
+ do_check_eq(aChangedData.title, TEST_TITLE);
+ do_check_eq(aChangedData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ do_check_eq(aChangedData.index, 1);
+
+ waitForBookmarkNotification("onItemAdded", function(aAddedData)
+ {
+ do_check_eq(aAddedData.title, TEST_TITLE);
+ do_check_eq(aAddedData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ do_check_eq(aAddedData.index, 2);
+ let id = aAddedData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aMovedData)
+ {
+ do_check_eq(aMovedData.id, id);
+ do_check_eq(aMovedData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ do_check_eq(aMovedData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aAddedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aChangedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch (ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
+ TEST_TITLE,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO,
+ "description", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+
+add_test(function test_separator()
+{
+ waitForBookmarkNotification("onItemChanged", function(aChangedData)
+ {
+ do_check_eq(aChangedData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ do_check_eq(aChangedData.index, 3);
+
+ waitForBookmarkNotification("onItemAdded", function(aAddedData)
+ {
+ do_check_eq(aAddedData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ do_check_eq(aAddedData.index, 4);
+ let id = aAddedData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aMovedData)
+ {
+ do_check_eq(aMovedData.id, id);
+ do_check_eq(aMovedData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ do_check_eq(aMovedData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aAddedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aChangedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch (ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+add_test(function test_bookmark()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemChanged", function(aChangedData)
+ {
+ do_check_eq(aChangedData.title, TEST_TITLE);
+ do_check_eq(aChangedData.url, TEST_URL);
+ do_check_eq(aChangedData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aChangedData.index, 5);
+
+ waitForBookmarkNotification("onItemAdded", function(aAddedData)
+ {
+ do_check_eq(aAddedData.title, TEST_TITLE);
+ do_check_eq(aAddedData.url, TEST_URL);
+ do_check_eq(aAddedData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aAddedData.index, 6);
+ let id = aAddedData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aMovedData)
+ {
+ do_check_eq(aMovedData.id, id);
+ do_check_eq(aMovedData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aMovedData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aAddedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aChangedData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch (ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI(TEST_URL),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ TEST_TITLE);
+ PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO,
+ "description", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+add_test(function test_visit()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemAdded", function(aAddedData)
+ {
+ do_check_eq(aAddedData.title, TEST_TITLE);
+ do_check_eq(aAddedData.url, TEST_URL);
+ do_check_eq(aAddedData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aAddedData.index, 7);
+
+ waitForBookmarkNotification("onItemAdded", function(aAddedData2)
+ {
+ do_check_eq(aAddedData2.title, TEST_TITLE);
+ do_check_eq(aAddedData2.url, TEST_URL);
+ do_check_eq(aAddedData2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ do_check_eq(aAddedData2.index, 8);
+ run_next_test();
+ });
+
+ try {
+ let node = wrapNodeByIdAndParent(aAddedData.id, PlacesUtils.unfiledBookmarksFolderId);
+ // Simulate a not-bookmarked node, will copy it to a new bookmark.
+ node.id = -1;
+ let txn = PlacesUIUtils.makeTransaction(
+ node,
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch (ex) {
+ do_throw(ex);
+ }
+ });
+
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI(TEST_URL),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ TEST_TITLE);
+});
+
+add_test(function check_annotations() {
+ // As last step check how many items for each annotation exist.
+
+ // Copies should retain the description annotation.
+ let descriptions =
+ PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.DESCRIPTION_ANNO, {});
+ do_check_eq(descriptions.length, 4);
+
+ // Only the original bookmarks should have this annotation.
+ let others = PlacesUtils.annotations.getItemsWithAnnotation("random-anno", {});
+ do_check_eq(others.length, 3);
+ run_next_test();
+});
+
+function run_test()
+{
+ run_next_test();
+}
diff --git a/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js b/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js
new file mode 100644
index 000000000..4db21555f
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_bookmarkshtml.js
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue correctly exports bookmarks.html at shutdown if
+ * browser.bookmarks.autoExportHTML is set to true.
+ */
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* () {
+ remove_bookmarks_html();
+
+ Services.prefs.setBoolPref("browser.bookmarks.autoExportHTML", true);
+ do_register_cleanup(() => Services.prefs.clearUserPref("browser.bookmarks.autoExportHTML"));
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Initialize Places through the History Service.
+ Cc["@mozilla.org/browser/nav-history-service;1"]
+ .getService(Ci.nsINavHistoryService);
+
+ Services.obs.addObserver(function observer() {
+ Services.obs.removeObserver(observer, "profile-before-change");
+ check_bookmarks_html();
+ }, "profile-before-change", false);
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt.js b/browser/components/places/tests/unit/test_browserGlue_corrupt.js
new file mode 100644
index 000000000..5b2a09068
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt.js
@@ -0,0 +1,59 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores bookmarks from a JSON backup if
+ * database is corrupt and one backup is available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ run_next_test();
+}
+
+do_register_cleanup(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+ return PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(function* test_main() {
+ // Create a corrupt database.
+ yield createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once restore has finished and smart bookmarks
+ // have been created.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Check that JSON backup has been restored.
+ // Notice restore from JSON notification is fired before smart bookmarks creation.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(bm.title, "examplejson");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
new file mode 100644
index 000000000..7cb4e5e4c
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue correctly imports from bookmarks.html if database
+ * is corrupt but a JSON backup is not available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ run_next_test();
+}
+
+do_register_cleanup(remove_bookmarks_html);
+
+add_task(function* () {
+ // Create a corrupt database.
+ yield createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once import has finished and smart bookmarks
+ // have been created.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Check that bookmarks html has been restored.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(bm.title, "example");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
new file mode 100644
index 000000000..480420091
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
@@ -0,0 +1,55 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores default bookmarks if database is
+ * corrupt, nor a JSON backup nor bookmarks.html are available.
+ */
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+
+function run_test() {
+ // Remove bookmarks.html from profile.
+ remove_bookmarks_html();
+
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ run_next_test();
+}
+
+add_task(function* () {
+ // Create a corrupt database.
+ yield createCorruptDB();
+
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Check the database was corrupt.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once import has finished and smart bookmarks
+ // have been created.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Check that default bookmarks have been restored.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+
+ // Bug 1283076: Nightly bookmark points to Get Involved page, not Getting Started one
+ let chanTitle = AppConstants.NIGHTLY_BUILD ? "Get Involved" : "Getting Started";
+ do_check_eq(bm.title, chanTitle);
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_distribution.js b/browser/components/places/tests/unit/test_browserGlue_distribution.js
new file mode 100644
index 000000000..c3d6e1d9e
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_distribution.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue correctly imports bookmarks from distribution.ini.
+ */
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed";
+const PREF_DISTRIBUTION_ID = "distribution.id";
+
+const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
+const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+
+function run_test() {
+ // Set special pref to load distribution.ini from the profile folder.
+ Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
+
+ // Copy distribution.ini file to the profile dir.
+ let distroDir = gProfD.clone();
+ distroDir.leafName = "distribution";
+ let iniFile = distroDir.clone();
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ print("distribution.ini already exists, did some test forget to cleanup?");
+ }
+
+ let testDistributionFile = gTestDir.clone();
+ testDistributionFile.append("distribution.ini");
+ testDistributionFile.copyTo(distroDir, "distribution.ini");
+ Assert.ok(testDistributionFile.exists());
+
+ run_next_test();
+}
+
+do_register_cleanup(function () {
+ // Remove the distribution file, even if the test failed, otherwise all
+ // next tests will import it.
+ let iniFile = gProfD.clone();
+ iniFile.leafName = "distribution";
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ }
+ Assert.ok(!iniFile.exists());
+});
+
+add_task(function* () {
+ // Disable Smart Bookmarks creation.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
+
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE);
+
+ // Force distribution.
+ let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
+ glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
+
+ // Test will continue on customization complete notification.
+ yield promiseTopicObserved(TOPIC_CUSTOMIZATION_COMPLETE);
+
+ // Check the custom bookmarks exist on menu.
+ let menuItem = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0
+ });
+ Assert.equal(menuItem.title, "Menu Link Before");
+
+ menuItem = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 1 + DEFAULT_BOOKMARKS_ON_MENU
+ });
+ Assert.equal(menuItem.title, "Menu Link After");
+
+ // Check no favicon or keyword exists for this bookmark
+ yield Assert.rejects(waitForResolvedPromise(() => {
+ return PlacesUtils.promiseFaviconData(menuItem.url.href);
+ }, "Favicon not found", 10), /Favicon\snot\sfound/, "Favicon not found");
+
+ let keywordItem = yield PlacesUtils.keywords.fetch({
+ url: menuItem.url.href
+ });
+ Assert.strictEqual(keywordItem, null);
+
+ // Check the custom bookmarks exist on toolbar.
+ let toolbarItem = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ Assert.equal(toolbarItem.title, "Toolbar Link Before");
+
+ // Check the custom favicon and keyword exist for this bookmark
+ let faviconItem = yield waitForResolvedPromise(() => {
+ return PlacesUtils.promiseFaviconData(toolbarItem.url.href);
+ }, "Favicon not found", 10);
+ Assert.equal(faviconItem.uri.spec, "https://example.org/favicon.png");
+ Assert.greater(faviconItem.dataLen, 0);
+ Assert.equal(faviconItem.mimeType, "image/png");
+
+ let base64Icon = "data:image/png;base64," +
+ base64EncodeString(String.fromCharCode.apply(String, faviconItem.data));
+ Assert.equal(base64Icon, SMALLPNG_DATA_URI.spec);
+
+ keywordItem = yield PlacesUtils.keywords.fetch({
+ url: toolbarItem.url.href
+ });
+ Assert.notStrictEqual(keywordItem, null);
+ Assert.equal(keywordItem.keyword, "e:t:b");
+
+ toolbarItem = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 1 + DEFAULT_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(toolbarItem.title, "Toolbar Link After");
+
+ // Check the bmprocessed pref has been created.
+ Assert.ok(Services.prefs.getBoolPref(PREF_BMPROCESSED));
+
+ // Check distribution prefs have been created.
+ Assert.equal(Services.prefs.getCharPref(PREF_DISTRIBUTION_ID), "516444");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_migrate.js b/browser/components/places/tests/unit/test_browserGlue_migrate.js
new file mode 100644
index 000000000..817f10c81
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_migrate.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue does not overwrite bookmarks imported from the
+ * migrators. They usually run before nsBrowserGlue, so if we find any
+ * bookmark on init, we should not try to import.
+ */
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Remove current database file.
+ clearDB();
+
+ run_next_test();
+}
+
+do_register_cleanup(remove_bookmarks_html);
+
+add_task(function* test_migrate_bookmarks() {
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE);
+
+ // A migrator would run before nsBrowserGlue Places initialization, so mimic
+ // that behavior adding a bookmark and notifying the migration.
+ let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
+ bg.observe(null, "initial-migration-will-import-default-bookmarks", null);
+
+ yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ url: "http://mozilla.org/",
+ title: "migrated"
+ });
+
+ let promise = promiseTopicObserved("places-browser-init-complete");
+ bg.observe(null, "initial-migration-did-import-default-bookmarks", null);
+ yield promise;
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Check the created bookmark still exists.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: SMART_BOOKMARKS_ON_MENU
+ });
+ Assert.equal(bm.title, "migrated");
+
+ // Check that we have not imported any new bookmark.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: SMART_BOOKMARKS_ON_MENU + 1
+ })));
+
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_MENU
+ })));
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_prefs.js b/browser/components/places/tests/unit/test_browserGlue_prefs.js
new file mode 100644
index 000000000..9f3504636
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_prefs.js
@@ -0,0 +1,240 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that nsBrowserGlue is correctly interpreting the preferences settable
+ * by the user or by other components.
+ */
+
+const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
+const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+const TOPICDATA_FORCE_PLACES_INIT = "force-places-init";
+
+var bg = Cc["@mozilla.org/browser/browserglue;1"].
+ getService(Ci.nsIObserver);
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ run_next_test();
+}
+
+do_register_cleanup(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ return PlacesUtils.bookmarks.eraseEverything();
+});
+
+function simulatePlacesInit() {
+ do_print("Simulate Places init");
+ // Force nsBrowserGlue::_initPlaces().
+ bg.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT);
+ return promiseTopicObserved("places-browser-init-complete");
+}
+
+add_task(function* test_checkPreferences() {
+ // Initialize Places through the History Service and check that a new
+ // database has been created.
+ Assert.equal(PlacesUtils.history.databaseStatus,
+ PlacesUtils.history.DATABASE_STATUS_CREATE);
+
+ // Wait for Places init notification.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ // Ensure preferences status.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+
+ Assert.throws(() => Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+ Assert.throws(() => Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+});
+
+add_task(function* test_import() {
+ do_print("Import from bookmarks.html if importBookmarksHTML is true.");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been imported, and a smart bookmark has been
+ // created.
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(bm.title, "example");
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
+
+add_task(function* test_import_noSmartBookmarks() {
+ do_print("import from bookmarks.html, but don't create smart bookmarks " +
+ "if they are disabled");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ Assert.equal(bm.title, "example");
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
+
+add_task(function* test_import_autoExport_updatedSmartBookmarks() {
+ do_print("Import from bookmarks.html, but don't create smart bookmarks " +
+ "if autoExportHTML is true and they are at latest version");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ Assert.equal(bm.title, "example");
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+});
+
+add_task(function* test_import_autoExport_oldSmartBookmarks() {
+ do_print("Import from bookmarks.html, and create smart bookmarks if " +
+ "autoExportHTML is true and they are not at latest version.");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(bm.title, "example");
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+});
+
+add_task(function* test_restore() {
+ do_print("restore from default bookmarks.html if " +
+ "restore_default_bookmarks is true.");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been restored.
+ Assert.ok(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ }));
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+});
+
+add_task(function* test_restore_import() {
+ do_print("setting both importBookmarksHTML and " +
+ "restore_default_bookmarks should restore defaults.");
+
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ })));
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ yield simulatePlacesInit();
+
+ // Check bookmarks.html has been restored.
+ Assert.ok(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ }));
+
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_restore.js b/browser/components/places/tests/unit/test_browserGlue_restore.js
new file mode 100644
index 000000000..9d7ac5ac1
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_restore.js
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue correctly restores bookmarks from a JSON backup if
+ * database has been created and one backup is available.
+ */
+
+function run_test() {
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ remove_all_JSON_backups();
+
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+
+ // Remove current database file.
+ clearDB();
+
+ run_next_test();
+}
+
+do_register_cleanup(function () {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+ return PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(function* test_main() {
+ // Initialize nsBrowserGlue before Places.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsISupports);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+
+ // Check a new database has been created.
+ // nsBrowserGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
+
+ // The test will continue once restore has finished and smart bookmarks
+ // have been created.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Check that JSON backup has been restored.
+ // Notice restore from JSON notification is fired before smart bookmarks creation.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: SMART_BOOKMARKS_ON_TOOLBAR
+ });
+ Assert.equal(bm.title, "examplejson");
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js b/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
new file mode 100644
index 000000000..6ecaec4fe
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
@@ -0,0 +1,285 @@
+/* -*- 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/. */
+
+/**
+ * Tests that nsBrowserGlue is correctly interpreting the preferences settable
+ * by the user or by other components.
+ */
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
+const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
+
+function run_test() {
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+ run_next_test();
+}
+
+do_register_cleanup(() => PlacesUtils.bookmarks.eraseEverything());
+
+function countFolderChildren(aFolderItemId) {
+ let rootNode = PlacesUtils.getFolderContents(aFolderItemId).root;
+ let cc = rootNode.childCount;
+ // Dump contents.
+ for (let i = 0; i < cc ; i++) {
+ let node = rootNode.getChild(i);
+ let title = PlacesUtils.nodeIsSeparator(node) ? "---" : node.title;
+ print("Found child(" + i + "): " + title);
+ }
+ rootNode.containerOpen = false;
+ return cc;
+}
+
+add_task(function* setup() {
+ // Initialize browserGlue, but remove it's listener to places-init-complete.
+ Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
+
+ // Initialize Places.
+ PlacesUtils.history;
+
+ // Wait for Places init notification.
+ yield promiseTopicObserved("places-browser-init-complete");
+
+ // Ensure preferences status.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+ Assert.throws(() => Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+});
+
+add_task(function* test_version_0() {
+ do_print("All smart bookmarks are created if smart bookmarks version is 0.");
+
+ // Sanity check: we should have default bookmark.
+ Assert.ok(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ }));
+
+ Assert.ok(yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0
+ }));
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
+
+add_task(function* test_version_change() {
+ do_print("An existing smart bookmark is replaced when version changes.");
+
+ // Sanity check: we have a smart bookmark on the toolbar.
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+
+ // Change its title.
+ yield PlacesUtils.bookmarks.update({guid: bm.guid, title: "new title"});
+ bm = yield PlacesUtils.bookmarks.fetch({guid: bm.guid});
+ Assert.equal(bm.title, "new title");
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check smart bookmark has been replaced, itemId has changed.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+ Assert.notEqual(bm.title, "new title");
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
+
+add_task(function* test_version_change_pos() {
+ do_print("bookmarks position is retained when version changes.");
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+ let firstItemTitle = bm.title;
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check smart bookmarks are still in correct position.
+ bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
+ Assert.equal(bm.title, firstItemTitle);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
+
+add_task(function* test_version_change_pos_moved() {
+ do_print("moved bookmarks position is retained when version changes.");
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ let bm1 = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: 0
+ });
+ yield checkItemHasAnnotation(bm1.guid, SMART_BOOKMARKS_ANNO);
+ let firstItemTitle = bm1.title;
+
+ // Move the first smart bookmark to the end of the menu.
+ yield PlacesUtils.bookmarks.update({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ guid: bm1.guid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX
+ });
+
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX
+ });
+ Assert.equal(bm.guid, bm1.guid);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ bm1 = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX
+ });
+ yield checkItemHasAnnotation(bm1.guid, SMART_BOOKMARKS_ANNO);
+ Assert.equal(bm1.title, firstItemTitle);
+
+ // Move back the smart bookmark to the original position.
+ yield PlacesUtils.bookmarks.update({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ guid: bm1.guid,
+ index: 1
+ });
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
+
+add_task(function* test_recreation() {
+ do_print("An explicitly removed smart bookmark should not be recreated.");
+
+ // Remove toolbar's smart bookmarks
+ let bm = yield PlacesUtils.bookmarks.fetch({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ index: 0
+ });
+ yield PlacesUtils.bookmarks.remove(bm.guid);
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ // We should not have recreated the smart bookmark on toolbar.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
+
+add_task(function* test_recreation_version_0() {
+ do_print("Even if a smart bookmark has been removed recreate it if version is 0.");
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+
+ yield rebuildSmartBookmarks();
+
+ // Count items.
+ // We should not have recreated the smart bookmark on toolbar.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+});
diff --git a/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js b/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js
new file mode 100644
index 000000000..072056b3f
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js
@@ -0,0 +1,150 @@
+/* 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/. */
+
+const UI_VERSION = 26;
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+const TOPICDATA_BROWSERGLUE_TEST = "force-ui-migration";
+const DEFAULT_BEHAVIOR_PREF = "browser.urlbar.default.behavior";
+const AUTOCOMPLETE_PREF = "browser.urlbar.autocomplete.enabled";
+
+var gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
+ .getService(Ci.nsIObserver);
+var gGetBoolPref = Services.prefs.getBoolPref;
+
+function run_test() {
+ run_next_test();
+}
+
+do_register_cleanup(cleanup);
+
+function cleanup() {
+ let prefix = "browser.urlbar.suggest.";
+ for (let type of ["history", "bookmark", "openpage", "history.onlyTyped"]) {
+ Services.prefs.clearUserPref(prefix + type);
+ }
+ Services.prefs.clearUserPref("browser.migration.version");
+ Services.prefs.clearUserPref(AUTOCOMPLETE_PREF);
+}
+
+function setupBehaviorAndMigrate(aDefaultBehavior, aAutocompleteEnabled = true) {
+ cleanup();
+ // Migrate browser.urlbar.default.behavior preference.
+ Services.prefs.setIntPref("browser.migration.version", UI_VERSION - 1);
+ Services.prefs.setIntPref(DEFAULT_BEHAVIOR_PREF, aDefaultBehavior);
+ Services.prefs.setBoolPref(AUTOCOMPLETE_PREF, aAutocompleteEnabled);
+ // Simulate a migration.
+ gBrowserGlue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_BROWSERGLUE_TEST);
+}
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 0");
+ setupBehaviorAndMigrate(0);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+ "Bookmark preference should be true.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
+ "Openpage preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false.");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 1");
+ setupBehaviorAndMigrate(1);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+ "Bookmark preference should be false.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 2");
+ setupBehaviorAndMigrate(2);
+
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
+ "History preference should be false.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+ "Bookmark preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 3");
+ setupBehaviorAndMigrate(3);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+ "Bookmark preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 19");
+ setupBehaviorAndMigrate(19);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+ "Bookmark preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 33");
+ setupBehaviorAndMigrate(33);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+ "Bookmark preference should be false.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"),
+ "Typed preference should be true");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 129");
+ setupBehaviorAndMigrate(129);
+
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+ "History preference should be true.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+ "Bookmark preference should be false.");
+ Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
+ "Openpage preference should be true");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
+
+add_task(function*() {
+ do_print("Migrate default.behavior = 0, autocomplete.enabled = false");
+ setupBehaviorAndMigrate(0, false);
+
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
+ "History preference should be false.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+ "Bookmark preference should be false.");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+ "Openpage preference should be false");
+ Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+ "Typed preference should be false");
+});
diff --git a/browser/components/places/tests/unit/test_clearHistory_shutdown.js b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
new file mode 100644
index 000000000..0c1d78801
--- /dev/null
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -0,0 +1,181 @@
+/* -*- 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/. */
+
+/**
+ * Tests that requesting clear history at shutdown will really clear history.
+ */
+
+const URIS = [
+ "http://a.example1.com/"
+, "http://b.example1.com/"
+, "http://b.example2.com/"
+, "http://c.example3.com/"
+];
+
+const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
+
+var EXPECTED_NOTIFICATIONS = [
+ "places-shutdown"
+, "places-will-close-connection"
+, "places-expiration-finished"
+, "places-connection-closed"
+];
+
+const UNEXPECTED_NOTIFICATIONS = [
+ "xpcom-shutdown"
+];
+
+const FTP_URL = "ftp://localhost/clearHistoryOnShutdown/";
+
+// Send the profile-after-change notification to the form history component to ensure
+// that it has been initialized.
+var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
+ getService(Ci.nsIObserver);
+formHistoryStartup.observe(null, "profile-after-change", null);
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+ "resource://gre/modules/FormHistory.jsm");
+
+var timeInMicroseconds = Date.now() * 1000;
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* test_execute() {
+ do_print("Initialize browserglue before Places");
+
+ // Avoid default bookmarks import.
+ let glue = Cc["@mozilla.org/browser/browserglue;1"].
+ getService(Ci.nsIObserver);
+ glue.observe(null, "initial-migration-will-import-default-bookmarks", null);
+ glue.observe(null, "test-initialize-sanitizer", null);
+
+
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.offlineApps", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.history", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.downloads", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.formData", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.sessions", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.siteSettings", true);
+
+ Services.prefs.setBoolPref("privacy.sanitize.sanitizeOnShutdown", true);
+
+ do_print("Add visits.");
+ for (let aUrl of URIS) {
+ yield PlacesTestUtils.addVisits({
+ uri: uri(aUrl), visitDate: timeInMicroseconds++,
+ transition: PlacesUtils.history.TRANSITION_TYPED
+ });
+ }
+ do_print("Add cache.");
+ yield storeCache(FTP_URL, "testData");
+ do_print("Add form history.");
+ yield addFormHistory();
+ Assert.equal((yield getFormHistoryCount()), 1, "Added form history");
+
+ do_print("Simulate and wait shutdown.");
+ yield shutdownPlaces();
+
+ Assert.equal((yield getFormHistoryCount()), 0, "Form history cleared");
+
+ let stmt = DBConn(true).createStatement(
+ "SELECT id FROM moz_places WHERE url = :page_url "
+ );
+
+ try {
+ URIS.forEach(function(aUrl) {
+ stmt.params.page_url = aUrl;
+ do_check_false(stmt.executeStep());
+ stmt.reset();
+ });
+ } finally {
+ stmt.finalize();
+ }
+
+ do_print("Check cache");
+ // Check cache.
+ yield checkCache(FTP_URL);
+});
+
+function addFormHistory() {
+ return new Promise(resolve => {
+ let now = Date.now() * 1000;
+ FormHistory.update({ op: "add",
+ fieldname: "testfield",
+ value: "test",
+ timesUsed: 1,
+ firstUsed: now,
+ lastUsed: now
+ },
+ { handleCompletion(reason) { resolve(); } });
+ });
+}
+
+function getFormHistoryCount() {
+ return new Promise((resolve, reject) => {
+ let count = -1;
+ FormHistory.count({ fieldname: "testfield" },
+ { handleResult(result) { count = result; },
+ handleCompletion(reason) { resolve(count); }
+ });
+ });
+}
+
+function storeCache(aURL, aContent) {
+ let cache = Services.cache2;
+ let storage = cache.diskCacheStorage(LoadContextInfo.default, false);
+
+ return new Promise(resolve => {
+ let storeCacheListener = {
+ onCacheEntryCheck: function (entry, appcache) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+
+ onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+ do_check_eq(status, Cr.NS_OK);
+
+ entry.setMetaDataElement("servertype", "0");
+ var os = entry.openOutputStream(0);
+
+ var written = os.write(aContent, aContent.length);
+ if (written != aContent.length) {
+ do_throw("os.write has not written all data!\n" +
+ " Expected: " + written + "\n" +
+ " Actual: " + aContent.length + "\n");
+ }
+ os.close();
+ entry.close();
+ resolve();
+ }
+ };
+
+ storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ storeCacheListener);
+ });
+}
+
+
+function checkCache(aURL) {
+ let cache = Services.cache2;
+ let storage = cache.diskCacheStorage(LoadContextInfo.default, false);
+
+ return new Promise(resolve => {
+ let checkCacheListener = {
+ onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+ do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
+ resolve();
+ }
+ };
+
+ storage.asyncOpenURI(Services.io.newURI(aURL, null, null), "",
+ Ci.nsICacheStorage.OPEN_READONLY,
+ checkCacheListener);
+ });
+}
diff --git a/browser/components/places/tests/unit/test_leftpane_corruption_handling.js b/browser/components/places/tests/unit/test_leftpane_corruption_handling.js
new file mode 100644
index 000000000..0af6f4e95
--- /dev/null
+++ b/browser/components/places/tests/unit/test_leftpane_corruption_handling.js
@@ -0,0 +1,174 @@
+/* -*- 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/. */
+
+/**
+ * Tests that we build a working leftpane in various corruption situations.
+ */
+
+// Used to store the original leftPaneFolderId getter.
+var gLeftPaneFolderIdGetter;
+var gAllBookmarksFolderIdGetter;
+// Used to store the original left Pane status as a JSON string.
+var gReferenceHierarchy;
+var gLeftPaneFolderId;
+
+add_task(function* () {
+ // We want empty roots.
+ yield PlacesUtils.bookmarks.eraseEverything();
+
+ // Sanity check.
+ Assert.ok(!!PlacesUIUtils);
+
+ // Check getters.
+ gLeftPaneFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId");
+ Assert.equal(typeof(gLeftPaneFolderIdGetter.get), "function");
+ gAllBookmarksFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "allBookmarksFolderId");
+ Assert.equal(typeof(gAllBookmarksFolderIdGetter.get), "function");
+
+ do_register_cleanup(() => PlacesUtils.bookmarks.eraseEverything());
+});
+
+add_task(function* () {
+ // Add a third party bogus annotated item. Should not be removed.
+ let folder = yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "test",
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER
+ });
+
+ let folderId = yield PlacesUtils.promiseItemId(folder.guid);
+ PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
+ "test", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+
+ // Create the left pane, and store its current status, it will be used
+ // as reference value.
+ gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
+ gReferenceHierarchy = folderIdToHierarchy(gLeftPaneFolderId);
+
+ while (gTests.length) {
+ // Run current test.
+ yield Task.spawn(gTests.shift());
+
+ // Regenerate getters.
+ Object.defineProperty(PlacesUIUtils, "leftPaneFolderId", gLeftPaneFolderIdGetter);
+ gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
+ Object.defineProperty(PlacesUIUtils, "allBookmarksFolderId", gAllBookmarksFolderIdGetter);
+
+ // Check the new left pane folder.
+ let leftPaneHierarchy = folderIdToHierarchy(gLeftPaneFolderId)
+ Assert.equal(gReferenceHierarchy, leftPaneHierarchy);
+
+ folder = yield PlacesUtils.bookmarks.fetch({guid: folder.guid});
+ Assert.equal(folder.title, "test");
+ }
+});
+
+// Corruption cases.
+var gTests = [
+
+ function* test1() {
+ print("1. Do nothing, checks test calibration.");
+ },
+
+ function* test2() {
+ print("2. Delete the left pane folder.");
+ let guid = yield PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
+ yield PlacesUtils.bookmarks.remove(guid);
+ },
+
+ function* test3() {
+ print("3. Delete a child of the left pane folder.");
+ let guid = yield PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
+ let bm = yield PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
+ yield PlacesUtils.bookmarks.remove(bm.guid);
+ },
+
+ function* test4() {
+ print("4. Delete AllBookmarks.");
+ let guid = yield PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
+ yield PlacesUtils.bookmarks.remove(guid);
+ },
+
+ function* test5() {
+ print("5. Create a duplicated left pane folder.");
+ let folder = yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "PlacesRoot",
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER
+ });
+
+ let folderId = yield PlacesUtils.promiseItemId(folder.guid);
+ PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_FOLDER_ANNO,
+ "PlacesRoot", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ },
+
+ function* test6() {
+ print("6. Create a duplicated left pane query.");
+ let folder = yield PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "AllBookmarks",
+ index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER
+ });
+
+ let folderId = yield PlacesUtils.promiseItemId(folder.guid);
+ PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
+ "AllBookmarks", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ },
+
+ function* test7() {
+ print("7. Remove the left pane folder annotation.");
+ PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
+ ORGANIZER_FOLDER_ANNO);
+ },
+
+ function* test8() {
+ print("8. Remove a left pane query annotation.");
+ PlacesUtils.annotations.removeItemAnnotation(PlacesUIUtils.allBookmarksFolderId,
+ ORGANIZER_QUERY_ANNO);
+ },
+
+ function* test9() {
+ print("9. Remove a child of AllBookmarks.");
+ let guid = yield PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
+ let bm = yield PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
+ yield PlacesUtils.bookmarks.remove(bm.guid);
+ }
+
+];
+
+/**
+ * Convert a folder item id to a JSON representation of it and its contents.
+ */
+function folderIdToHierarchy(aFolderId) {
+ let root = PlacesUtils.getFolderContents(aFolderId).root;
+ let hier = JSON.stringify(hierarchyToObj(root));
+ root.containerOpen = false;
+ return hier;
+}
+
+function hierarchyToObj(aNode) {
+ let o = {}
+ o.title = aNode.title;
+ o.annos = PlacesUtils.getAnnotationsForItem(aNode.itemId)
+ if (PlacesUtils.nodeIsURI(aNode)) {
+ o.uri = aNode.uri;
+ }
+ else if (PlacesUtils.nodeIsFolder(aNode)) {
+ o.children = [];
+ PlacesUtils.asContainer(aNode).containerOpen = true;
+ for (let i = 0; i < aNode.childCount; ++i) {
+ o.children.push(hierarchyToObj(aNode.getChild(i)));
+ }
+ aNode.containerOpen = false;
+ }
+ return o;
+}
diff --git a/browser/components/places/tests/unit/xpcshell.ini b/browser/components/places/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..1c40e1c53
--- /dev/null
+++ b/browser/components/places/tests/unit/xpcshell.ini
@@ -0,0 +1,25 @@
+[DEFAULT]
+head = head_bookmarks.js
+tail =
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+support-files =
+ bookmarks.glue.html
+ bookmarks.glue.json
+ corruptDB.sqlite
+ distribution.ini
+
+[test_421483.js]
+[test_browserGlue_bookmarkshtml.js]
+[test_browserGlue_corrupt.js]
+[test_browserGlue_corrupt_nobackup.js]
+[test_browserGlue_corrupt_nobackup_default.js]
+[test_browserGlue_distribution.js]
+[test_browserGlue_migrate.js]
+[test_browserGlue_prefs.js]
+[test_browserGlue_restore.js]
+[test_browserGlue_smartBookmarks.js]
+[test_browserGlue_urlbar_defaultbehavior_migration.js]
+[test_clearHistory_shutdown.js]
+[test_leftpane_corruption_handling.js]
+[test_PUIU_makeTransaction.js]