Bug 1406594 - Fix history.search and history.getVisited so that they find hidden items correctly. r?aswan
I had to add a mochitest because PlacesUtils.history.insertMany cannot create hidden redirects.
I also added a support for the "reload" transition while I am here.
MozReview-Commit-ID: ECQp6pW2r8v
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -16,16 +16,17 @@ var {
let nsINavHistoryService = Ci.nsINavHistoryService;
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
["link", nsINavHistoryService.TRANSITION_LINK],
["typed", nsINavHistoryService.TRANSITION_TYPED],
["auto_bookmark", nsINavHistoryService.TRANSITION_BOOKMARK],
["auto_subframe", nsINavHistoryService.TRANSITION_EMBED],
["manual_subframe", nsINavHistoryService.TRANSITION_FRAMED_LINK],
+ ["reload", nsINavHistoryService.TRANSITION_RELOAD],
]);
let TRANSITION_TYPE_TO_TRANSITIONS_MAP = new Map();
for (let [transition, transitionType] of TRANSITION_TO_TRANSITION_TYPES_MAP) {
TRANSITION_TYPE_TO_TRANSITIONS_MAP.set(transitionType, transition);
}
const getTransitionType = transition => {
@@ -184,16 +185,17 @@ this.history = class extends ExtensionAP
let endTime = (query.endTime == null) ?
Number.MAX_VALUE :
PlacesUtils.toPRTime(normalizeTime(query.endTime));
if (beginTime > endTime) {
return Promise.reject({message: "The startTime cannot be after the endTime"});
}
let options = PlacesUtils.history.getNewQueryOptions();
+ options.includeHidden = true;
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
options.maxResults = query.maxResults || 100;
let historyQuery = PlacesUtils.history.getNewQuery();
historyQuery.searchTerms = query.text;
historyQuery.beginTime = beginTime;
historyQuery.endTime = endTime;
let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
@@ -203,16 +205,17 @@ this.history = class extends ExtensionAP
getVisits: function(details) {
let url = details.url;
if (!url) {
return Promise.reject({message: "A URL must be provided for getVisits"});
}
let options = PlacesUtils.history.getNewQueryOptions();
+ options.includeHidden = true;
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
options.resultType = options.RESULTS_AS_VISIT;
let historyQuery = PlacesUtils.history.getNewQuery();
historyQuery.uri = Services.io.newURI(url);
let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToVisitItem);
return Promise.resolve(results);
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/mochitest/.eslintrc.js
@@ -0,0 +1,10 @@
+"use strict";
+
+module.exports = {
+ "extends": "plugin:mozilla/mochitest-test",
+
+ "env": {
+ "browser": true,
+ "webextensions": true,
+ },
+};
--- a/browser/components/extensions/test/mochitest/mochitest.ini
+++ b/browser/components/extensions/test/mochitest/mochitest.ini
@@ -1,7 +1,9 @@
[DEFAULT]
support-files =
../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
+ ../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
tags = webextensions
[test_ext_all_apis.html]
+[test_ext_history_redirect.html]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/mochitest/test_ext_history_redirect.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for simple WebExtension</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+const REDIRECT_URL = new URL("redirection.sjs", location).href;
+
+function waitForLoad(win) {
+ return new Promise(resolve => {
+ win.addEventListener("load", function() {
+ resolve();
+ }, {capture: true, once: true});
+ });
+}
+
+add_task(async function history_redirect() {
+ function background() {
+ browser.test.onMessage.addListener(async (msg, url) => {
+ switch (msg) {
+ case "delete-all": {
+ let results = await browser.history.deleteAll();
+ browser.test.sendMessage("delete-all-result", results);
+ break;
+ }
+ case "search": {
+ let results = await browser.history.search({text: url, 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 extensionData = {
+ manifest: {
+ permissions: [
+ "history",
+ ],
+ },
+ background,
+ };
+
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+
+ await Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+ info("extension loaded");
+
+ extension.sendMessage("delete-all");
+ await extension.awaitMessage("delete-all-result");
+
+ let win = window.open(REDIRECT_URL);
+ await waitForLoad(win);
+
+ extension.sendMessage("search", REDIRECT_URL);
+ let results = await extension.awaitMessage("search-result");
+ is(results.length, 1, "search returned expected length of results");
+
+ extension.sendMessage("get-visits", REDIRECT_URL);
+ let visits = await extension.awaitMessage("get-visits-result");
+ is(visits.length, 1, "getVisits returned expected length of visits");
+
+ // cleanup phase
+ win.close();
+
+ await extension.unload();
+ info("extension unloaded");
+});
+</script>
+
+</body>
+</html>
--- a/browser/components/extensions/test/xpcshell/test_ext_history.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_history.js
@@ -399,16 +399,93 @@ add_task(async function test_get_visits(
await PlacesTestUtils.clearHistory();
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::UpdatePlaces
+ // 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 PlacesTestUtils.clearHistory();
+ 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 = [
{