michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: function waitForBookmarkNotification(aNotification, aCallback, aProperty) michael@0: { michael@0: PlacesUtils.bookmarks.addObserver({ michael@0: validate: function (aMethodName, aData) michael@0: { michael@0: if (aMethodName == aNotification && michael@0: (!aProperty || aProperty == aData.property)) { michael@0: PlacesUtils.bookmarks.removeObserver(this); michael@0: aCallback(aData); michael@0: } michael@0: }, michael@0: michael@0: // nsINavBookmarkObserver michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]), michael@0: onBeginUpdateBatch: function onBeginUpdateBatch() michael@0: this.validate(arguments.callee.name, arguments), michael@0: onEndUpdateBatch: function onEndUpdateBatch() michael@0: this.validate(arguments.callee.name, arguments), michael@0: onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType, michael@0: aURI, aTitle) michael@0: { michael@0: return this.validate(arguments.callee.name, { id: aItemId, michael@0: index: aIndex, michael@0: type: aItemType, michael@0: url: aURI ? aURI.spec : null, michael@0: title: aTitle }); michael@0: }, michael@0: onItemRemoved: function onItemRemoved() michael@0: this.validate(arguments.callee.name, arguments), michael@0: onItemChanged: function onItemChanged(aItemId, aProperty, aIsAnno, michael@0: aNewValue, aLastModified, aItemType) michael@0: { michael@0: return this.validate(arguments.callee.name, michael@0: { id: aItemId, michael@0: get index() PlacesUtils.bookmarks.getItemIndex(this.id), michael@0: type: aItemType, michael@0: property: aProperty, michael@0: get url() aItemType == PlacesUtils.bookmarks.TYPE_BOOKMARK ? michael@0: PlacesUtils.bookmarks.getBookmarkURI(this.id).spec : michael@0: null, michael@0: get title() PlacesUtils.bookmarks.getItemTitle(this.id), michael@0: }); michael@0: }, michael@0: onItemVisited: function onItemVisited() michael@0: this.validate(arguments.callee.name, arguments), michael@0: onItemMoved: function onItemMoved(aItemId, aOldParentId, aOldIndex, michael@0: aNewParentId, aNewIndex, aItemType) michael@0: { michael@0: this.validate(arguments.callee.name, { id: aItemId, michael@0: index: aNewIndex, michael@0: type: aItemType }); michael@0: } michael@0: }, false); michael@0: } michael@0: michael@0: function wrapNodeByIdAndParent(aItemId, aParentId) michael@0: { michael@0: let wrappedNode; michael@0: let root = PlacesUtils.getFolderContents(aParentId, false, false).root; michael@0: for (let i = 0; i < root.childCount; ++i) { michael@0: let node = root.getChild(i); michael@0: if (node.itemId == aItemId) { michael@0: let type; michael@0: if (PlacesUtils.nodeIsContainer(node)) { michael@0: type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER; michael@0: } michael@0: else if (PlacesUtils.nodeIsURI(node)) { michael@0: type = PlacesUtils.TYPE_X_MOZ_PLACE; michael@0: } michael@0: else if (PlacesUtils.nodeIsSeparator(node)) { michael@0: type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR; michael@0: } michael@0: else { michael@0: do_throw("Unknown node type"); michael@0: } michael@0: wrappedNode = PlacesUtils.wrapNode(node, type); michael@0: } michael@0: } michael@0: root.containerOpen = false; michael@0: return JSON.parse(wrappedNode); michael@0: } michael@0: michael@0: add_test(function test_text_paste() michael@0: { michael@0: const TEST_URL = "http://places.moz.org/" michael@0: const TEST_TITLE = "Places bookmark" michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.url, TEST_URL); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 0); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: { title: TEST_TITLE, uri: TEST_URL }, michael@0: PlacesUtils.TYPE_X_MOZ_URL, michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: true // Unused for text. michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: }); michael@0: michael@0: add_test(function test_container() michael@0: { michael@0: const TEST_TITLE = "Places folder" michael@0: michael@0: waitForBookmarkNotification("onItemChanged", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER); michael@0: do_check_eq(aData.index, 1); michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER); michael@0: do_check_eq(aData.index, 2); michael@0: let id = aData.id; michael@0: michael@0: waitForBookmarkNotification("onItemMoved", function(aData) michael@0: { michael@0: do_check_eq(aData.id, id); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER); michael@0: do_check_eq(aData.index, 1); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: 1, // Move to position 1. michael@0: false michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: }); michael@0: michael@0: try { michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: true michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: } catch(ex) { michael@0: do_throw(ex); michael@0: } michael@0: }, "random-anno"); michael@0: michael@0: let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId, michael@0: TEST_TITLE, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX); michael@0: PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO, michael@0: "description", 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: PlacesUtils.annotations.setItemAnnotation(id, "random-anno", michael@0: "random-value", 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: }); michael@0: michael@0: michael@0: add_test(function test_separator() michael@0: { michael@0: waitForBookmarkNotification("onItemChanged", function(aData) michael@0: { michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR); michael@0: do_check_eq(aData.index, 3); michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR); michael@0: do_check_eq(aData.index, 4); michael@0: let id = aData.id; michael@0: michael@0: waitForBookmarkNotification("onItemMoved", function(aData) michael@0: { michael@0: do_check_eq(aData.id, id); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR); michael@0: do_check_eq(aData.index, 1); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: 1, // Move to position 1. michael@0: false michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: }); michael@0: michael@0: try { michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: true michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: } catch(ex) { michael@0: do_throw(ex); michael@0: } michael@0: }, "random-anno"); michael@0: michael@0: let id = PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX); michael@0: PlacesUtils.annotations.setItemAnnotation(id, "random-anno", michael@0: "random-value", 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: }); michael@0: michael@0: add_test(function test_bookmark() michael@0: { michael@0: const TEST_URL = "http://places.moz.org/" michael@0: const TEST_TITLE = "Places bookmark" michael@0: michael@0: waitForBookmarkNotification("onItemChanged", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.url, TEST_URL); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 5); michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.url, TEST_URL); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 6); michael@0: let id = aData.id; michael@0: michael@0: waitForBookmarkNotification("onItemMoved", function(aData) michael@0: { michael@0: do_check_eq(aData.id, id); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 1); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: 1, // Move to position 1. michael@0: false michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: }); michael@0: michael@0: try { michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId), michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: true michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: } catch(ex) { michael@0: do_throw(ex); michael@0: } michael@0: }, "random-anno"); michael@0: michael@0: let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, michael@0: NetUtil.newURI(TEST_URL), michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: TEST_TITLE); michael@0: PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO, michael@0: "description", 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: PlacesUtils.annotations.setItemAnnotation(id, "random-anno", michael@0: "random-value", 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: }); michael@0: michael@0: add_test(function test_visit() michael@0: { michael@0: const TEST_URL = "http://places.moz.org/" michael@0: const TEST_TITLE = "Places bookmark" michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.url, TEST_URL); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 7); michael@0: michael@0: waitForBookmarkNotification("onItemAdded", function(aData) michael@0: { michael@0: do_check_eq(aData.title, TEST_TITLE); michael@0: do_check_eq(aData.url, TEST_URL); michael@0: do_check_eq(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: do_check_eq(aData.index, 8); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: try { michael@0: let node = wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId); michael@0: // Simulate a not-bookmarked node, will copy it to a new bookmark. michael@0: node.id = -1; michael@0: let txn = PlacesUIUtils.makeTransaction( michael@0: node, michael@0: 0, // Unused for real nodes. michael@0: PlacesUtils.unfiledBookmarksFolderId, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: true michael@0: ); michael@0: PlacesUtils.transactionManager.doTransaction(txn); michael@0: } catch(ex) { michael@0: do_throw(ex); michael@0: } michael@0: }); michael@0: michael@0: PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, michael@0: NetUtil.newURI(TEST_URL), michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: TEST_TITLE); michael@0: }); michael@0: michael@0: add_test(function check_annotations() { michael@0: // As last step check how many items for each annotation exist. michael@0: michael@0: // Copies should retain the description annotation. michael@0: let descriptions = michael@0: PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.DESCRIPTION_ANNO, {}); michael@0: do_check_eq(descriptions.length, 4); michael@0: michael@0: // Only the original bookmarks should have this annotation. michael@0: let others = PlacesUtils.annotations.getItemsWithAnnotation("random-anno", {}); michael@0: do_check_eq(others.length, 3); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: function run_test() michael@0: { michael@0: run_next_test(); michael@0: }