diff --git a/toolkit/components/aboutprocesses/content/aboutProcesses.css b/toolkit/components/aboutprocesses/content/aboutProcesses.css
index 7690db974220..fd7ef032a878 100644
--- a/toolkit/components/aboutprocesses/content/aboutProcesses.css
+++ b/toolkit/components/aboutprocesses/content/aboutProcesses.css
@@ -10,6 +10,7 @@ html {
body {
overflow-x: hidden;
}
+
#process-table {
-moz-user-select: none;
font-size: 1em;
@@ -48,13 +49,19 @@ td:nth-child(1) {
/* At least one column needs to have a flexible width,
so no width specified for td:nth-child(2) */
td:nth-child(3) {
- width: 10%;
+ width: 10%;
}
td:nth-child(4) {
- width: 10%;
+ width: 10%;
}
td:nth-child(5) {
- width: 10%;
+ width: 10%;
+}
+td:nth-child(6) {
+ width: 10%;
+}
+td:nth-child(7) {
+ width: 16px;
}
#process-thead > tr {
@@ -73,7 +80,8 @@ td:nth-child(5) {
}
td {
padding: 5px 10px;
- min-height: 2em;
+ min-height: 1.5em;
+ max-height: 1.5em;
color: var(--in-content-text-color);
max-width: 70vw;
overflow: hidden;
@@ -167,3 +175,25 @@ td {
#process-tbody > tr.thread {
font-size-adjust: 0.5;
}
+
+.killing {
+ opacity: 0.3;
+ transition-property: "opacity";
+ transition-duration: 1s;
+}
+
+.killed {
+ opacity: 0.3;
+}
+
+/* icons */
+.close-icon {
+ background: url("chrome://global/skin/icons/delete.svg") no-repeat center;
+ opacity: 0;
+}
+tr:-moz-any([selected], :hover) > .close-icon {
+ opacity: 1;
+}
+.close-icon:hover {
+ background-color: var(--in-content-button-background);
+}
diff --git a/toolkit/components/aboutprocesses/content/aboutProcesses.ftl b/toolkit/components/aboutprocesses/content/aboutProcesses.ftl
index 3c137c33b02f..3f4b45dffb79 100644
--- a/toolkit/components/aboutprocesses/content/aboutProcesses.ftl
+++ b/toolkit/components/aboutprocesses/content/aboutProcesses.ftl
@@ -5,6 +5,11 @@
# Page title
about-processes-title = Process Manager
+## Tooltips
+
+about-processes-shutdown-process =
+ .title = Kill process
+
## Column headers
about-processes-column-id = Id
diff --git a/toolkit/components/aboutprocesses/content/aboutProcesses.html b/toolkit/components/aboutprocesses/content/aboutProcesses.html
index c2089a288823..bccde311d454 100644
--- a/toolkit/components/aboutprocesses/content/aboutProcesses.html
+++ b/toolkit/components/aboutprocesses/content/aboutProcesses.html
@@ -22,6 +22,7 @@
|
|
|
+ |
diff --git a/toolkit/components/aboutprocesses/content/aboutProcesses.js b/toolkit/components/aboutprocesses/content/aboutProcesses.js
index 274c5712dc91..88701bbb12c3 100644
--- a/toolkit/components/aboutprocesses/content/aboutProcesses.js
+++ b/toolkit/components/aboutprocesses/content/aboutProcesses.js
@@ -229,7 +229,10 @@ var State = {
var View = {
_fragment: document.createDocumentFragment(),
+ // Processes that we killed during the previous iteration.
+ _killedRecently: [],
async commit() {
+ this._killedRecently.length = 0;
let tbody = document.getElementById("process-tbody");
// Force translation to happen before we insert the new content in the DOM
@@ -329,6 +332,33 @@ var View = {
classes: ["numberOfThreads"],
});
+ // Column: Kill button – but not for all processes.
+ let killButton = this._addCell(row, {
+ content: "",
+ classes: ["action-icon"],
+ });
+
+ if (data.type == "socket") {
+ // We can't kill this process. Let's not pretend that we can.
+ // Don't display the kill button.
+ } else if (this._killedRecently.some(pid => pid == data.pid)) {
+ // We're racing between the "kill" action and the visual refresh.
+ // In a few cases, we could end up with the visual refresh showing
+ // a process as un-killed while we actually just killed it.
+ //
+ // We still want to display the process in case something actually
+ // went bad and the user needs the information to realize this.
+ // But we also want to make it visible that the process is being
+ // killed.
+ row.classList.add("killed");
+ } else {
+ // Otherwise, let's display the kill button.
+ killButton.classList.add("close-icon");
+ document.l10n.setAttributes(
+ killButton,
+ "about-processes-shutdown-process"
+ );
+ }
this._fragment.appendChild(row);
return row;
},
@@ -391,13 +421,21 @@ var View = {
classes: ["numberOfThreads"],
});
+ // Column: Buttons (empty)
+ this._addCell(row, {
+ content: "",
+ classes: [],
+ });
+
this._fragment.appendChild(row);
return row;
},
_addCell(row, { content, classes }) {
let elt = document.createElement("td");
- this._setTextAndTooltip(elt, content);
+ if (content) {
+ this._setTextAndTooltip(elt, content);
+ }
elt.classList.add(...classes);
row.appendChild(elt);
return elt;
@@ -541,19 +579,13 @@ var Control = {
tbody.addEventListener("click", event => {
this._updateLastMouseEvent();
- // Handle showing or hiding subitems of a row.
let target = event.target;
if (target.classList.contains("twisty")) {
- let row = target.parentNode.parentNode;
- let id = row.process.pid;
- if (target.classList.toggle("open")) {
- this._openItems.add(id);
- this._showChildren(row);
- View.insertAfterRow(row);
- } else {
- this._openItems.delete(id);
- this._removeSubtree(row);
- }
+ // Handle showing or hiding subitems of a row.
+ this._handleTwisty(target);
+ return;
+ } else if (target.classList.contains("close-icon")) {
+ this._handleClose(target);
return;
}
@@ -756,6 +788,34 @@ var Control = {
return order;
});
},
+
+ _handleTwisty(target) {
+ let row = target.parentNode.parentNode;
+ let id = row.process.pid;
+ if (target.classList.toggle("open")) {
+ this._openItems.add(id);
+ this._showChildren(row);
+ View.insertAfterRow(row);
+ } else {
+ this._openItems.delete(id);
+ this._removeSubtree(row);
+ }
+ },
+
+ _handleClose(target) {
+ const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
+ Ci.nsIProcessToolsService
+ );
+ let row = target.parentNode;
+ let pid = row.process.pid;
+ ProcessTools.kill(pid);
+
+ row.classList.add("killing");
+ target.classList.remove("close-icon");
+ // Make sure that the user can't click twice on the kill button.
+ // Otherwise, chaos might ensure. Plus we risk crashing under Windows.
+ View._killedRecently.push(pid);
+ },
};
window.onload = async function() {