summaryrefslogtreecommitdiffstats
path: root/browser/components/tabunloader/content
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/tabunloader/content/aboutUnloads.css25
-rw-r--r--browser/components/tabunloader/content/aboutUnloads.html74
-rw-r--r--browser/components/tabunloader/content/aboutUnloads.js126
3 files changed, 225 insertions, 0 deletions
diff --git a/browser/components/tabunloader/content/aboutUnloads.css b/browser/components/tabunloader/content/aboutUnloads.css
new file mode 100644
index 0000000000..150610a416
--- /dev/null
+++ b/browser/components/tabunloader/content/aboutUnloads.css
@@ -0,0 +1,25 @@
+/* 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/. */
+
+body {
+ display: block;
+}
+
+.control-panel {
+ float: inline-end;
+ margin: 10px 0;
+}
+
+#button-unload {
+ float: inherit;
+ margin-inline-end: 0;
+}
+
+.top-level-process {
+ font-weight: bold;
+}
+
+.shared-process {
+ font-style: italic;
+}
diff --git a/browser/components/tabunloader/content/aboutUnloads.html b/browser/components/tabunloader/content/aboutUnloads.html
new file mode 100644
index 0000000000..193840cd99
--- /dev/null
+++ b/browser/components/tabunloader/content/aboutUnloads.html
@@ -0,0 +1,74 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <title data-l10n-id="about-unloads-page-title"></title>
+ <meta
+ http-equiv="Content-Security-Policy"
+ content="default-src chrome:; object-src 'none'"
+ />
+ <meta charset="utf-8" />
+ <meta name="color-scheme" content="light dark" />
+ <link
+ rel="stylesheet"
+ href="chrome://global/skin/in-content/info-pages.css"
+ />
+ <link
+ rel="stylesheet"
+ href="chrome://browser/content/tabunloader/aboutUnloads.css"
+ />
+ <link rel="localization" href="browser/aboutUnloads.ftl" />
+ <link rel="localization" href="branding/brand.ftl" />
+ <script src="chrome://browser/content/tabunloader/aboutUnloads.js"></script>
+ </head>
+ <body>
+ <h1 data-l10n-id="about-unloads-page-title"></h1>
+ <p data-l10n-id="about-unloads-intro"></p>
+ <p data-l10n-id="about-unloads-learn-more">
+ <a
+ data-l10n-name="doc-link"
+ href="https://firefox-source-docs.mozilla.org/browser/tabunloader/"
+ >
+ </a>
+ </p>
+ <div class="control-panel">
+ <div><span id="label-last-updated"></span></div>
+ <button
+ id="button-unload"
+ data-l10n-id="about-unloads-button-unload"
+ ></button>
+ </div>
+ <table class="tab-table">
+ <thead>
+ <tr>
+ <th data-l10n-id="about-unloads-column-priority" />
+ <th data-l10n-id="about-unloads-column-host" />
+ <th data-l10n-id="about-unloads-column-last-accessed" />
+ <th data-l10n-id="about-unloads-column-weight" />
+ <th data-l10n-id="about-unloads-column-sortweight" />
+ <th data-l10n-id="about-unloads-column-memory" />
+ <th data-l10n-id="about-unloads-column-processes" />
+ </tr>
+ </thead>
+ <tbody>
+ <tr id="no-unloadable-tab-message">
+ <td data-l10n-id="about-unloads-no-unloadable-tab" colspan="8"></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <template name="tab-table-row">
+ <tr>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </template>
+ </body>
+</html>
diff --git a/browser/components/tabunloader/content/aboutUnloads.js b/browser/components/tabunloader/content/aboutUnloads.js
new file mode 100644
index 0000000000..b58cd83609
--- /dev/null
+++ b/browser/components/tabunloader/content/aboutUnloads.js
@@ -0,0 +1,126 @@
+/* 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";
+
+const { TabUnloader } = ChromeUtils.import(
+ "resource:///modules/TabUnloader.jsm"
+);
+
+async function refreshData() {
+ const sortedTabs = await TabUnloader.getSortedTabs(null);
+ const tabTable = document.querySelector(".tab-table > tbody");
+ const getHost = uri => {
+ try {
+ return uri?.host;
+ } catch (e) {
+ return uri?.spec;
+ }
+ };
+ const updateTimestamp = () => {
+ document.l10n.setAttributes(
+ document.getElementById("label-last-updated"),
+ "about-unloads-last-updated",
+ { date: Date.now() }
+ );
+ };
+
+ // Reset the table
+ // Don't delete the first row showing the "no unloadable tab" message
+ while (tabTable.rows.length > 1) {
+ tabTable.deleteRow(1);
+ }
+ if (!sortedTabs.length) {
+ document.getElementById("button-unload").disabled = true;
+ document.getElementById("no-unloadable-tab-message").hidden = false;
+ updateTimestamp();
+ return;
+ }
+ document.getElementById("button-unload").disabled =
+ !TabUnloader.isDiscardable(sortedTabs[0]);
+ document.getElementById("no-unloadable-tab-message").hidden = true;
+
+ const fragmentRows = new DocumentFragment();
+ const templateRow = document.querySelector("template[name=tab-table-row]");
+
+ let ordinal = 0;
+ for (const tabInfo of sortedTabs) {
+ if (!("tab" in tabInfo)) {
+ continue;
+ }
+
+ const fragment = templateRow.content.cloneNode(true);
+ const row = fragment.querySelector("tr");
+
+ row.children[0].textContent = TabUnloader.isDiscardable(tabInfo)
+ ? ++ordinal
+ : "-";
+ row.children[1].textContent = getHost(
+ tabInfo.tab?.linkedBrowser?.currentURI
+ );
+ if ("lastAccessed" in tabInfo.tab) {
+ document.l10n.setAttributes(
+ row.children[2],
+ "about-unloads-last-accessed",
+ { date: tabInfo.tab.lastAccessed }
+ );
+ }
+ row.children[3].textContent = tabInfo.weight;
+ row.children[4].textContent = tabInfo.sortWeight;
+ if ("memory" in tabInfo) {
+ document.l10n.setAttributes(
+ row.children[5],
+ "about-unloads-memory-in-mb",
+ { mem: tabInfo.memory / 1024 / 1024 }
+ );
+ }
+
+ if (tabInfo.processes) {
+ for (const [pid, procEntry] of tabInfo.processes) {
+ if (pid < 0) {
+ // Tab is hosted by the main process
+ continue;
+ }
+
+ const procLabel = document.createElement("span");
+ const procInfo = procEntry.entryToProcessMap;
+
+ if (procEntry.isTopLevel) {
+ procLabel.classList.add("top-level-process");
+ }
+ if (procInfo.tabSet.size > 1) {
+ procLabel.classList.add("shared-process");
+ }
+ procLabel.textContent = pid;
+ document.l10n.setAttributes(
+ procLabel,
+ "about-unloads-memory-in-mb-tooltip",
+ { mem: procInfo.memory / 1024 / 1024 }
+ );
+ row.children[6].appendChild(procLabel);
+ row.children[6].appendChild(document.createTextNode(" "));
+ }
+ }
+
+ fragmentRows.appendChild(fragment);
+ }
+
+ tabTable.appendChild(fragmentRows);
+ updateTimestamp();
+}
+
+async function onLoad() {
+ document
+ .getElementById("button-unload")
+ .addEventListener("click", async () => {
+ await TabUnloader.unloadLeastRecentlyUsedTab(null);
+ await refreshData();
+ });
+ await refreshData();
+}
+
+try {
+ document.addEventListener("DOMContentLoaded", onLoad, { once: true });
+} catch (ex) {
+ console.error(ex);
+}