michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: /** michael@0: * Generic nsINavHistoryObserver that doesn't implement anything, but provides michael@0: * dummy methods to prevent errors about an object not having a certain method. michael@0: */ michael@0: function NavHistoryObserver() { michael@0: } michael@0: NavHistoryObserver.prototype = { michael@0: onBeginUpdateBatch: function() { }, michael@0: onEndUpdateBatch: function() { }, michael@0: onVisit: function() { }, michael@0: onTitleChanged: function() { }, michael@0: onDeleteURI: function() { }, michael@0: onClearHistory: function() { }, michael@0: onPageChanged: function() { }, michael@0: onDeleteVisits: function() { }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]) michael@0: }; michael@0: michael@0: /** michael@0: * Registers a one-time history observer for and calls the callback michael@0: * when the specified nsINavHistoryObserver method is called. michael@0: * Returns a promise that is resolved when the callback returns. michael@0: */ michael@0: function onNotify(callback) { michael@0: let deferred = Promise.defer(); michael@0: let obs = new NavHistoryObserver(); michael@0: obs[callback.name] = function () { michael@0: PlacesUtils.history.removeObserver(this); michael@0: callback.apply(this, arguments); michael@0: deferred.resolve(); michael@0: }; michael@0: PlacesUtils.history.addObserver(obs, false); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Asynchronous task that adds a visit to the history database. michael@0: */ michael@0: function task_add_visit(uri, timestamp, transition) { michael@0: uri = uri || NetUtil.newURI("http://firefox.com/"); michael@0: timestamp = timestamp || Date.now() * 1000; michael@0: yield promiseAddVisits({ michael@0: uri: uri, michael@0: transition: transition || TRANSITION_TYPED, michael@0: visitDate: timestamp michael@0: }); michael@0: throw new Task.Result([uri, timestamp]); michael@0: } michael@0: michael@0: function run_test() { michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_task(function test_onVisit() { michael@0: let promiseNotify = onNotify(function onVisit(aURI, aVisitID, aTime, michael@0: aSessionID, aReferringID, michael@0: aTransitionType, aGUID, michael@0: aHidden) { michael@0: do_check_true(aURI.equals(testuri)); michael@0: do_check_true(aVisitID > 0); michael@0: do_check_eq(aTime, testtime); michael@0: do_check_eq(aSessionID, 0); michael@0: do_check_eq(aReferringID, 0); michael@0: do_check_eq(aTransitionType, TRANSITION_TYPED); michael@0: do_check_guid_for_uri(aURI, aGUID); michael@0: do_check_false(aHidden); michael@0: }); michael@0: let testuri = NetUtil.newURI("http://firefox.com/"); michael@0: let testtime = Date.now() * 1000; michael@0: yield task_add_visit(testuri, testtime); michael@0: yield promiseNotify; michael@0: }); michael@0: michael@0: add_task(function test_onVisit() { michael@0: let promiseNotify = onNotify(function onVisit(aURI, aVisitID, aTime, michael@0: aSessionID, aReferringID, michael@0: aTransitionType, aGUID, michael@0: aHidden) { michael@0: do_check_true(aURI.equals(testuri)); michael@0: do_check_true(aVisitID > 0); michael@0: do_check_eq(aTime, testtime); michael@0: do_check_eq(aSessionID, 0); michael@0: do_check_eq(aReferringID, 0); michael@0: do_check_eq(aTransitionType, TRANSITION_FRAMED_LINK); michael@0: do_check_guid_for_uri(aURI, aGUID); michael@0: do_check_true(aHidden); michael@0: }); michael@0: let testuri = NetUtil.newURI("http://hidden.firefox.com/"); michael@0: let testtime = Date.now() * 1000; michael@0: yield task_add_visit(testuri, testtime, TRANSITION_FRAMED_LINK); michael@0: yield promiseNotify; michael@0: }); michael@0: michael@0: add_task(function test_onDeleteURI() { michael@0: let promiseNotify = onNotify(function onDeleteURI(aURI, aGUID, aReason) { michael@0: do_check_true(aURI.equals(testuri)); michael@0: // Can't use do_check_guid_for_uri() here because the visit is already gone. michael@0: do_check_eq(aGUID, testguid); michael@0: do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_DELETED); michael@0: }); michael@0: let [testuri] = yield task_add_visit(); michael@0: let testguid = do_get_guid_for_uri(testuri); michael@0: PlacesUtils.bhistory.removePage(testuri); michael@0: yield promiseNotify; michael@0: }); michael@0: michael@0: add_task(function test_onDeleteVisits() { michael@0: let promiseNotify = onNotify(function onDeleteVisits(aURI, aVisitTime, aGUID, michael@0: aReason) { michael@0: do_check_true(aURI.equals(testuri)); michael@0: // Can't use do_check_guid_for_uri() here because the visit is already gone. michael@0: do_check_eq(aGUID, testguid); michael@0: do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_DELETED); michael@0: do_check_eq(aVisitTime, 0); // All visits have been removed. michael@0: }); michael@0: let msecs24hrsAgo = Date.now() - (86400 * 1000); michael@0: let [testuri] = yield task_add_visit(undefined, msecs24hrsAgo * 1000); michael@0: // Add a bookmark so the page is not removed. michael@0: PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, michael@0: testuri, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: "test"); michael@0: let testguid = do_get_guid_for_uri(testuri); michael@0: PlacesUtils.bhistory.removePage(testuri); michael@0: yield promiseNotify; michael@0: }); michael@0: michael@0: add_task(function test_onTitleChanged() { michael@0: let promiseNotify = onNotify(function onTitleChanged(aURI, aTitle, aGUID) { michael@0: do_check_true(aURI.equals(testuri)); michael@0: do_check_eq(aTitle, title); michael@0: do_check_guid_for_uri(aURI, aGUID); michael@0: }); michael@0: michael@0: let [testuri] = yield task_add_visit(); michael@0: let title = "test-title"; michael@0: yield promiseAddVisits({ michael@0: uri: testuri, michael@0: title: title michael@0: }); michael@0: yield promiseNotify; michael@0: }); michael@0: michael@0: add_task(function test_onPageChanged() { michael@0: let promiseNotify = onNotify(function onPageChanged(aURI, aChangedAttribute, michael@0: aNewValue, aGUID) { michael@0: do_check_eq(aChangedAttribute, Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON); michael@0: do_check_true(aURI.equals(testuri)); michael@0: do_check_eq(aNewValue, SMALLPNG_DATA_URI.spec); michael@0: do_check_guid_for_uri(aURI, aGUID); michael@0: }); michael@0: michael@0: let [testuri] = yield task_add_visit(); michael@0: michael@0: // The new favicon for the page must have data associated with it in order to michael@0: // receive the onPageChanged notification. To keep this test self-contained, michael@0: // we use an URI representing the smallest possible PNG file. michael@0: PlacesUtils.favicons.setAndFetchFaviconForPage(testuri, SMALLPNG_DATA_URI, michael@0: false, michael@0: PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, michael@0: null); michael@0: yield promiseNotify; michael@0: });