michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: /* This test checks that bookmarks service is correctly forwarding async michael@0: * events like visit or favicon additions. */ michael@0: michael@0: const NOW = Date.now() * 1000; michael@0: michael@0: let observer = { michael@0: bookmarks: [], michael@0: observedBookmarks: 0, michael@0: observedVisitId: 0, michael@0: deferred: null, michael@0: michael@0: /** michael@0: * Returns a promise that is resolved when the observer determines that the michael@0: * test can continue. This is required rather than calling run_next_test michael@0: * directly in the observer because there are cases where we must wait for michael@0: * other asynchronous events to be completed in addition to this. michael@0: */ michael@0: setupCompletionPromise: function () michael@0: { michael@0: this.observedBookmarks = 0; michael@0: this.deferred = Promise.defer(); michael@0: return this.deferred.promise; michael@0: }, michael@0: michael@0: onBeginUpdateBatch: function () {}, michael@0: onEndUpdateBatch: function () {}, michael@0: onItemAdded: function () {}, michael@0: onItemRemoved: function () {}, michael@0: onItemMoved: function () {}, michael@0: onItemChanged: function(aItemId, aProperty, aIsAnnotation, aNewValue, michael@0: aLastModified, aItemType) michael@0: { michael@0: do_log_info("Check that we got the correct change information."); michael@0: do_check_neq(this.bookmarks.indexOf(aItemId), -1); michael@0: if (aProperty == "favicon") { michael@0: do_check_false(aIsAnnotation); michael@0: do_check_eq(aNewValue, SMALLPNG_DATA_URI.spec); michael@0: do_check_eq(aLastModified, 0); michael@0: do_check_eq(aItemType, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: } michael@0: else if (aProperty == "cleartime") { michael@0: do_check_false(aIsAnnotation); michael@0: do_check_eq(aNewValue, ""); michael@0: do_check_eq(aLastModified, 0); michael@0: do_check_eq(aItemType, PlacesUtils.bookmarks.TYPE_BOOKMARK); michael@0: } michael@0: else { michael@0: do_throw("Unexpected property change " + aProperty); michael@0: } michael@0: michael@0: if (++this.observedBookmarks == this.bookmarks.length) { michael@0: this.deferred.resolve(); michael@0: } michael@0: }, michael@0: onItemVisited: function(aItemId, aVisitId, aTime) michael@0: { michael@0: do_log_info("Check that we got the correct visit information."); michael@0: do_check_neq(this.bookmarks.indexOf(aItemId), -1); michael@0: this.observedVisitId = aVisitId; michael@0: do_check_eq(aTime, NOW); michael@0: if (++this.observedBookmarks == this.bookmarks.length) { michael@0: this.deferred.resolve(); michael@0: } michael@0: }, michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([ michael@0: Ci.nsINavBookmarkObserver, michael@0: ]) michael@0: }; michael@0: PlacesUtils.bookmarks.addObserver(observer, false); michael@0: michael@0: add_task(function test_add_visit() michael@0: { michael@0: let observerPromise = observer.setupCompletionPromise(); michael@0: michael@0: // Add a visit to the bookmark and wait for the observer. michael@0: let visitId; michael@0: let deferUpdatePlaces = Promise.defer(); michael@0: PlacesUtils.asyncHistory.updatePlaces({ michael@0: uri: NetUtil.newURI("http://book.ma.rk/"), michael@0: visits: [{ transitionType: TRANSITION_TYPED, visitDate: NOW }] michael@0: }, { michael@0: handleError: function TAV_handleError() { michael@0: deferUpdatePlaces.reject(new Error("Unexpected error in adding visit.")); michael@0: }, michael@0: handleResult: function (aPlaceInfo) { michael@0: visitId = aPlaceInfo.visits[0].visitId; michael@0: }, michael@0: handleCompletion: function TAV_handleCompletion() { michael@0: deferUpdatePlaces.resolve(); michael@0: } michael@0: }); michael@0: michael@0: // Wait for both the observer and the asynchronous update, in any order. michael@0: yield deferUpdatePlaces.promise; michael@0: yield observerPromise; michael@0: michael@0: // Check that both asynchronous results are consistent. michael@0: do_check_eq(observer.observedVisitId, visitId); michael@0: }); michael@0: michael@0: add_task(function test_add_icon() michael@0: { michael@0: let observerPromise = observer.setupCompletionPromise(); michael@0: PlacesUtils.favicons.setAndFetchFaviconForPage(NetUtil.newURI("http://book.ma.rk/"), michael@0: SMALLPNG_DATA_URI, true, michael@0: PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE); michael@0: yield observerPromise; michael@0: }); michael@0: michael@0: add_task(function test_remove_page() michael@0: { michael@0: let observerPromise = observer.setupCompletionPromise(); michael@0: PlacesUtils.history.removePage(NetUtil.newURI("http://book.ma.rk/")); michael@0: yield observerPromise; michael@0: }); michael@0: michael@0: add_task(function cleanup() michael@0: { michael@0: PlacesUtils.bookmarks.removeObserver(observer, false); michael@0: }); michael@0: michael@0: add_task(function shutdown() michael@0: { michael@0: // Check that async observers don't try to create async statements after michael@0: // shutdown. That would cause assertions, since the async thread is gone michael@0: // already. Note that in such a case the notifications are not fired, so we michael@0: // cannot test for them. michael@0: // Put an history notification that triggers AsyncGetBookmarksForURI between michael@0: // asyncClose() and the actual connection closing. Enqueuing a main-thread michael@0: // event just after places-will-close-connection should ensure it runs before michael@0: // places-connection-closed. michael@0: // Notice this code is not using helpers cause it depends on a very specific michael@0: // order, a change in the helpers code could make this test useless. michael@0: let deferred = Promise.defer(); michael@0: michael@0: Services.obs.addObserver(function onNotification() { michael@0: Services.obs.removeObserver(onNotification, "places-will-close-connection"); michael@0: do_check_true(true, "Observed fake places shutdown"); michael@0: michael@0: Services.tm.mainThread.dispatch(() => { michael@0: // WARNING: this is very bad, never use out of testing code. michael@0: PlacesUtils.bookmarks.QueryInterface(Ci.nsINavHistoryObserver) michael@0: .onPageChanged(NetUtil.newURI("http://book.ma.rk/"), michael@0: Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON, michael@0: "test", "test"); michael@0: deferred.resolve(promiseTopicObserved("places-connection-closed")); michael@0: }, Ci.nsIThread.DISPATCH_NORMAL); michael@0: }, "places-will-close-connection", false); michael@0: shutdownPlaces(); michael@0: michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: function run_test() michael@0: { michael@0: // Add multiple bookmarks to the same uri. michael@0: observer.bookmarks.push( michael@0: PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, michael@0: NetUtil.newURI("http://book.ma.rk/"), michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: "Bookmark") michael@0: ); michael@0: observer.bookmarks.push( michael@0: PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId, michael@0: NetUtil.newURI("http://book.ma.rk/"), michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: "Bookmark") michael@0: ); michael@0: michael@0: run_next_test(); michael@0: }