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 --- .../preferences/dialogs/siteDataSettings.js | 335 +++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 browser/components/preferences/dialogs/siteDataSettings.js (limited to 'browser/components/preferences/dialogs/siteDataSettings.js') diff --git a/browser/components/preferences/dialogs/siteDataSettings.js b/browser/components/preferences/dialogs/siteDataSettings.js new file mode 100644 index 0000000000..7c0c490e5d --- /dev/null +++ b/browser/components/preferences/dialogs/siteDataSettings.js @@ -0,0 +1,335 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ +/* 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/. */ + +"use strict"; + +var { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +ChromeUtils.defineModuleGetter( + this, + "SiteDataManager", + "resource:///modules/SiteDataManager.jsm" +); +ChromeUtils.defineESModuleGetters(this, { + DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", +}); + +let gSiteDataSettings = { + // Array of metadata of sites. Each array element is object holding: + // - uri: uri of site; instance of nsIURI + // - baseDomain: base domain of the site + // - cookies: array of cookies of that site + // - usage: disk usage which site uses + // - userAction: "remove" or "update-permission"; the action user wants to take. + _sites: null, + + _list: null, + _searchBox: null, + + _createSiteListItem(site) { + let item = document.createXULElement("richlistitem"); + item.setAttribute("host", site.baseDomain); + let container = document.createXULElement("hbox"); + + // Creates a new column item with the specified relative width. + function addColumnItem(l10n, flexWidth, tooltipText) { + let box = document.createXULElement("hbox"); + box.className = "item-box"; + box.setAttribute("style", `flex: ${flexWidth} ${flexWidth};`); + let label = document.createXULElement("label"); + label.setAttribute("crop", "end"); + if (l10n) { + if (l10n.hasOwnProperty("raw")) { + box.setAttribute("tooltiptext", l10n.raw); + label.setAttribute("value", l10n.raw); + } else { + document.l10n.setAttributes(label, l10n.id, l10n.args); + } + } + if (tooltipText) { + box.setAttribute("tooltiptext", tooltipText); + } + box.appendChild(label); + container.appendChild(box); + } + + // Add "Host" column. + let hostData = site.baseDomain + ? { raw: site.baseDomain } + : { id: "site-data-local-file-host" }; + addColumnItem(hostData, "4"); + + // Add "Cookies" column. + addColumnItem({ raw: site.cookies.length }, "1"); + + // Add "Storage" column + if (site.usage > 0 || site.persisted) { + let [value, unit] = DownloadUtils.convertByteUnits(site.usage); + let strName = site.persisted + ? "site-storage-persistent" + : "site-storage-usage"; + addColumnItem( + { + id: strName, + args: { value, unit }, + }, + "2" + ); + } else { + // Pass null to avoid showing "0KB" when there is no site data stored. + addColumnItem(null, "2"); + } + + // Add "Last Used" column. + let formattedLastAccessed = + site.lastAccessed > 0 + ? this._relativeTimeFormat.formatBestUnit(site.lastAccessed) + : null; + let formattedFullDate = + site.lastAccessed > 0 + ? this._absoluteTimeFormat.format(site.lastAccessed) + : null; + addColumnItem( + site.lastAccessed > 0 ? { raw: formattedLastAccessed } : null, + "2", + formattedFullDate + ); + + item.appendChild(container); + return item; + }, + + init() { + function setEventListener(id, eventType, callback) { + document + .getElementById(id) + .addEventListener(eventType, callback.bind(gSiteDataSettings)); + } + + this._absoluteTimeFormat = new Services.intl.DateTimeFormat(undefined, { + dateStyle: "short", + timeStyle: "short", + }); + + this._relativeTimeFormat = new Services.intl.RelativeTimeFormat( + undefined, + {} + ); + + this._list = document.getElementById("sitesList"); + this._searchBox = document.getElementById("searchBox"); + SiteDataManager.getSites().then(sites => { + this._sites = sites; + let sortCol = document.querySelector( + "treecol[data-isCurrentSortCol=true]" + ); + this._sortSites(this._sites, sortCol); + this._buildSitesList(this._sites); + Services.obs.notifyObservers(null, "sitedata-settings-init"); + }); + + setEventListener("sitesList", "select", this.onSelect); + setEventListener("hostCol", "click", this.onClickTreeCol); + setEventListener("usageCol", "click", this.onClickTreeCol); + setEventListener("lastAccessedCol", "click", this.onClickTreeCol); + setEventListener("cookiesCol", "click", this.onClickTreeCol); + setEventListener("searchBox", "command", this.onCommandSearch); + setEventListener("removeAll", "command", this.onClickRemoveAll); + setEventListener("removeSelected", "command", this.removeSelected); + + document.addEventListener("dialogaccept", e => this.saveChanges(e)); + }, + + _updateButtonsState() { + let items = this._list.getElementsByTagName("richlistitem"); + let removeSelectedBtn = document.getElementById("removeSelected"); + let removeAllBtn = document.getElementById("removeAll"); + removeSelectedBtn.disabled = !this._list.selectedItems.length; + removeAllBtn.disabled = !items.length; + + let l10nId = this._searchBox.value + ? "site-data-remove-shown" + : "site-data-remove-all"; + document.l10n.setAttributes(removeAllBtn, l10nId); + }, + + /** + * @param sites {Array} + * @param col {XULElement} the being sorted on + */ + _sortSites(sites, col) { + let isCurrentSortCol = col.getAttribute("data-isCurrentSortCol"); + let sortDirection = + col.getAttribute("data-last-sortDirection") || "ascending"; + if (isCurrentSortCol) { + // Sort on the current column, flip the sorting direction + sortDirection = + sortDirection === "ascending" ? "descending" : "ascending"; + } + + let sortFunc = null; + switch (col.id) { + case "hostCol": + sortFunc = (a, b) => { + let aHost = a.baseDomain.toLowerCase(); + let bHost = b.baseDomain.toLowerCase(); + return aHost.localeCompare(bHost); + }; + break; + + case "cookiesCol": + sortFunc = (a, b) => a.cookies.length - b.cookies.length; + break; + + case "usageCol": + sortFunc = (a, b) => a.usage - b.usage; + break; + + case "lastAccessedCol": + sortFunc = (a, b) => a.lastAccessed - b.lastAccessed; + break; + } + if (sortDirection === "descending") { + sites.sort((a, b) => sortFunc(b, a)); + } else { + sites.sort(sortFunc); + } + + let cols = this._list.previousElementSibling.querySelectorAll("treecol"); + cols.forEach(c => { + c.removeAttribute("sortDirection"); + c.removeAttribute("data-isCurrentSortCol"); + }); + col.setAttribute("data-isCurrentSortCol", true); + col.setAttribute("sortDirection", sortDirection); + col.setAttribute("data-last-sortDirection", sortDirection); + }, + + /** + * @param sites {Array} array of metadata of sites + */ + _buildSitesList(sites) { + // Clear old entries. + let oldItems = this._list.querySelectorAll("richlistitem"); + for (let item of oldItems) { + item.remove(); + } + + let keyword = this._searchBox.value.toLowerCase().trim(); + let fragment = document.createDocumentFragment(); + for (let site of sites) { + if (keyword && !site.baseDomain.includes(keyword)) { + continue; + } + + if (site.userAction === "remove") { + continue; + } + + let item = this._createSiteListItem(site); + fragment.appendChild(item); + } + this._list.appendChild(fragment); + this._updateButtonsState(); + }, + + _removeSiteItems(items) { + for (let i = items.length - 1; i >= 0; --i) { + let item = items[i]; + let baseDomain = item.getAttribute("host"); + let siteForBaseDomain = this._sites.find( + site => site.baseDomain == baseDomain + ); + if (siteForBaseDomain) { + siteForBaseDomain.userAction = "remove"; + } + item.remove(); + } + this._updateButtonsState(); + }, + + async saveChanges(event) { + let removals = this._sites + .filter(site => site.userAction == "remove") + .map(site => site.baseDomain); + + if (removals.length) { + let removeAll = removals.length == this._sites.length; + let promptArg = removeAll ? undefined : removals; + if (!SiteDataManager.promptSiteDataRemoval(window, promptArg)) { + // If the user cancelled the confirm dialog keep the site data window open, + // they can still press cancel again to exit. + event.preventDefault(); + return; + } + try { + if (removeAll) { + await SiteDataManager.removeAll(); + } else { + await SiteDataManager.remove(removals); + } + } catch (e) { + console.error(e); + } + } + }, + + removeSelected() { + let lastIndex = this._list.selectedItems.length - 1; + let lastSelectedItem = this._list.selectedItems[lastIndex]; + let lastSelectedItemPosition = this._list.getIndexOfItem(lastSelectedItem); + let nextSelectedItem = this._list.getItemAtIndex( + lastSelectedItemPosition + 1 + ); + + this._removeSiteItems(this._list.selectedItems); + this._list.clearSelection(); + + if (nextSelectedItem) { + this._list.selectedItem = nextSelectedItem; + } else { + this._list.selectedIndex = this._list.itemCount - 1; + } + }, + + onClickTreeCol(e) { + this._sortSites(this._sites, e.target); + this._buildSitesList(this._sites); + this._list.clearSelection(); + }, + + onCommandSearch() { + this._buildSitesList(this._sites); + this._list.clearSelection(); + }, + + onClickRemoveAll() { + let siteItems = this._list.getElementsByTagName("richlistitem"); + if (siteItems.length) { + this._removeSiteItems(siteItems); + } + }, + + onKeyPress(e) { + if ( + e.keyCode == KeyEvent.DOM_VK_DELETE || + (AppConstants.platform == "macosx" && + e.keyCode == KeyEvent.DOM_VK_BACK_SPACE) + ) { + if (!e.target.closest("#sitesList")) { + // The user is typing or has not selected an item from the list to remove + return; + } + // The users intention is to delete site data + this.removeSelected(); + } + }, + + onSelect() { + this._updateButtonsState(); + }, +}; -- cgit v1.2.3