This hides private browsing DNS and Alt-Svc entries from private browsing when in normal browsing. This also starts to show Socket and HTTP private browsing entries when in private browsing. No changes in WebSocket, which fully hides the private browsing entries, because that tab isn't very useful and perhaps removed in the future. Differential Revision: https://phabricator.services.mozilla.com/D304124
471 lines
14 KiB
JavaScript
471 lines
14 KiB
JavaScript
/* 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 FileUtils = ChromeUtils.importESModule(
|
|
"resource://gre/modules/FileUtils.sys.mjs"
|
|
).FileUtils;
|
|
const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
|
|
);
|
|
const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
|
|
Ci.nsIDashboard
|
|
);
|
|
const gDirServ = Cc["@mozilla.org/file/directory_service;1"].getService(
|
|
Ci.nsIDirectoryServiceProvider
|
|
);
|
|
const gNetLinkSvc =
|
|
Cc["@mozilla.org/network/network-link-service;1"] &&
|
|
Cc["@mozilla.org/network/network-link-service;1"].getService(
|
|
Ci.nsINetworkLinkService
|
|
);
|
|
|
|
const gRequestNetworkingData = {
|
|
http: gDashboard.requestHttpConnections,
|
|
sockets: gDashboard.requestSockets,
|
|
dns: gDashboard.requestDNSInfo,
|
|
websockets: gDashboard.requestWebsocketConnections,
|
|
altsvc: gDashboard.requestAltSvcCache,
|
|
dnslookuptool: () => {},
|
|
networkid: displayNetworkID,
|
|
};
|
|
const gDashboardCallbacks = {
|
|
http: displayHttp,
|
|
sockets: displaySockets,
|
|
dns: displayDns,
|
|
websockets: displayWebsockets,
|
|
altsvc: displayAltSvc,
|
|
};
|
|
|
|
const REFRESH_INTERVAL_MS = 3000;
|
|
|
|
const gIsPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);
|
|
|
|
function isPrivateBrowsingEntry(originAttributesSuffix) {
|
|
return !!ChromeUtils.CreateOriginAttributesFromOriginSuffix(
|
|
originAttributesSuffix
|
|
).privateBrowsingId;
|
|
}
|
|
|
|
function col(element) {
|
|
let col = document.createElement("td");
|
|
let content = document.createTextNode(element);
|
|
col.appendChild(content);
|
|
return col;
|
|
}
|
|
|
|
function displayHttp(data) {
|
|
let cont = document.getElementById("http_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "http_content");
|
|
|
|
for (let i = 0; i < data.connections.length; i++) {
|
|
if (
|
|
!gIsPrivateBrowsing &&
|
|
isPrivateBrowsingEntry(data.connections[i].originAttributesSuffix)
|
|
) {
|
|
continue;
|
|
}
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(data.connections[i].host));
|
|
row.appendChild(col(data.connections[i].port));
|
|
row.appendChild(col(data.connections[i].httpVersion));
|
|
row.appendChild(col(data.connections[i].ssl));
|
|
row.appendChild(col(data.connections[i].active.length));
|
|
row.appendChild(col(data.connections[i].idle.length));
|
|
row.appendChild(col(data.connections[i].originAttributesSuffix));
|
|
new_cont.appendChild(row);
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function displaySockets(data) {
|
|
let cont = document.getElementById("sockets_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "sockets_content");
|
|
|
|
for (let i = 0; i < data.sockets.length; i++) {
|
|
if (
|
|
!gIsPrivateBrowsing &&
|
|
isPrivateBrowsingEntry(data.sockets[i].originAttributesSuffix)
|
|
) {
|
|
continue;
|
|
}
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(data.sockets[i].host));
|
|
row.appendChild(col(data.sockets[i].port));
|
|
row.appendChild(col(data.sockets[i].type));
|
|
row.appendChild(col(data.sockets[i].active));
|
|
row.appendChild(col(data.sockets[i].sent));
|
|
row.appendChild(col(data.sockets[i].received));
|
|
row.appendChild(col(data.sockets[i].originAttributesSuffix));
|
|
new_cont.appendChild(row);
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function displayDns(data) {
|
|
let suffixContent = document.getElementById("dns_suffix_content");
|
|
let suffixParent = suffixContent.parentNode;
|
|
let suffixes = [];
|
|
try {
|
|
suffixes = gNetLinkSvc.dnsSuffixList; // May throw
|
|
} catch (e) {}
|
|
let suffix_tbody = document.createElement("tbody");
|
|
suffix_tbody.id = "dns_suffix_content";
|
|
for (let suffix of suffixes) {
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(suffix));
|
|
suffix_tbody.appendChild(row);
|
|
}
|
|
suffixParent.replaceChild(suffix_tbody, suffixContent);
|
|
|
|
let trr_url_tbody = document.createElement("tbody");
|
|
trr_url_tbody.id = "dns_trr_url";
|
|
let trr_url = document.createElement("tr");
|
|
trr_url.appendChild(col(Services.dns.currentTrrURI));
|
|
trr_url.appendChild(col(Services.dns.currentTrrMode));
|
|
trr_url_tbody.appendChild(trr_url);
|
|
let prevURL = document.getElementById("dns_trr_url");
|
|
prevURL.parentNode.replaceChild(trr_url_tbody, prevURL);
|
|
|
|
let cont = document.getElementById("dns_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "dns_content");
|
|
|
|
for (let i = 0; i < data.entries.length; i++) {
|
|
// TODO: Will be supported in bug 1889387.
|
|
if (data.entries[i].type != Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT) {
|
|
continue;
|
|
}
|
|
if (
|
|
!gIsPrivateBrowsing &&
|
|
isPrivateBrowsingEntry(data.entries[i].originAttributesSuffix)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(data.entries[i].hostname));
|
|
row.appendChild(col(data.entries[i].family));
|
|
row.appendChild(col(data.entries[i].trr));
|
|
let column = document.createElement("td");
|
|
|
|
for (let j = 0; j < data.entries[i].hostaddr.length; j++) {
|
|
column.appendChild(document.createTextNode(data.entries[i].hostaddr[j]));
|
|
column.appendChild(document.createElement("br"));
|
|
}
|
|
|
|
row.appendChild(column);
|
|
row.appendChild(col(data.entries[i].expiration));
|
|
row.appendChild(col(data.entries[i].originAttributesSuffix));
|
|
row.appendChild(col(data.entries[i].flags));
|
|
new_cont.appendChild(row);
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function displayWebsockets(data) {
|
|
let cont = document.getElementById("websockets_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "websockets_content");
|
|
|
|
for (let i = 0; i < data.websockets.length; i++) {
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(data.websockets[i].hostport));
|
|
row.appendChild(col(data.websockets[i].encrypted));
|
|
row.appendChild(col(data.websockets[i].msgsent));
|
|
row.appendChild(col(data.websockets[i].msgreceived));
|
|
row.appendChild(col(data.websockets[i].sentsize));
|
|
row.appendChild(col(data.websockets[i].receivedsize));
|
|
new_cont.appendChild(row);
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function formatTTL(seconds) {
|
|
if (seconds <= 0) {
|
|
return "expired";
|
|
}
|
|
let parts = [];
|
|
let d = Math.floor(seconds / 86400);
|
|
if (d) {
|
|
parts.push(`${d}d`);
|
|
}
|
|
let h = Math.floor((seconds % 86400) / 3600);
|
|
if (h) {
|
|
parts.push(`${h}h`);
|
|
}
|
|
let m = Math.floor((seconds % 3600) / 60);
|
|
if (m) {
|
|
parts.push(`${m}m`);
|
|
}
|
|
let s = seconds % 60;
|
|
if (s || !parts.length) {
|
|
parts.push(`${s}s`);
|
|
}
|
|
return parts.join(" ");
|
|
}
|
|
|
|
function displayAltSvc(data) {
|
|
let cont = document.getElementById("altsvc_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "altsvc_content");
|
|
|
|
for (let i = 0; i < data.entries.length; i++) {
|
|
let entry = data.entries[i];
|
|
if (
|
|
!gIsPrivateBrowsing &&
|
|
isPrivateBrowsingEntry(entry.originAttributesSuffix)
|
|
) {
|
|
continue;
|
|
}
|
|
let row = document.createElement("tr");
|
|
let scheme = entry.https ? "https" : "http";
|
|
row.appendChild(col(`${scheme}://${entry.originHost}:${entry.originPort}`));
|
|
row.appendChild(col(`${entry.alternateHost}:${entry.alternatePort}`));
|
|
row.appendChild(col(entry.alpn));
|
|
row.appendChild(col(entry.validated));
|
|
row.appendChild(col(formatTTL(entry.ttl)));
|
|
row.appendChild(col(entry.originAttributesSuffix));
|
|
new_cont.appendChild(row);
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function displayNetworkID() {
|
|
try {
|
|
let linkIsUp = gNetLinkSvc.isLinkUp;
|
|
let linkStatusKnown = gNetLinkSvc.linkStatusKnown;
|
|
let networkID = gNetLinkSvc.networkID;
|
|
|
|
document.getElementById("networkid_isUp").innerText = linkIsUp;
|
|
document.getElementById("networkid_statusKnown").innerText =
|
|
linkStatusKnown;
|
|
document.getElementById("networkid_id").innerText = networkID;
|
|
} catch (e) {
|
|
document.getElementById("networkid_isUp").innerText = "<unknown>";
|
|
document.getElementById("networkid_statusKnown").innerText = "<unknown>";
|
|
document.getElementById("networkid_id").innerText = "<unknown>";
|
|
}
|
|
}
|
|
|
|
function requestAllNetworkingData() {
|
|
for (let id in gRequestNetworkingData) {
|
|
requestNetworkingDataForTab(id);
|
|
}
|
|
}
|
|
|
|
function requestNetworkingDataForTab(id) {
|
|
gRequestNetworkingData[id](gDashboardCallbacks[id]);
|
|
}
|
|
|
|
let gInited = false;
|
|
function init() {
|
|
if (gInited) {
|
|
return;
|
|
}
|
|
gInited = true;
|
|
|
|
requestAllNetworkingData();
|
|
|
|
let autoRefresh = document.getElementById("autorefcheck");
|
|
if (autoRefresh.checked) {
|
|
setAutoRefreshInterval(autoRefresh);
|
|
}
|
|
|
|
autoRefresh.addEventListener("click", function () {
|
|
let refrButton = document.getElementById("refreshButton");
|
|
if (this.checked) {
|
|
setAutoRefreshInterval(this);
|
|
refrButton.disabled = "disabled";
|
|
} else {
|
|
clearInterval(this.interval);
|
|
refrButton.disabled = null;
|
|
}
|
|
});
|
|
|
|
let refr = document.getElementById("refreshButton");
|
|
refr.addEventListener("click", requestAllNetworkingData);
|
|
if (document.getElementById("autorefcheck").checked) {
|
|
refr.disabled = "disabled";
|
|
}
|
|
|
|
// Event delegation on #categories element
|
|
let menu = document.getElementById("categories");
|
|
menu.addEventListener("click", function click(e) {
|
|
if (e.target && e.target.parentNode == menu) {
|
|
show(e.target);
|
|
}
|
|
});
|
|
|
|
let clearHTTPCache = document.getElementById("clearHTTPCache");
|
|
clearHTTPCache.addEventListener("click", async function () {
|
|
Services.cache2.clear();
|
|
});
|
|
|
|
let dnsLookupButton = document.getElementById("dnsLookupButton");
|
|
dnsLookupButton.addEventListener("click", function () {
|
|
doLookup();
|
|
});
|
|
|
|
let hostInput = document.getElementById("host");
|
|
hostInput.addEventListener("keydown", function (e) {
|
|
if (e.key === "Enter") {
|
|
doLookup();
|
|
}
|
|
});
|
|
|
|
let clearDNSCache = document.getElementById("clearDNSCache");
|
|
clearDNSCache.addEventListener("click", function () {
|
|
Services.dns.clearCache(true);
|
|
});
|
|
|
|
if (location.hash) {
|
|
let sectionButton = document.getElementById(
|
|
"category-" + location.hash.substring(1)
|
|
);
|
|
if (sectionButton) {
|
|
sectionButton.click();
|
|
}
|
|
}
|
|
}
|
|
|
|
function show(button) {
|
|
let current_tab = document.querySelector(".active");
|
|
let category = button.getAttribute("id").substring("category-".length);
|
|
let content = document.getElementById(category);
|
|
if (current_tab == content) {
|
|
return;
|
|
}
|
|
current_tab.classList.remove("active");
|
|
current_tab.hidden = true;
|
|
content.classList.add("active");
|
|
content.hidden = false;
|
|
|
|
let current_button = document.querySelector("[selected=true]");
|
|
current_button.removeAttribute("selected");
|
|
button.setAttribute("selected", "true");
|
|
|
|
let autoRefresh = document.getElementById("autorefcheck");
|
|
if (autoRefresh.checked) {
|
|
clearInterval(autoRefresh.interval);
|
|
setAutoRefreshInterval(autoRefresh);
|
|
}
|
|
|
|
let title = document.getElementById("sectionTitle");
|
|
title.textContent = button.children[0].textContent;
|
|
location.hash = category;
|
|
}
|
|
|
|
function setAutoRefreshInterval(checkBox) {
|
|
let active_tab = document.querySelector(".active");
|
|
checkBox.interval = setInterval(function () {
|
|
requestNetworkingDataForTab(active_tab.id);
|
|
}, REFRESH_INTERVAL_MS);
|
|
}
|
|
|
|
// We use the pageshow event instead of onload. This is needed because sometimes
|
|
// the page is loaded via session-restore/bfcache. In such cases we need to call
|
|
// init() to keep the page behaviour consistent with the ticked checkboxes.
|
|
// Mostly the issue is with the autorefresh checkbox.
|
|
window.addEventListener("pageshow", function () {
|
|
init();
|
|
});
|
|
|
|
function doLookup() {
|
|
let host = document.getElementById("host").value;
|
|
if (host) {
|
|
try {
|
|
gDashboard.requestDNSLookup(host, displayDNSLookup);
|
|
} catch (e) {}
|
|
try {
|
|
gDashboard.requestDNSHTTPSRRLookup(host, displayHTTPSRRLookup);
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
|
|
function displayDNSLookup(data) {
|
|
let cont = document.getElementById("dnslookuptool_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "dnslookuptool_content");
|
|
|
|
if (data.answer) {
|
|
for (let address of data.address) {
|
|
let row = document.createElement("tr");
|
|
row.appendChild(col(address));
|
|
new_cont.appendChild(row);
|
|
}
|
|
} else {
|
|
new_cont.appendChild(col(data.error));
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|
|
|
|
function displayHTTPSRRLookup(data) {
|
|
let cont = document.getElementById("https_rr_content");
|
|
let parent = cont.parentNode;
|
|
let new_cont = document.createElement("tbody");
|
|
new_cont.setAttribute("id", "https_rr_content");
|
|
|
|
if (data.answer) {
|
|
for (let record of data.records) {
|
|
let row = document.createElement("tr");
|
|
let alpn = record.alpn ? `alpn="${record.alpn.alpn}" ` : "";
|
|
let noDefaultAlpn = record.noDefaultAlpn ? "noDefaultAlpn " : "";
|
|
let port = record.port ? `port="${record.port.port}" ` : "";
|
|
let echConfig = record.echConfig
|
|
? `echConfig="${record.echConfig.echConfig}" `
|
|
: "";
|
|
let ODoHConfig = record.ODoHConfig
|
|
? `odoh="${record.ODoHConfig.ODoHConfig}" `
|
|
: "";
|
|
let ipv4hint = "";
|
|
let ipv6hint = "";
|
|
if (record.ipv4Hint) {
|
|
let ipv4Str = "";
|
|
for (let addr of record.ipv4Hint.address) {
|
|
ipv4Str += `${addr}, `;
|
|
}
|
|
// Remove ", " at the end.
|
|
ipv4Str = ipv4Str.slice(0, -2);
|
|
ipv4hint = `ipv4hint="${ipv4Str}" `;
|
|
}
|
|
if (record.ipv6Hint) {
|
|
let ipv6Str = "";
|
|
for (let addr of record.ipv6Hint.address) {
|
|
ipv6Str += `${addr}, `;
|
|
}
|
|
// Remove ", " at the end.
|
|
ipv6Str = ipv6Str.slice(0, -2);
|
|
ipv6hint = `ipv6hint="${ipv6Str}" `;
|
|
}
|
|
|
|
let str = `${record.priority} ${record.targetName} `;
|
|
str += `(${alpn}${noDefaultAlpn}${port}`;
|
|
str += `${ipv4hint}${echConfig}${ipv6hint}`;
|
|
str += `${ODoHConfig})`;
|
|
row.appendChild(col(str));
|
|
new_cont.appendChild(row);
|
|
}
|
|
} else {
|
|
new_cont.appendChild(col(data.error));
|
|
}
|
|
|
|
parent.replaceChild(new_cont, cont);
|
|
}
|