/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
import {
classMap,
html,
ifDefined,
when,
nothing,
} from "chrome://global/content/vendor/lit.all.mjs";
import { navigateToLink } from "chrome://browser/content/firefoxview/helpers.mjs";
import { SidebarPage } from "./sidebar-page.mjs";
ChromeUtils.defineESModuleGetters(lazy, {
HistoryController: "resource:///modules/HistoryController.sys.mjs",
Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
SidebarTreeView:
"moz-src:///browser/components/sidebar/SidebarTreeView.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
});
const NEVER_REMEMBER_HISTORY_PREF = "browser.privatebrowsing.autostart";
const DAYS_EXPANDED_INITIALLY = 2;
export class SidebarHistory extends SidebarPage {
static queries = {
cards: { all: "moz-card" },
emptyState: "fxview-empty-state",
lists: { all: "sidebar-tab-list" },
menuButton: ".menu-button",
searchTextbox: "moz-input-search",
};
constructor() {
super();
this.handlePopupEvent = this.handlePopupEvent.bind(this);
this.controller = new lazy.HistoryController(this, {
component: "sidebar",
});
this.treeView = new lazy.SidebarTreeView(this);
}
connectedCallback() {
super.connectedCallback();
const { document: doc } = this.topWindow;
this._menu = doc.getElementById("sidebar-history-menu");
this._menuSortByDate = doc.getElementById("sidebar-history-sort-by-date");
this._menuSortBySite = doc.getElementById("sidebar-history-sort-by-site");
this._menuSortByDateSite = doc.getElementById(
"sidebar-history-sort-by-date-and-site"
);
this._menuSortByLastVisited = doc.getElementById(
"sidebar-history-sort-by-last-visited"
);
this._menu.addEventListener("command", this);
this._menu.addEventListener("popuphidden", this.handlePopupEvent);
this._contextMenu.addEventListener("popupshowing", this);
this.addContextMenuListeners();
this.addSidebarFocusedListeners();
this.controller.updateCache();
}
disconnectedCallback() {
super.disconnectedCallback();
this._menu.removeEventListener("command", this);
this._menu.removeEventListener("popuphidden", this.handlePopupEvent);
this._contextMenu.removeEventListener("popupshowing", this);
this.removeContextMenuListeners();
this.removeSidebarFocusedListeners();
}
handleEvent(e) {
switch (e.type) {
case "popupshowing":
this.updateContextMenu();
break;
default:
super.handleEvent(e);
}
}
get isMultipleRowsSelected() {
return !!this.treeView.selectedLists.size;
}
/**
* Only show multiselect commands when multiple items are selected.
*/
updateContextMenu() {
for (const child of this._contextMenu.children) {
const isMultiSelectCommand = child.classList.contains(
"sidebar-history-multiselect-command"
);
if (this.isMultipleRowsSelected) {
child.hidden = !isMultiSelectCommand;
} else {
child.hidden = isMultiSelectCommand;
}
}
}
handleContextMenuEvent(e) {
this.triggerNode =
this.findTriggerNode(e, "sidebar-tab-row") ||
this.findTriggerNode(e, "moz-input-search");
if (!this.triggerNode) {
e.preventDefault();
}
}
handleCommandEvent(e) {
switch (e.target.id) {
case "sidebar-history-sort-by-date":
this.controller.onChangeSortOption(e, "date");
break;
case "sidebar-history-sort-by-site":
this.controller.onChangeSortOption(e, "site");
break;
case "sidebar-history-sort-by-date-and-site":
this.controller.onChangeSortOption(e, "datesite");
break;
case "sidebar-history-sort-by-last-visited":
this.controller.onChangeSortOption(e, "lastvisited");
break;
case "sidebar-history-clear":
lazy.Sanitizer.showUI(this.topWindow);
break;
case "sidebar-history-context-delete-page":
this.controller.deleteFromHistory().catch(console.error);
break;
case "sidebar-history-context-delete-pages":
this.#deleteMultipleFromHistory().catch(console.error);
break;
default:
super.handleCommandEvent(e);
break;
}
}
#deleteMultipleFromHistory() {
const pageGuids = [...this.treeView.selectedLists].flatMap(
({ selectedGuids }) => [...selectedGuids]
);
return lazy.PlacesUtils.history.remove(pageGuids);
}
// We should let moz-button handle this, see bug 1875374.
handlePopupEvent(e) {
if (e.type == "popuphidden") {
this.menuButton.setAttribute("aria-expanded", false);
}
}
handleSidebarFocusedEvent() {
this.searchTextbox?.focus();
}
onPrimaryAction(e) {
if (this.isMultipleRowsSelected) {
// Avoid opening multiple links at once.
return;
}
navigateToLink(e);
this.treeView.clearSelection();
}
onSecondaryAction(e) {
this.triggerNode = e.detail.item;
this.controller.deleteFromHistory().catch(console.error);
}
/**
* The template to use for cards-container.
*/
get cardsTemplate() {
if (this.controller.isHistoryPending) {
// don't render cards until initial history visits entries are available
return "";
} else if (this.controller.searchResults) {
return this.#searchResultsTemplate();
} else if (!this.controller.isHistoryEmpty) {
return this.#historyCardsTemplate();
}
return this.#emptyMessageTemplate();
}
#historyCardsTemplate() {
const { historyVisits } = this.controller;
switch (this.controller.sortOption) {
case "date":
return historyVisits.map(({ l10nId, items }, i) =>
this.#dateCardTemplate(l10nId, i, items)
);
case "site":
return historyVisits.map(({ domain, items }, i) =>
this.#siteCardTemplate(domain, i, items)
);
case "datesite":
return historyVisits.map(({ l10nId, items }, i) =>
this.#dateCardTemplate(l10nId, i, items, true)
);
case "lastvisited":
return historyVisits.map(
({ items }) =>
html`