summaryrefslogtreecommitdiffstats
path: root/browser/components/tabunloader
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
-rw-r--r--browser/components/tabunloader/docs/fullmode.pngbin0 -> 70005 bytes
-rw-r--r--browser/components/tabunloader/docs/index.rst58
-rw-r--r--browser/components/tabunloader/docs/lightmode.pngbin0 -> 52756 bytes
-rw-r--r--browser/components/tabunloader/jar.mn8
-rw-r--r--browser/components/tabunloader/moz.build12
8 files changed, 303 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);
+}
diff --git a/browser/components/tabunloader/docs/fullmode.png b/browser/components/tabunloader/docs/fullmode.png
new file mode 100644
index 0000000000..70d44ccd35
--- /dev/null
+++ b/browser/components/tabunloader/docs/fullmode.png
Binary files differ
diff --git a/browser/components/tabunloader/docs/index.rst b/browser/components/tabunloader/docs/index.rst
new file mode 100644
index 0000000000..534a10b4fd
--- /dev/null
+++ b/browser/components/tabunloader/docs/index.rst
@@ -0,0 +1,58 @@
+Tab Unloading
+=============
+
+Tab Unloading is a feature that automatically unloads tabs to prevent Firefox
+from crashing due to insufficient memory when the system’s available memory is
+low.
+
+The feature consists of two parts: memory pressure detector and tab unloader.
+When the memory pressure detector detects a low memory situation, it triggers
+the tab unloader that prioritizes tabs and chooses a tab to unload, or if there
+are no unloadable tabs, triggers the internal memory-pressure warning allowing
+the browser’s subsystems to reduce their memory use.
+
+There are two modes to prioritize tabs.
+
+Firefox basically unloads tabs in least-recently-used order, excluding
+tabs playing media, using Picture-in-Picture, or using WebRTC. Pinned
+tabs are deprioritized and are less likely to be unloaded.
+
+When there are more tabs opened, in most cases when there are more than
+eleven tabs, Firefox does extra calculations identifying processes hosting
+tabs and estimates memory usage of each tab, and then unloads tabs with
+larger memory and more processes that will be terminated by tab unloading.
+
+You may disable the feature by setting the preference
+``browser.tabs.unloadOnLowMemory`` to ``false``.
+
+about:unloads
+-------------
+
+The about:unloads page shows how Firefox prioritizes tabs and which tab will
+be unloaded when the tab unloader is triggered. You can trigger tab unloading
+manually by clicking the **Unload** button in the page.
+
+The page contains a table where existing tabs are displayed in the same order
+used by Firefox to choose the next tab to unload. When you click the button,
+a tab shown in the first row, which has the lowest value in the **Priority**,
+is unloaded. If the value of **Priority** is a hyphen (-), the corresponding
+tab is not unloadable.
+
+In the first of the two modes mentioned above, Firefox calculates **Last Accessed**
+and **Base Weight** for each tab and orders tabs by those values, not calculating
+the other attributes such as **Secondary Weight** to save CPU power. Below is
+an example of this case.
+
+.. image:: lightmode.png
+
+In the second mode, Firefox identifies processes hosting each tab and shows
+their process IDs in the **Process IDs** column. Process IDs are displayed in
+**bold** when they are hosting the tab’s top frame, and in *italic* when the
+process is shared between different tabs.
+
+After identifying the processes of all tabs, Firefox estimates memory usage of
+tabs and calculates the secondary weight for tabs that are not recently accessed.
+For recently accessed tabs, Firefox does not calculate the **Secondary Weight**
+and **Memory**, leaving those columns empty.
+
+.. image:: fullmode.png
diff --git a/browser/components/tabunloader/docs/lightmode.png b/browser/components/tabunloader/docs/lightmode.png
new file mode 100644
index 0000000000..8b6237f5f3
--- /dev/null
+++ b/browser/components/tabunloader/docs/lightmode.png
Binary files differ
diff --git a/browser/components/tabunloader/jar.mn b/browser/components/tabunloader/jar.mn
new file mode 100644
index 0000000000..f2b6effcd8
--- /dev/null
+++ b/browser/components/tabunloader/jar.mn
@@ -0,0 +1,8 @@
+# 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/.
+
+browser.jar:
+ content/browser/tabunloader/aboutUnloads.css (content/aboutUnloads.css)
+ content/browser/tabunloader/aboutUnloads.html (content/aboutUnloads.html)
+ content/browser/tabunloader/aboutUnloads.js (content/aboutUnloads.js)
diff --git a/browser/components/tabunloader/moz.build b/browser/components/tabunloader/moz.build
new file mode 100644
index 0000000000..b4408d3f36
--- /dev/null
+++ b/browser/components/tabunloader/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+with Files("**"):
+ BUG_COMPONENT = ("Firefox", "Tabbed Browser")
+
+SPHINX_TREES["/browser/tabunloader"] = "docs"