Bug 1726591 [wpt PR 30088] - Check if a <fe*Lighting> has a light source before inval...
[gecko.git] / toolkit / content / aboutNetworking.js
blob44baf62121d92fb09c4916fc122ca54627412bfc
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
7 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
8 const FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm")
9   .FileUtils;
10 const gEnv = Cc["@mozilla.org/process/environment;1"].getService(
11   Ci.nsIEnvironment
13 const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
14   Ci.nsIDashboard
16 const gDirServ = Cc["@mozilla.org/file/directory_service;1"].getService(
17   Ci.nsIDirectoryServiceProvider
19 const gNetLinkSvc =
20   Cc["@mozilla.org/network/network-link-service;1"] &&
21   Cc["@mozilla.org/network/network-link-service;1"].getService(
22     Ci.nsINetworkLinkService
23   );
24 const gDNSService = Cc["@mozilla.org/network/dns-service;1"].getService(
25   Ci.nsIDNSService
28 const gRequestNetworkingData = {
29   http: gDashboard.requestHttpConnections,
30   sockets: gDashboard.requestSockets,
31   dns: gDashboard.requestDNSInfo,
32   websockets: gDashboard.requestWebsocketConnections,
33   dnslookuptool: () => {},
34   logging: () => {},
35   rcwn: gDashboard.requestRcwnStats,
36   networkid: displayNetworkID,
38 const gDashboardCallbacks = {
39   http: displayHttp,
40   sockets: displaySockets,
41   dns: displayDns,
42   websockets: displayWebsockets,
43   rcwn: displayRcwnStats,
46 const REFRESH_INTERVAL_MS = 3000;
48 function col(element) {
49   let col = document.createElement("td");
50   let content = document.createTextNode(element);
51   col.appendChild(content);
52   return col;
55 function displayHttp(data) {
56   let cont = document.getElementById("http_content");
57   let parent = cont.parentNode;
58   let new_cont = document.createElement("tbody");
59   new_cont.setAttribute("id", "http_content");
61   for (let i = 0; i < data.connections.length; i++) {
62     let row = document.createElement("tr");
63     row.appendChild(col(data.connections[i].host));
64     row.appendChild(col(data.connections[i].port));
65     row.appendChild(col(data.connections[i].httpVersion));
66     row.appendChild(col(data.connections[i].ssl));
67     row.appendChild(col(data.connections[i].active.length));
68     row.appendChild(col(data.connections[i].idle.length));
69     new_cont.appendChild(row);
70   }
72   parent.replaceChild(new_cont, cont);
75 function displaySockets(data) {
76   let cont = document.getElementById("sockets_content");
77   let parent = cont.parentNode;
78   let new_cont = document.createElement("tbody");
79   new_cont.setAttribute("id", "sockets_content");
81   for (let i = 0; i < data.sockets.length; i++) {
82     let row = document.createElement("tr");
83     row.appendChild(col(data.sockets[i].host));
84     row.appendChild(col(data.sockets[i].port));
85     row.appendChild(col(data.sockets[i].type));
86     row.appendChild(col(data.sockets[i].active));
87     row.appendChild(col(data.sockets[i].sent));
88     row.appendChild(col(data.sockets[i].received));
89     new_cont.appendChild(row);
90   }
92   parent.replaceChild(new_cont, cont);
95 function displayDns(data) {
96   let suffixContent = document.getElementById("dns_suffix_content");
97   let suffixParent = suffixContent.parentNode;
98   let suffixes = [];
99   try {
100     suffixes = gNetLinkSvc.dnsSuffixList; // May throw
101   } catch (e) {}
102   let suffix_tbody = document.createElement("tbody");
103   suffix_tbody.id = "dns_suffix_content";
104   for (let suffix of suffixes) {
105     let row = document.createElement("tr");
106     row.appendChild(col(suffix));
107     suffix_tbody.appendChild(row);
108   }
109   suffixParent.replaceChild(suffix_tbody, suffixContent);
111   let trr_url_tbody = document.createElement("tbody");
112   trr_url_tbody.id = "dns_trr_url";
113   let trr_url = document.createElement("tr");
114   trr_url.appendChild(col(gDNSService.currentTrrURI));
115   trr_url_tbody.appendChild(trr_url);
116   let prevURL = document.getElementById("dns_trr_url");
117   prevURL.parentNode.replaceChild(trr_url_tbody, prevURL);
119   let cont = document.getElementById("dns_content");
120   let parent = cont.parentNode;
121   let new_cont = document.createElement("tbody");
122   new_cont.setAttribute("id", "dns_content");
124   for (let i = 0; i < data.entries.length; i++) {
125     let row = document.createElement("tr");
126     row.appendChild(col(data.entries[i].hostname));
127     row.appendChild(col(data.entries[i].family));
128     row.appendChild(col(data.entries[i].trr));
129     let column = document.createElement("td");
131     for (let j = 0; j < data.entries[i].hostaddr.length; j++) {
132       column.appendChild(document.createTextNode(data.entries[i].hostaddr[j]));
133       column.appendChild(document.createElement("br"));
134     }
136     row.appendChild(column);
137     row.appendChild(col(data.entries[i].expiration));
138     row.appendChild(col(data.entries[i].originAttributesSuffix));
139     new_cont.appendChild(row);
140   }
142   parent.replaceChild(new_cont, cont);
145 function displayWebsockets(data) {
146   let cont = document.getElementById("websockets_content");
147   let parent = cont.parentNode;
148   let new_cont = document.createElement("tbody");
149   new_cont.setAttribute("id", "websockets_content");
151   for (let i = 0; i < data.websockets.length; i++) {
152     let row = document.createElement("tr");
153     row.appendChild(col(data.websockets[i].hostport));
154     row.appendChild(col(data.websockets[i].encrypted));
155     row.appendChild(col(data.websockets[i].msgsent));
156     row.appendChild(col(data.websockets[i].msgreceived));
157     row.appendChild(col(data.websockets[i].sentsize));
158     row.appendChild(col(data.websockets[i].receivedsize));
159     new_cont.appendChild(row);
160   }
162   parent.replaceChild(new_cont, cont);
165 function displayRcwnStats(data) {
166   let status = Services.prefs.getBoolPref("network.http.rcwn.enabled");
167   let linkType = Ci.nsINetworkLinkService.LINK_TYPE_UNKNOWN;
168   try {
169     linkType = gNetLinkSvc.linkType;
170   } catch (e) {}
171   if (
172     !(
173       linkType == Ci.nsINetworkLinkService.LINK_TYPE_UNKNOWN ||
174       linkType == Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET ||
175       linkType == Ci.nsINetworkLinkService.LINK_TYPE_USB ||
176       linkType == Ci.nsINetworkLinkService.LINK_TYPE_WIFI
177     )
178   ) {
179     status = false;
180   }
182   let cacheWon = data.rcwnCacheWonCount;
183   let netWon = data.rcwnNetWonCount;
184   let total = data.totalNetworkRequests;
185   let cacheSlow = data.cacheSlowCount;
186   let cacheNotSlow = data.cacheNotSlowCount;
188   document.getElementById("rcwn_status").innerText = status;
189   document.getElementById("total_req_count").innerText = total;
190   document.getElementById("rcwn_cache_won_count").innerText = cacheWon;
191   document.getElementById("rcwn_cache_net_count").innerText = netWon;
192   document.getElementById("rcwn_cache_slow").innerText = cacheSlow;
193   document.getElementById("rcwn_cache_not_slow").innerText = cacheNotSlow;
195   // Keep in sync with CachePerfStats::EDataType in CacheFileUtils.h
196   const perfStatTypes = ["open", "read", "write", "entryopen"];
198   const perfStatFieldNames = ["avgShort", "avgLong", "stddevLong"];
200   for (let typeIndex in perfStatTypes) {
201     for (let statFieldIndex in perfStatFieldNames) {
202       document.getElementById(
203         "rcwn_perfstats_" +
204           perfStatTypes[typeIndex] +
205           "_" +
206           perfStatFieldNames[statFieldIndex]
207       ).innerText =
208         data.perfStats[typeIndex][perfStatFieldNames[statFieldIndex]];
209     }
210   }
213 function displayNetworkID() {
214   try {
215     let linkIsUp = gNetLinkSvc.isLinkUp;
216     let linkStatusKnown = gNetLinkSvc.linkStatusKnown;
217     let networkID = gNetLinkSvc.networkID;
219     document.getElementById("networkid_isUp").innerText = linkIsUp;
220     document.getElementById(
221       "networkid_statusKnown"
222     ).innerText = linkStatusKnown;
223     document.getElementById("networkid_id").innerText = networkID;
224   } catch (e) {
225     document.getElementById("networkid_isUp").innerText = "<unknown>";
226     document.getElementById("networkid_statusKnown").innerText = "<unknown>";
227     document.getElementById("networkid_id").innerText = "<unknown>";
228   }
231 function requestAllNetworkingData() {
232   for (let id in gRequestNetworkingData) {
233     requestNetworkingDataForTab(id);
234   }
237 function requestNetworkingDataForTab(id) {
238   gRequestNetworkingData[id](gDashboardCallbacks[id]);
241 let gInited = false;
242 function init() {
243   if (gInited) {
244     return;
245   }
246   gInited = true;
247   gDashboard.enableLogging = true;
249   requestAllNetworkingData();
251   let autoRefresh = document.getElementById("autorefcheck");
252   if (autoRefresh.checked) {
253     setAutoRefreshInterval(autoRefresh);
254   }
256   autoRefresh.addEventListener("click", function() {
257     let refrButton = document.getElementById("refreshButton");
258     if (this.checked) {
259       setAutoRefreshInterval(this);
260       refrButton.disabled = "disabled";
261     } else {
262       clearInterval(this.interval);
263       refrButton.disabled = null;
264     }
265   });
267   let refr = document.getElementById("refreshButton");
268   refr.addEventListener("click", requestAllNetworkingData);
269   if (document.getElementById("autorefcheck").checked) {
270     refr.disabled = "disabled";
271   }
273   // Event delegation on #categories element
274   let menu = document.getElementById("categories");
275   menu.addEventListener("click", function click(e) {
276     if (e.target && e.target.parentNode == menu) {
277       show(e.target);
278     }
279   });
281   let dnsLookupButton = document.getElementById("dnsLookupButton");
282   dnsLookupButton.addEventListener("click", function() {
283     doLookup();
284   });
286   let clearDNSCache = document.getElementById("clearDNSCache");
287   clearDNSCache.addEventListener("click", function() {
288     gDNSService.clearCache(true);
289   });
291   let setLogButton = document.getElementById("set-log-file-button");
292   setLogButton.addEventListener("click", setLogFile);
294   let setModulesButton = document.getElementById("set-log-modules-button");
295   setModulesButton.addEventListener("click", setLogModules);
297   let startLoggingButton = document.getElementById("start-logging-button");
298   startLoggingButton.addEventListener("click", startLogging);
300   let stopLoggingButton = document.getElementById("stop-logging-button");
301   stopLoggingButton.addEventListener("click", stopLogging);
303   try {
304     let file = gDirServ.getFile("TmpD", {});
305     file.append("log.txt");
306     document.getElementById("log-file").value = file.path;
307   } catch (e) {
308     console.error(e);
309   }
311   // Update the value of the log file.
312   updateLogFile();
314   // Update the active log modules
315   updateLogModules();
317   // If we can't set the file and the modules at runtime,
318   // the start and stop buttons wouldn't really do anything.
319   if (setLogButton.disabled && setModulesButton.disabled) {
320     startLoggingButton.disabled = true;
321     stopLoggingButton.disabled = true;
322   }
324   if (location.hash) {
325     let sectionButton = document.getElementById(
326       "category-" + location.hash.substring(1)
327     );
328     if (sectionButton) {
329       sectionButton.click();
330     }
331   }
334 function updateLogFile() {
335   let logPath = "";
337   // Try to get the environment variable for the log file
338   logPath = gEnv.get("MOZ_LOG_FILE") || gEnv.get("NSPR_LOG_FILE");
339   let currentLogFile = document.getElementById("current-log-file");
340   let setLogFileButton = document.getElementById("set-log-file-button");
342   // If the log file was set from an env var, we disable the ability to set it
343   // at runtime.
344   if (logPath.length) {
345     currentLogFile.innerText = logPath;
346     setLogFileButton.disabled = true;
347   } else {
348     // There may be a value set by a pref.
349     currentLogFile.innerText = gDashboard.getLogPath();
350   }
353 function updateLogModules() {
354   // Try to get the environment variable for the log file
355   let logModules =
356     gEnv.get("MOZ_LOG") ||
357     gEnv.get("MOZ_LOG_MODULES") ||
358     gEnv.get("NSPR_LOG_MODULES");
359   let currentLogModules = document.getElementById("current-log-modules");
360   let setLogModulesButton = document.getElementById("set-log-modules-button");
361   if (logModules.length) {
362     currentLogModules.innerText = logModules;
363     // If the log modules are set by an environment variable at startup, do not
364     // allow changing them throught a pref. It would be difficult to figure out
365     // which ones are enabled and which ones are not. The user probably knows
366     // what he they are doing.
367     setLogModulesButton.disabled = true;
368   } else {
369     let activeLogModules = [];
370     try {
371       if (Services.prefs.getBoolPref("logging.config.add_timestamp")) {
372         activeLogModules.push("timestamp");
373       }
374     } catch (e) {}
375     try {
376       if (Services.prefs.getBoolPref("logging.config.sync")) {
377         activeLogModules.push("sync");
378       }
379     } catch (e) {}
381     let children = Services.prefs.getBranch("logging.").getChildList("");
383     for (let pref of children) {
384       if (pref.startsWith("config.")) {
385         continue;
386       }
388       try {
389         let value = Services.prefs.getIntPref(`logging.${pref}`);
390         activeLogModules.push(`${pref}:${value}`);
391       } catch (e) {
392         console.error(e);
393       }
394     }
396     currentLogModules.innerText = activeLogModules.join(",");
397   }
400 function setLogFile() {
401   let setLogButton = document.getElementById("set-log-file-button");
402   if (setLogButton.disabled) {
403     // There's no point trying since it wouldn't work anyway.
404     return;
405   }
406   let logFile = document.getElementById("log-file").value.trim();
407   Services.prefs.setCharPref("logging.config.LOG_FILE", logFile);
408   updateLogFile();
411 function clearLogModules() {
412   // Turn off all the modules.
413   let children = Services.prefs.getBranch("logging.").getChildList("");
414   for (let pref of children) {
415     if (!pref.startsWith("config.")) {
416       Services.prefs.clearUserPref(`logging.${pref}`);
417     }
418   }
419   Services.prefs.clearUserPref("logging.config.add_timestamp");
420   Services.prefs.clearUserPref("logging.config.sync");
421   updateLogModules();
424 function setLogModules() {
425   let setLogModulesButton = document.getElementById("set-log-modules-button");
426   if (setLogModulesButton.disabled) {
427     // The modules were set via env var, so we shouldn't try to change them.
428     return;
429   }
431   let modules = document.getElementById("log-modules").value.trim();
433   // Clear previously set log modules.
434   clearLogModules();
436   let logModules = modules.split(",");
437   for (let module of logModules) {
438     if (module == "timestamp") {
439       Services.prefs.setBoolPref("logging.config.add_timestamp", true);
440     } else if (module == "rotate") {
441       // XXX: rotate is not yet supported.
442     } else if (module == "append") {
443       // XXX: append is not yet supported.
444     } else if (module == "sync") {
445       Services.prefs.setBoolPref("logging.config.sync", true);
446     } else {
447       let lastColon = module.lastIndexOf(":");
448       let key = module.slice(0, lastColon);
449       let value = parseInt(module.slice(lastColon + 1), 10);
450       Services.prefs.setIntPref(`logging.${key}`, value);
451     }
452   }
454   updateLogModules();
457 function startLogging() {
458   setLogFile();
459   setLogModules();
462 function stopLogging() {
463   clearLogModules();
464   // clear the log file as well
465   Services.prefs.clearUserPref("logging.config.LOG_FILE");
466   updateLogFile();
469 function show(button) {
470   let current_tab = document.querySelector(".active");
471   let category = button.getAttribute("id").substring("category-".length);
472   let content = document.getElementById(category);
473   if (current_tab == content) {
474     return;
475   }
476   current_tab.classList.remove("active");
477   current_tab.hidden = true;
478   content.classList.add("active");
479   content.hidden = false;
481   let current_button = document.querySelector("[selected=true]");
482   current_button.removeAttribute("selected");
483   button.setAttribute("selected", "true");
485   let autoRefresh = document.getElementById("autorefcheck");
486   if (autoRefresh.checked) {
487     clearInterval(autoRefresh.interval);
488     setAutoRefreshInterval(autoRefresh);
489   }
491   let title = document.getElementById("sectionTitle");
492   title.textContent = button.children[0].textContent;
493   location.hash = category;
496 function setAutoRefreshInterval(checkBox) {
497   let active_tab = document.querySelector(".active");
498   checkBox.interval = setInterval(function() {
499     requestNetworkingDataForTab(active_tab.id);
500   }, REFRESH_INTERVAL_MS);
503 // We use the pageshow event instead of onload. This is needed because sometimes
504 // the page is loaded via session-restore/bfcache. In such cases we need to call
505 // init() to keep the page behaviour consistent with the ticked checkboxes.
506 // Mostly the issue is with the autorefresh checkbox.
507 window.addEventListener("pageshow", function() {
508   init();
511 function doLookup() {
512   let host = document.getElementById("host").value;
513   if (host) {
514     try {
515       gDashboard.requestDNSLookup(host, displayDNSLookup);
516     } catch (e) {}
517     try {
518       gDashboard.requestDNSHTTPSRRLookup(host, displayHTTPSRRLookup);
519     } catch (e) {}
520   }
523 function displayDNSLookup(data) {
524   let cont = document.getElementById("dnslookuptool_content");
525   let parent = cont.parentNode;
526   let new_cont = document.createElement("tbody");
527   new_cont.setAttribute("id", "dnslookuptool_content");
529   if (data.answer) {
530     for (let address of data.address) {
531       let row = document.createElement("tr");
532       row.appendChild(col(address));
533       new_cont.appendChild(row);
534     }
535   } else {
536     new_cont.appendChild(col(data.error));
537   }
539   parent.replaceChild(new_cont, cont);
542 function displayHTTPSRRLookup(data) {
543   let cont = document.getElementById("https_rr_content");
544   let parent = cont.parentNode;
545   let new_cont = document.createElement("tbody");
546   new_cont.setAttribute("id", "https_rr_content");
548   if (data.answer) {
549     for (let record of data.records) {
550       let row = document.createElement("tr");
551       let alpn = record.alpn ? `alpn="${record.alpn.alpn}" ` : "";
552       let noDefaultAlpn = record.noDefaultAlpn ? "noDefaultAlpn " : "";
553       let port = record.port ? `port="${record.port.port}" ` : "";
554       let echConfig = record.echConfig
555         ? `echConfig="${record.echConfig.echConfig}" `
556         : "";
557       let ODoHConfig = record.ODoHConfig
558         ? `odoh="${record.ODoHConfig.ODoHConfig}" `
559         : "";
560       let ipv4hint = "";
561       let ipv6hint = "";
562       if (record.ipv4Hint) {
563         let ipv4Str = "";
564         for (let addr of record.ipv4Hint.address) {
565           ipv4Str += `${addr}, `;
566         }
567         // Remove ", " at the end.
568         ipv4Str = ipv4Str.slice(0, -2);
569         ipv4hint = `ipv4hint="${ipv4Str}" `;
570       }
571       if (record.ipv6Hint) {
572         let ipv6Str = "";
573         for (let addr of record.ipv6Hint.address) {
574           ipv6Str += `${addr}, `;
575         }
576         // Remove ", " at the end.
577         ipv6Str = ipv6Str.slice(0, -2);
578         ipv6hint = `ipv6hint="${ipv6Str}" `;
579       }
581       let str = `${record.priority} ${record.targetName} `;
582       str += `(${alpn}${noDefaultAlpn}${port}`;
583       str += `${ipv4hint}${echConfig}${ipv6hint}`;
584       str += `${ODoHConfig})`;
585       row.appendChild(col(str));
586       new_cont.appendChild(row);
587     }
588   } else {
589     new_cont.appendChild(col(data.error));
590   }
592   parent.replaceChild(new_cont, cont);