From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../extensions/test/xpcshell/test_ext_history.js | 864 +++++++++++++++++++++ 1 file changed, 864 insertions(+) create mode 100644 browser/components/extensions/test/xpcshell/test_ext_history.js (limited to 'browser/components/extensions/test/xpcshell/test_ext_history.js') diff --git a/browser/components/extensions/test/xpcshell/test_ext_history.js b/browser/components/extensions/test/xpcshell/test_ext_history.js new file mode 100644 index 0000000000..c0f6c39be7 --- /dev/null +++ b/browser/components/extensions/test/xpcshell/test_ext_history.js @@ -0,0 +1,864 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + ExtensionCommon: "resource://gre/modules/ExtensionCommon.sys.mjs", + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", +}); + +AddonTestUtils.init(this); +AddonTestUtils.overrideCertDB(); +AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "43" +); + +add_task(async function test_delete() { + function background() { + let historyClearedCount = 0; + let removedUrls = []; + + browser.history.onVisitRemoved.addListener(data => { + if (data.allHistory) { + historyClearedCount++; + browser.test.assertEq( + 0, + data.urls.length, + "onVisitRemoved received an empty urls array" + ); + } else { + removedUrls.push(...data.urls); + } + }); + + browser.test.onMessage.addListener((msg, arg) => { + if (msg === "delete-url") { + browser.history.deleteUrl({ url: arg }).then(result => { + browser.test.assertEq( + undefined, + result, + "browser.history.deleteUrl returns nothing" + ); + browser.test.sendMessage("url-deleted"); + }); + } else if (msg === "delete-range") { + browser.history.deleteRange(arg).then(result => { + browser.test.assertEq( + undefined, + result, + "browser.history.deleteRange returns nothing" + ); + browser.test.sendMessage("range-deleted", removedUrls); + }); + } else if (msg === "delete-all") { + browser.history.deleteAll().then(result => { + browser.test.assertEq( + undefined, + result, + "browser.history.deleteAll returns nothing" + ); + browser.test.sendMessage("history-cleared", [ + historyClearedCount, + removedUrls, + ]); + }); + } + }); + + browser.test.sendMessage("ready"); + } + + const BASE_URL = "http://mozilla.com/test_history/"; + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background: `(${background})()`, + }); + + await extension.startup(); + await extension.awaitMessage("ready"); + await PlacesUtils.history.clear(); + + let historyClearedCount; + let visits = []; + let visitDate = new Date(1999, 9, 9, 9, 9).getTime(); + + function pushVisit(subvisits) { + visitDate += 1000; + subvisits.push({ date: new Date(visitDate) }); + } + + // Add 5 visits for one uri and 3 visits for 3 others + for (let i = 0; i < 4; ++i) { + let visit = { + url: `${BASE_URL}${i}`, + title: "visit " + i, + visits: [], + }; + if (i === 0) { + for (let j = 0; j < 5; ++j) { + pushVisit(visit.visits); + } + } else { + pushVisit(visit.visits); + } + visits.push(visit); + } + + await PlacesUtils.history.insertMany(visits); + equal( + await PlacesTestUtils.visitsInDB(visits[0].url), + 5, + "5 visits for uri found in history database" + ); + + let testUrl = visits[2].url; + ok( + await PlacesTestUtils.isPageInDB(testUrl), + "expected url found in history database" + ); + + extension.sendMessage("delete-url", testUrl); + await extension.awaitMessage("url-deleted"); + equal( + await PlacesTestUtils.isPageInDB(testUrl), + false, + "expected url not found in history database" + ); + + // delete 3 of the 5 visits for url 1 + let filter = { + startTime: visits[0].visits[0].date, + endTime: visits[0].visits[2].date, + }; + + extension.sendMessage("delete-range", filter); + let removedUrls = await extension.awaitMessage("range-deleted"); + ok( + !removedUrls.includes(visits[0].url), + `${visits[0].url} not received by onVisitRemoved` + ); + ok( + await PlacesTestUtils.isPageInDB(visits[0].url), + "expected uri found in history database" + ); + equal( + await PlacesTestUtils.visitsInDB(visits[0].url), + 2, + "2 visits for uri found in history database" + ); + ok( + await PlacesTestUtils.isPageInDB(visits[1].url), + "expected uri found in history database" + ); + equal( + await PlacesTestUtils.visitsInDB(visits[1].url), + 1, + "1 visit for uri found in history database" + ); + + // delete the rest of the visits for url 1, and the visit for url 2 + filter.startTime = visits[0].visits[0].date; + filter.endTime = visits[1].visits[0].date; + + extension.sendMessage("delete-range", filter); + await extension.awaitMessage("range-deleted"); + + equal( + await PlacesTestUtils.isPageInDB(visits[0].url), + false, + "expected uri not found in history database" + ); + equal( + await PlacesTestUtils.visitsInDB(visits[0].url), + 0, + "0 visits for uri found in history database" + ); + equal( + await PlacesTestUtils.isPageInDB(visits[1].url), + false, + "expected uri not found in history database" + ); + equal( + await PlacesTestUtils.visitsInDB(visits[1].url), + 0, + "0 visits for uri found in history database" + ); + + ok( + await PlacesTestUtils.isPageInDB(visits[3].url), + "expected uri found in history database" + ); + + extension.sendMessage("delete-all"); + [historyClearedCount, removedUrls] = await extension.awaitMessage( + "history-cleared" + ); + equal( + historyClearedCount, + 2, + "onVisitRemoved called for each clearing of history" + ); + equal( + removedUrls.length, + 3, + "onVisitRemoved called the expected number of times" + ); + for (let i = 1; i < 3; ++i) { + let url = visits[i].url; + ok(removedUrls.includes(url), `${url} received by onVisitRemoved`); + } + await extension.unload(); +}); + +const SINGLE_VISIT_URL = "http://example.com/"; +const DOUBLE_VISIT_URL = "http://example.com/2/"; +const MOZILLA_VISIT_URL = "http://mozilla.com/"; +const REFERENCE_DATE = new Date(); +// pages/visits to add via History.insert +const PAGE_INFOS = [ + { + url: SINGLE_VISIT_URL, + title: `test visit for ${SINGLE_VISIT_URL}`, + visits: [{ date: new Date(Number(REFERENCE_DATE) - 1000) }], + }, + { + url: DOUBLE_VISIT_URL, + title: `test visit for ${DOUBLE_VISIT_URL}`, + visits: [ + { date: REFERENCE_DATE }, + { date: new Date(Number(REFERENCE_DATE) - 2000) }, + ], + }, + { + url: MOZILLA_VISIT_URL, + title: `test visit for ${MOZILLA_VISIT_URL}`, + visits: [{ date: new Date(Number(REFERENCE_DATE) - 3000) }], + }, +]; + +add_task(async function test_search() { + function background(BGSCRIPT_REFERENCE_DATE) { + const futureTime = Date.now() + 24 * 60 * 60 * 1000; + + browser.test.onMessage.addListener(msg => { + browser.history + .search({ text: "" }) + .then(results => { + browser.test.sendMessage("empty-search", results); + return browser.history.search({ text: "mozilla.com" }); + }) + .then(results => { + browser.test.sendMessage("text-search", results); + return browser.history.search({ text: "example.com", maxResults: 1 }); + }) + .then(results => { + browser.test.sendMessage("max-results-search", results); + return browser.history.search({ + text: "", + startTime: BGSCRIPT_REFERENCE_DATE - 2000, + endTime: BGSCRIPT_REFERENCE_DATE - 1000, + }); + }) + .then(results => { + browser.test.sendMessage("date-range-search", results); + return browser.history.search({ text: "", startTime: futureTime }); + }) + .then(results => { + browser.test.assertEq( + 0, + results.length, + "no results returned for late start time" + ); + return browser.history.search({ text: "", endTime: 0 }); + }) + .then(results => { + browser.test.assertEq( + 0, + results.length, + "no results returned for early end time" + ); + return browser.history.search({ + text: "", + startTime: Date.now(), + endTime: 0, + }); + }) + .then( + results => { + browser.test.fail( + "history.search rejects with startTime that is after the endTime" + ); + }, + error => { + browser.test.assertEq( + "The startTime cannot be after the endTime", + error.message, + "history.search rejects with startTime that is after the endTime" + ); + } + ) + .then(() => { + browser.test.notifyPass("search"); + }); + }); + + browser.test.sendMessage("ready"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background: `(${background})(${Number(REFERENCE_DATE)})`, + }); + + function findResult(url, results) { + return results.find(r => r.url === url); + } + + function checkResult(results, url, expectedCount) { + let result = findResult(url, results); + notEqual(result, null, `history.search result was found for ${url}`); + equal( + result.visitCount, + expectedCount, + `history.search reports ${expectedCount} visit(s)` + ); + equal( + result.title, + `test visit for ${url}`, + "title for search result is correct" + ); + } + + await extension.startup(); + await extension.awaitMessage("ready"); + await PlacesUtils.history.clear(); + + await PlacesUtils.history.insertMany(PAGE_INFOS); + + extension.sendMessage("check-history"); + + let results = await extension.awaitMessage("empty-search"); + equal(results.length, 3, "history.search with empty text returned 3 results"); + checkResult(results, SINGLE_VISIT_URL, 1); + checkResult(results, DOUBLE_VISIT_URL, 2); + checkResult(results, MOZILLA_VISIT_URL, 1); + + results = await extension.awaitMessage("text-search"); + equal( + results.length, + 1, + "history.search with specific text returned 1 result" + ); + checkResult(results, MOZILLA_VISIT_URL, 1); + + results = await extension.awaitMessage("max-results-search"); + equal(results.length, 1, "history.search with maxResults returned 1 result"); + checkResult(results, DOUBLE_VISIT_URL, 2); + + results = await extension.awaitMessage("date-range-search"); + equal( + results.length, + 2, + "history.search with a date range returned 2 result" + ); + checkResult(results, DOUBLE_VISIT_URL, 2); + checkResult(results, SINGLE_VISIT_URL, 1); + + await extension.awaitFinish("search"); + await extension.unload(); + await PlacesUtils.history.clear(); +}); + +add_task(async function test_add_url() { + function background() { + const TEST_DOMAIN = "http://example.com/"; + + browser.test.onMessage.addListener((msg, testData) => { + let [details, type] = testData; + details.url = details.url || `${TEST_DOMAIN}${type}`; + if (msg === "add-url") { + details.title = `Title for ${type}`; + browser.history + .addUrl(details) + .then(() => { + return browser.history.search({ text: details.url }); + }) + .then(results => { + browser.test.assertEq( + 1, + results.length, + "1 result found when searching for added URL" + ); + browser.test.sendMessage("url-added", { + details, + result: results[0], + }); + }); + } else if (msg === "expect-failure") { + let expectedMsg = testData[2]; + browser.history.addUrl(details).then( + () => { + browser.test.fail(`Expected error thrown for ${type}`); + }, + error => { + browser.test.assertTrue( + error.message.includes(expectedMsg), + `"Expected error thrown when trying to add a URL with ${type}` + ); + browser.test.sendMessage("add-failed"); + } + ); + } + }); + + browser.test.sendMessage("ready"); + } + + let addTestData = [ + [{}, "default"], + [{ visitTime: new Date() }, "with_date"], + [{ visitTime: Date.now() }, "with_ms_number"], + [{ visitTime: new Date().toISOString() }, "with_iso_string"], + [{ transition: "typed" }, "valid_transition"], + ]; + + let failTestData = [ + [ + { transition: "generated" }, + "an invalid transition", + "|generated| is not a supported transition for history", + ], + [{ visitTime: Date.now() + 1000000 }, "a future date", "Invalid value"], + [{ url: "about.config" }, "an invalid url", "Invalid value"], + ]; + + async function checkUrl(results) { + ok( + await PlacesTestUtils.isPageInDB(results.details.url), + `${results.details.url} found in history database` + ); + ok( + PlacesUtils.isValidGuid(results.result.id), + "URL was added with a valid id" + ); + equal( + results.result.title, + results.details.title, + "URL was added with the correct title" + ); + if (results.details.visitTime) { + equal( + results.result.lastVisitTime, + Number(ExtensionCommon.normalizeTime(results.details.visitTime)), + "URL was added with the correct date" + ); + } + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background: `(${background})()`, + }); + + await PlacesUtils.history.clear(); + await extension.startup(); + await extension.awaitMessage("ready"); + + for (let data of addTestData) { + extension.sendMessage("add-url", data); + let results = await extension.awaitMessage("url-added"); + await checkUrl(results); + } + + for (let data of failTestData) { + extension.sendMessage("expect-failure", data); + await extension.awaitMessage("add-failed"); + } + + await extension.unload(); +}); + +add_task(async function test_get_visits() { + async function background() { + const TEST_DOMAIN = "http://example.com/"; + const FIRST_DATE = Date.now(); + const INITIAL_DETAILS = { + url: TEST_DOMAIN, + visitTime: FIRST_DATE, + transition: "link", + }; + + let visitIds = new Set(); + + async function checkVisit(visit, expected) { + visitIds.add(visit.visitId); + browser.test.assertEq( + expected.visitTime, + visit.visitTime, + "visit has the correct visitTime" + ); + browser.test.assertEq( + expected.transition, + visit.transition, + "visit has the correct transition" + ); + let results = await browser.history.search({ text: expected.url }); + // all results will have the same id, so we only need to use the first one + browser.test.assertEq( + results[0].id, + visit.id, + "visit has the correct id" + ); + } + + let details = Object.assign({}, INITIAL_DETAILS); + + await browser.history.addUrl(details); + let results = await browser.history.getVisits({ url: details.url }); + + browser.test.assertEq( + 1, + results.length, + "the expected number of visits were returned" + ); + await checkVisit(results[0], details); + + details.url = `${TEST_DOMAIN}/1/`; + await browser.history.addUrl(details); + + results = await browser.history.getVisits({ url: details.url }); + browser.test.assertEq( + 1, + results.length, + "the expected number of visits were returned" + ); + await checkVisit(results[0], details); + + details.visitTime = FIRST_DATE - 1000; + details.transition = "typed"; + await browser.history.addUrl(details); + results = await browser.history.getVisits({ url: details.url }); + + browser.test.assertEq( + 2, + results.length, + "the expected number of visits were returned" + ); + await checkVisit(results[0], INITIAL_DETAILS); + await checkVisit(results[1], details); + browser.test.assertEq(3, visitIds.size, "each visit has a unique visitId"); + await browser.test.notifyPass("get-visits"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background: `(${background})()`, + }); + + await PlacesUtils.history.clear(); + await extension.startup(); + + await extension.awaitFinish("get-visits"); + await extension.unload(); +}); + +add_task(async function test_transition_types() { + const VISIT_URL_PREFIX = "http://example.com/"; + const TRANSITIONS = [ + ["link", Ci.nsINavHistoryService.TRANSITION_LINK], + ["typed", Ci.nsINavHistoryService.TRANSITION_TYPED], + ["auto_bookmark", Ci.nsINavHistoryService.TRANSITION_BOOKMARK], + // Only session history contains TRANSITION_EMBED visits, + // So global history query cannot find them. + // ["auto_subframe", Ci.nsINavHistoryService.TRANSITION_EMBED], + // Redirects are not correctly tested here because History + // will not make redirect entries hidden. + ["link", Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT], + ["link", Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY], + ["link", Ci.nsINavHistoryService.TRANSITION_DOWNLOAD], + ["manual_subframe", Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK], + ["reload", Ci.nsINavHistoryService.TRANSITION_RELOAD], + ]; + + // pages/visits to add via History.insertMany + let pageInfos = []; + let visitDate = new Date(1999, 9, 9, 9, 9).getTime(); + for (let [, transitionType] of TRANSITIONS) { + pageInfos.push({ + url: VISIT_URL_PREFIX + transitionType + "/", + visits: [ + { transition: transitionType, date: new Date((visitDate -= 1000)) }, + ], + }); + } + + function background() { + browser.test.onMessage.addListener(async (msg, url) => { + switch (msg) { + case "search": { + let results = await browser.history.search({ + text: "", + startTime: new Date(0), + }); + browser.test.sendMessage("search-result", results); + break; + } + case "get-visits": { + let results = await browser.history.getVisits({ url }); + browser.test.sendMessage("get-visits-result", results); + break; + } + } + }); + + browser.test.sendMessage("ready"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background, + }); + + await PlacesUtils.history.clear(); + await extension.startup(); + await extension.awaitMessage("ready"); + + await PlacesUtils.history.insertMany(pageInfos); + + extension.sendMessage("search"); + let results = await extension.awaitMessage("search-result"); + equal( + results.length, + pageInfos.length, + "search returned expected length of results" + ); + for (let i = 0; i < pageInfos.length; ++i) { + equal(results[i].url, pageInfos[i].url, "search returned the expected url"); + + extension.sendMessage("get-visits", pageInfos[i].url); + let visits = await extension.awaitMessage("get-visits-result"); + equal(visits.length, 1, "getVisits returned expected length of visits"); + equal( + visits[0].transition, + TRANSITIONS[i][0], + "getVisits returned the expected transition" + ); + } + + await extension.unload(); +}); + +add_task(async function test_on_visited() { + const SINGLE_VISIT_URL = "http://example.com/1/"; + const DOUBLE_VISIT_URL = "http://example.com/2/"; + let visitDate = new Date(1999, 9, 9, 9, 9).getTime(); + + // pages/visits to add via History.insertMany + const PAGE_INFOS = [ + { + url: SINGLE_VISIT_URL, + title: `visit to ${SINGLE_VISIT_URL}`, + visits: [{ date: new Date(visitDate) }], + }, + { + url: DOUBLE_VISIT_URL, + title: `visit to ${DOUBLE_VISIT_URL}`, + visits: [ + { date: new Date((visitDate += 1000)) }, + { date: new Date((visitDate += 1000)) }, + ], + }, + { + url: SINGLE_VISIT_URL, + title: "Title Changed", + visits: [{ date: new Date(visitDate) }], + }, + ]; + + function background() { + let onVisitedData = []; + + browser.history.onVisited.addListener(data => { + if (data.url.includes("moz-extension")) { + return; + } + onVisitedData.push(data); + if (onVisitedData.length == 4) { + browser.test.sendMessage("on-visited-data", onVisitedData); + } + }); + + // Verifying onTitleChange Event along with onVisited event + browser.history.onTitleChanged.addListener(data => { + browser.test.sendMessage("on-title-changed-data", data); + }); + + browser.test.sendMessage("ready"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["history"], + }, + background: `(${background})()`, + }); + + await PlacesUtils.history.clear(); + await extension.startup(); + await extension.awaitMessage("ready"); + + await PlacesUtils.history.insertMany(PAGE_INFOS); + + let onVisitedData = await extension.awaitMessage("on-visited-data"); + + function checkOnVisitedData(index, expected) { + let onVisited = onVisitedData[index]; + ok(PlacesUtils.isValidGuid(onVisited.id), "onVisited received a valid id"); + equal(onVisited.url, expected.url, "onVisited received the expected url"); + equal( + onVisited.title, + expected.title, + "onVisited received the expected title" + ); + equal( + onVisited.lastVisitTime, + expected.time, + "onVisited received the expected time" + ); + equal( + onVisited.visitCount, + expected.visitCount, + "onVisited received the expected visitCount" + ); + } + + let expected = { + url: PAGE_INFOS[0].url, + title: PAGE_INFOS[0].title, + time: PAGE_INFOS[0].visits[0].date.getTime(), + visitCount: 1, + }; + checkOnVisitedData(0, expected); + + expected.url = PAGE_INFOS[1].url; + expected.title = PAGE_INFOS[1].title; + expected.time = PAGE_INFOS[1].visits[0].date.getTime(); + checkOnVisitedData(1, expected); + + expected.time = PAGE_INFOS[1].visits[1].date.getTime(); + expected.visitCount = 2; + checkOnVisitedData(2, expected); + + expected.url = PAGE_INFOS[2].url; + expected.title = PAGE_INFOS[2].title; + expected.time = PAGE_INFOS[2].visits[0].date.getTime(); + expected.visitCount = 2; + checkOnVisitedData(3, expected); + + let onTitleChangedData = await extension.awaitMessage( + "on-title-changed-data" + ); + Assert.deepEqual( + { + id: onVisitedData[3].id, + url: SINGLE_VISIT_URL, + title: "Title Changed", + }, + onTitleChangedData, + "expected event data for onTitleChanged" + ); + + await extension.unload(); +}); + +add_task( + { + pref_set: [["extensions.eventPages.enabled", true]], + }, + async function test_history_event_page() { + await AddonTestUtils.promiseStartupManager(); + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + browser_specific_settings: { gecko: { id: "eventpage@history" } }, + permissions: ["history"], + background: { persistent: false }, + }, + background() { + browser.history.onVisited.addListener(() => { + browser.test.sendMessage("onVisited"); + }); + browser.history.onVisitRemoved.addListener(() => { + browser.test.sendMessage("onVisitRemoved"); + }); + browser.history.onTitleChanged.addListener(() => {}); + browser.test.sendMessage("ready"); + }, + }); + + const EVENTS = ["onVisited", "onVisitRemoved", "onTitleChanged"]; + await PlacesUtils.history.clear(); + + await extension.startup(); + await extension.awaitMessage("ready"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: false, + }); + } + + // test events waken background + await extension.terminateBackground(); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: true, + }); + } + + await PlacesUtils.history.insertMany(PAGE_INFOS); + + await extension.awaitMessage("ready"); + await extension.awaitMessage("onVisited"); + ok(true, "persistent event woke background"); + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: false, + }); + } + + await AddonTestUtils.promiseRestartManager(); + await extension.awaitStartup(); + + for (let event of EVENTS) { + assertPersistentListeners(extension, "history", event, { + primed: true, + }); + } + + await PlacesUtils.history.clear(); + await extension.awaitMessage("ready"); + await extension.awaitMessage("onVisitRemoved"); + + await extension.unload(); + await AddonTestUtils.promiseShutdownManager(); + } +); -- cgit v1.2.3