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: * This file tests the nsIDownloadHistory Places implementation. michael@0: */ michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "gDownloadHistory", michael@0: "@mozilla.org/browser/download-history;1", michael@0: "nsIDownloadHistory"); michael@0: michael@0: const DOWNLOAD_URI = NetUtil.newURI("http://www.example.com/"); michael@0: const REFERRER_URI = NetUtil.newURI("http://www.example.org/"); michael@0: const PRIVATE_URI = NetUtil.newURI("http://www.example.net/"); michael@0: michael@0: /** michael@0: * Waits for the first visit notification to be received. michael@0: * michael@0: * @param aCallback michael@0: * Callback function to be called with the same arguments of onVisit. michael@0: */ michael@0: function waitForOnVisit(aCallback) { michael@0: let historyObserver = { michael@0: __proto__: NavHistoryObserver.prototype, michael@0: onVisit: function HO_onVisit() { michael@0: PlacesUtils.history.removeObserver(this); michael@0: aCallback.apply(null, arguments); michael@0: } michael@0: }; michael@0: PlacesUtils.history.addObserver(historyObserver, false); michael@0: } michael@0: michael@0: /** michael@0: * Waits for the first onDeleteURI notification to be received. michael@0: * michael@0: * @param aCallback michael@0: * Callback function to be called with the same arguments of onDeleteURI. michael@0: */ michael@0: function waitForOnDeleteURI(aCallback) { michael@0: let historyObserver = { michael@0: __proto__: NavHistoryObserver.prototype, michael@0: onDeleteURI: function HO_onDeleteURI() { michael@0: PlacesUtils.history.removeObserver(this); michael@0: aCallback.apply(null, arguments); michael@0: } michael@0: }; michael@0: PlacesUtils.history.addObserver(historyObserver, false); michael@0: } michael@0: michael@0: /** michael@0: * Waits for the first onDeleteVisits notification to be received. michael@0: * michael@0: * @param aCallback michael@0: * Callback function to be called with the same arguments of onDeleteVisits. michael@0: */ michael@0: function waitForOnDeleteVisits(aCallback) { michael@0: let historyObserver = { michael@0: __proto__: NavHistoryObserver.prototype, michael@0: onDeleteVisits: function HO_onDeleteVisits() { michael@0: PlacesUtils.history.removeObserver(this); michael@0: aCallback.apply(null, arguments); michael@0: } michael@0: }; michael@0: PlacesUtils.history.addObserver(historyObserver, false); michael@0: } michael@0: michael@0: function run_test() michael@0: { michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_test(function test_dh_is_from_places() michael@0: { michael@0: // Test that this nsIDownloadHistory is the one places implements. michael@0: do_check_true(gDownloadHistory instanceof Ci.mozIAsyncHistory); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_test(function test_dh_addRemoveDownload() michael@0: { michael@0: waitForOnVisit(function DHAD_onVisit(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: michael@0: // Verify that the URI is already available in results at this time. michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: waitForOnDeleteURI(function DHRAD_onDeleteURI(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: michael@0: // Verify that the URI is already available in results at this time. michael@0: do_check_false(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: run_next_test(); michael@0: }); michael@0: gDownloadHistory.removeAllDownloads(); michael@0: }); michael@0: michael@0: gDownloadHistory.addDownload(DOWNLOAD_URI, null, Date.now() * 1000); michael@0: }); michael@0: michael@0: add_test(function test_dh_addMultiRemoveDownload() michael@0: { michael@0: promiseAddVisits({ uri: DOWNLOAD_URI, michael@0: transition: TRANSITION_TYPED }).then(function () { michael@0: waitForOnVisit(function DHAD_onVisit(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: waitForOnDeleteVisits(function DHRAD_onDeleteVisits(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: promiseClearHistory().then(run_next_test); michael@0: }); michael@0: gDownloadHistory.removeAllDownloads(); michael@0: }); michael@0: michael@0: gDownloadHistory.addDownload(DOWNLOAD_URI, null, Date.now() * 1000); michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_dh_addBookmarkRemoveDownload() michael@0: { michael@0: PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, michael@0: DOWNLOAD_URI, michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: "A bookmark"); michael@0: waitForOnVisit(function DHAD_onVisit(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: waitForOnDeleteVisits(function DHRAD_onDeleteVisits(aURI) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: promiseClearHistory().then(run_next_test); michael@0: }); michael@0: gDownloadHistory.removeAllDownloads(); michael@0: }); michael@0: michael@0: gDownloadHistory.addDownload(DOWNLOAD_URI, null, Date.now() * 1000); michael@0: }); michael@0: michael@0: add_test(function test_dh_addDownload_referrer() michael@0: { michael@0: waitForOnVisit(function DHAD_prepareReferrer(aURI, aVisitID) { michael@0: do_check_true(aURI.equals(REFERRER_URI)); michael@0: let referrerVisitId = aVisitID; michael@0: michael@0: waitForOnVisit(function DHAD_onVisit(aURI, aVisitID, aTime, aSessionID, michael@0: aReferringID) { michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: do_check_eq(aReferringID, referrerVisitId); michael@0: michael@0: // Verify that the URI is already available in results at this time. michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: michael@0: promiseClearHistory().then(run_next_test); michael@0: }); michael@0: michael@0: gDownloadHistory.addDownload(DOWNLOAD_URI, REFERRER_URI, Date.now() * 1000); michael@0: }); michael@0: michael@0: // Note that we don't pass the optional callback argument here because we must michael@0: // ensure that we receive the onVisit notification before we call addDownload. michael@0: PlacesUtils.asyncHistory.updatePlaces({ michael@0: uri: REFERRER_URI, michael@0: visits: [{ michael@0: transitionType: Ci.nsINavHistoryService.TRANSITION_TYPED, michael@0: visitDate: Date.now() * 1000 michael@0: }] michael@0: }); michael@0: }); michael@0: michael@0: add_test(function test_dh_addDownload_disabledHistory() michael@0: { michael@0: waitForOnVisit(function DHAD_onVisit(aURI) { michael@0: // We should only receive the notification for the non-private URI. This michael@0: // test is based on the assumption that visit notifications are received in michael@0: // the same order of the addDownload calls, which is currently true because michael@0: // database access is serialized on the same worker thread. michael@0: do_check_true(aURI.equals(DOWNLOAD_URI)); michael@0: michael@0: do_check_true(!!page_in_database(DOWNLOAD_URI)); michael@0: do_check_false(!!page_in_database(PRIVATE_URI)); michael@0: michael@0: promiseClearHistory().then(run_next_test); michael@0: }); michael@0: michael@0: Services.prefs.setBoolPref("places.history.enabled", false); michael@0: gDownloadHistory.addDownload(PRIVATE_URI, REFERRER_URI, Date.now() * 1000); michael@0: michael@0: // The addDownload functions calls CanAddURI synchronously, thus we can set michael@0: // the preference back to true immediately (not all apps enable places by michael@0: // default). michael@0: Services.prefs.setBoolPref("places.history.enabled", true); michael@0: gDownloadHistory.addDownload(DOWNLOAD_URI, REFERRER_URI, Date.now() * 1000); michael@0: }); michael@0: michael@0: /** michael@0: * Tests that nsIDownloadHistory::AddDownload saves the additional download michael@0: * details if the optional destination URL is specified. michael@0: */ michael@0: add_test(function test_dh_details() michael@0: { michael@0: const REMOTE_URI = NetUtil.newURI("http://localhost/"); michael@0: const SOURCE_URI = NetUtil.newURI("http://example.com/test_dh_details"); michael@0: const DEST_FILE_NAME = "dest.txt"; michael@0: michael@0: // We must build a real, valid file URI for the destination. michael@0: let destFileUri = NetUtil.newURI(FileUtils.getFile("TmpD", [DEST_FILE_NAME])); michael@0: michael@0: let titleSet = false; michael@0: let destinationFileUriSet = false; michael@0: let destinationFileNameSet = false; michael@0: michael@0: function checkFinished() michael@0: { michael@0: if (titleSet && destinationFileUriSet && destinationFileNameSet) { michael@0: PlacesUtils.annotations.removeObserver(annoObserver); michael@0: PlacesUtils.history.removeObserver(historyObserver); michael@0: michael@0: promiseClearHistory().then(run_next_test); michael@0: } michael@0: }; michael@0: michael@0: let annoObserver = { michael@0: onPageAnnotationSet: function AO_onPageAnnotationSet(aPage, aName) michael@0: { michael@0: if (aPage.equals(SOURCE_URI)) { michael@0: let value = PlacesUtils.annotations.getPageAnnotation(aPage, aName); michael@0: switch (aName) michael@0: { michael@0: case "downloads/destinationFileURI": michael@0: destinationFileUriSet = true; michael@0: do_check_eq(value, destFileUri.spec); michael@0: break; michael@0: case "downloads/destinationFileName": michael@0: destinationFileNameSet = true; michael@0: do_check_eq(value, DEST_FILE_NAME); michael@0: break; michael@0: } michael@0: checkFinished(); michael@0: } michael@0: }, michael@0: onItemAnnotationSet: function() {}, michael@0: onPageAnnotationRemoved: function() {}, michael@0: onItemAnnotationRemoved: function() {} michael@0: } michael@0: michael@0: let historyObserver = { michael@0: onBeginUpdateBatch: function() {}, michael@0: onEndUpdateBatch: function() {}, michael@0: onVisit: function() {}, michael@0: onTitleChanged: function HO_onTitleChanged(aURI, aPageTitle) michael@0: { michael@0: if (aURI.equals(SOURCE_URI)) { michael@0: titleSet = true; michael@0: do_check_eq(aPageTitle, DEST_FILE_NAME); michael@0: checkFinished(); michael@0: } michael@0: }, michael@0: onDeleteURI: function() {}, michael@0: onClearHistory: function() {}, michael@0: onPageChanged: function() {}, michael@0: onDeleteVisits: function() {} michael@0: }; michael@0: michael@0: PlacesUtils.annotations.addObserver(annoObserver, false); michael@0: PlacesUtils.history.addObserver(historyObserver, false); michael@0: michael@0: // Both null values and remote URIs should not cause errors. michael@0: gDownloadHistory.addDownload(SOURCE_URI, null, Date.now() * 1000); michael@0: gDownloadHistory.addDownload(SOURCE_URI, null, Date.now() * 1000, null); michael@0: gDownloadHistory.addDownload(SOURCE_URI, null, Date.now() * 1000, REMOTE_URI); michael@0: michael@0: // Valid local file URIs should cause the download details to be saved. michael@0: gDownloadHistory.addDownload(SOURCE_URI, null, Date.now() * 1000, michael@0: destFileUri); michael@0: });