commit 26d4ce95369819a1aeefd30fd52f670fc589b5ea Author: Arthur Edelstein arthuredelstein@gmail.com Date: Thu Apr 12 15:36:44 2018 -0700
Bug 24309: Move circuit display to the identity popup.
New circuit display design by Antonela Debiasi. The new design includes a "Guard" indicator, a "Load New Circuit" button, new Photon-compatible style. --- src/chrome.manifest | 1 + src/chrome/content/popup.xul | 12 -- src/chrome/content/tor-circuit-display.js | 130 +++++++++++++++------ src/chrome/content/tor-circuit-display.xul | 34 ++++++ src/chrome/locale/en/torbutton.dtd | 3 +- src/chrome/locale/en/torbutton.properties | 5 +- src/chrome/skin/relay.svg | 10 ++ src/chrome/skin/tor-circuit-display.css | 176 ++++++++++++++++------------- 8 files changed, 247 insertions(+), 124 deletions(-)
diff --git a/src/chrome.manifest b/src/chrome.manifest index 65f55a9..f72fffa 100644 --- a/src/chrome.manifest +++ b/src/chrome.manifest @@ -15,6 +15,7 @@ overlay chrome://browser/content/aboutDialog.xul chrome://torbutton/content/abou # UI customization overlay chrome://browser/content/baseMenuOverlay.xul chrome://torbutton/content/menu-overlay.xul overlay about:preferences chrome://torbutton/content/privacy-prefs-overlay.xul +overlay chrome://browser/content/browser.xul chrome://torbutton/content/tor-circuit-display.xul
# Strings for the about:tbupdate page override chrome://browser/locale/aboutTBUpdate.dtd chrome://torbutton/locale/aboutTBUpdate.dtd diff --git a/src/chrome/content/popup.xul b/src/chrome/content/popup.xul index 9447b70..b088a82 100644 --- a/src/chrome/content/popup.xul +++ b/src/chrome/content/popup.xul @@ -1,6 +1,5 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://torbutton/skin/torbutton.css" type="text/css"?> -<?xml-stylesheet href="chrome://torbutton/skin/tor-circuit-display.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://torbutton/locale/torbutton.dtd">
@@ -51,17 +50,6 @@ insertafter="context-stop" oncommand="torbutton_check_for_update()"/> </vbox> - <vbox id="circuit-display-container"> - <div id="circuit-display" xmlns="http://www.w3.org/1999/xhtml" dir="auto"> - <p id="title">&torbutton.circuit_display.title;</p> - <p id="domain">(example.com):</p> - <ul id="circuit-nodes" dir="auto"> - <li>example A</li> - <li>example B</li> - <li>example C</li> - </ul> - </div> - </vbox> </hbox> </panel> </overlay> diff --git a/src/chrome/content/tor-circuit-display.js b/src/chrome/content/tor-circuit-display.js index b5c24f8..cb43499 100644 --- a/src/chrome/content/tor-circuit-display.js +++ b/src/chrome/content/tor-circuit-display.js @@ -215,7 +215,7 @@ let regionBundle = Services.strings.createBundle( // Convert a country code to a localized country name. // Example: `'de'` -> `'Deutschland'` in German locale. let localizedCountryNameFromCode = function (countryCode) { - if (typeof(countryCode) === "undefined") return undefined; + if (!countryCode) return uiString("unknown_country"); try { return regionBundle.GetStringFromName(countryCode.toLowerCase()); } catch (e) { @@ -249,7 +249,7 @@ let nodeLines = function (nodeData) { (country ? " (" + country + ")" : "")) : // For each non-bridge relay, show its host country and IP. - (country || uiString("unknown_country")) + + country + // As we don't have a bridge, show the IP address // of the node. Use unicode escapes to ensure that // parentheses behave properly in both left-to-right @@ -260,46 +260,90 @@ let nodeLines = function (nodeData) { return result; };
-// __onionSiteRelayLine__. -// When we have an onion site, we simply show the word '(relay)'. -let onionSiteRelayLine = "<li class='relay'>(" + uiString("relay") + ")</li>"; +// __xmlTree(ns, data)__. +// Takes an xml namespace, ns, and a +// data structure representing xml elements like +// [tag, { attr-key: attr-value }, ...xml-children] +// and returns nested xml element objects. +let xmlTree = function xmlTree (ns, data) { + let [type, attrs, ...children] = data; + let element = document.createElementNS(ns, type); + for (let [key, val] of Object.entries(attrs)) { + element.setAttribute(key, val); + } + for (let child of children) { + if (child !== null && child !== undefined) { + element.append(typeof child === "string" ? child : xmlTree(ns, child)); + } + } + return element; +}; + +// __htmlTree(data)__. +// Takes a data structure representing html elements like +// [tag, { attr-key: attr-value }, ...html-children] +// and return nested html element objects. +let htmlTree = data => xmlTree("http://www.w3.org/1999/xhtml", data); + +// __appendHtml(parent, data)__. +// Takes a data structure representing html elements like +// [tag, { attr-key: attr-value }, ...html-children] +// and append nested html element objects to the parent element. +let appendHtml = (parent, data) => parent.appendChild(htmlTree(data)); + +// __circuitCircuitData()__. +// Obtains the circuit used by the given browser. +let currentCircuitData = function (browser) { + if (browser) { + let credentials = browserToCredentialsMap.get(browser); + if (credentials) { + let [SOCKS_username, SOCKS_password] = credentials; + let nodeData = credentialsToNodeDataMap[`${SOCKS_username}|${SOCKS_password}`]; + let domain = SOCKS_username; + return { domain, nodeData }; + } + } + return { domain: null, nodeData: null }; +};
// __updateCircuitDisplay()__. // Updates the Tor circuit display, showing the current domain // and the relay nodes for that domain. let updateCircuitDisplay = function () { - let selectedBrowser = gBrowser.selectedBrowser; - if (selectedBrowser) { - let credentials = browserToCredentialsMap.get(selectedBrowser), - nodeData = null; - if (credentials) { - let [SOCKS_username, SOCKS_password] = credentials; - // Check if we have anything to show for these credentials. - nodeData = credentialsToNodeDataMap[SOCKS_username + "|" + SOCKS_password]; - if (nodeData) { - // Update the displayed domain. - let domain = SOCKS_username; - document.getElementById("domain").innerHTML = "(" + domain + "):"; - // Update the displayed information for the relay nodes. - let lines = nodeLines(nodeData), - nodeInnerHTML = "<li>" + uiString("this_browser") + "</li>"; - for (let line of lines) { - nodeInnerHTML += "<li>" + line + "</li>"; - } - nodeInnerHTML += domain.endsWith(".onion") ? - (onionSiteRelayLine + - onionSiteRelayLine + - onionSiteRelayLine + - "<li>" + uiString("onion_site") + "</li>") : - "<li>" + uiString("internet") + "</li>"; - document.getElementById("circuit-nodes").innerHTML = nodeInnerHTML; + let { domain, nodeData } = currentCircuitData(gBrowser.selectedBrowser); + if (domain && nodeData) { + // Update the displayed information for the relay nodes. + let nodeHtmlList = document.getElementById("circuit-display-nodes"); + let li = (...data) => appendHtml(nodeHtmlList, ["li", {}, ...data]); + nodeHtmlList.innerHTML = ""; + li(uiString("this_browser")); + for (let i = 0; i < nodeData.length; ++i) { + let relayText; + if (nodeData[i].type === "bridge") { + relayText = uiString("tor_bridge") + + ((nodeData[i].bridgeType !== "vanilla") ? `: ${nodeData[i].bridgeType}` : ""); + } else { + relayText = localizedCountryNameFromCode(nodeData[i].countryCode); } - } else { - logger.eclog(5, "no SOCKS credentials found for current document."); + li(relayText, " ", + ["span", { class: "circuit-ip-address" }, nodeData[i].ip], " ", + (i === 0 && nodeData[0].type !== "bridge") ? + ["span", { class: "circuit-guard-info" }, uiString("guard")] : null); } + if (domain.endsWith(".onion")) { + for (let i = 0; i < 3; ++i) { + li(uiString("relay")); + } + } + li(domain); + // Hide the note about guards if we are using a bridge. + document.getElementById("circuit-guard-note-container").style.display = + (nodeData[0].type === "bridge") ? "none" : "block"; + } else { // Only show the Tor circuit if we have credentials and node data. - showCircuitDisplay(credentials && nodeData); + logger.eclog(5, "no SOCKS credentials found for current document."); } + showCircuitDisplay(domain && nodeData); };
// __syncDisplayWithSelectedTab(syncOn)__. @@ -314,7 +358,7 @@ let syncDisplayWithSelectedTab = (function() { } } }; return function (syncOn) { - let popupMenu = document.getElementById("torbutton-context-menu"); + let popupMenu = document.getElementById("identity-popup"); if (syncOn) { // Update the circuit display just before the popup menu is shown. popupMenu.addEventListener("popupshowing", updateCircuitDisplay); @@ -331,6 +375,23 @@ let syncDisplayWithSelectedTab = (function() { }; })();
+// __setupGuardNote()__. +// Call once to show the Guard note as intended. +let setupGuardNote = function () { + let guardNote = document.getElementById("circuit-guard-note-container"); + let guardNoteString = uiString("guard_note"); + let learnMoreString = uiString("learn_more"); + let [noteBefore, name, noteAfter] = guardNoteString.split(/[[]]/); + let localeCode = torbutton_get_general_useragent_locale(); + appendHtml(guardNote, + ["div", {}, + noteBefore, ["span", {class: "circuit-guard-name"}, name], + noteAfter, " ", + ["span", {onclick: `gBrowser.selectedTab = gBrowser.addTab('https://tb-manual.torproject.org/$%7BlocaleCode%7D%27);%60, + class: "circuit-link"}, + learnMoreString]]); +}; + // ## Main function
// __setupDisplay(ipcFile, host, port, password, enablePrefName)__. @@ -338,6 +399,7 @@ let syncDisplayWithSelectedTab = (function() { // the "enablePref" is set to true, and stopped when it is set to false. // A reference to this function (called createTorCircuitDisplay) is exported as a global. let setupDisplay = function (ipcFile, host, port, password, enablePrefName) { + setupGuardNote(); let myController = null, stopCollectingIsolationData = null, stopCollectingBrowserCredentials = null, diff --git a/src/chrome/content/tor-circuit-display.xul b/src/chrome/content/tor-circuit-display.xul new file mode 100644 index 0000000..b4d4ac7 --- /dev/null +++ b/src/chrome/content/tor-circuit-display.xul @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://torbutton/skin/tor-circuit-display.css" type="text/css"?> + +<!DOCTYPE overlay SYSTEM "chrome://torbutton/locale/torbutton.dtd"> + +<overlay id="circuit-display-overlay" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml%22%3E + <panelview id="identity-popup-mainView"> + <!-- Circuit display section --> + <xul:hbox id="circuit-display-container" + class="identity-popup-section" + insertafter="identity-popup-security"> + <xul:vbox id="circuit-display-content" flex="1"> + <xul:label id="circuit-display-headline" + class="identity-popup-headline" + value="&torbutton.circuit_display.title;"/> + <html:ul id="circuit-display-nodes" dir="auto"> + <li>example A</li> + <li>example B</li> + <li>example C</li> + </html:ul> + </xul:vbox> + <xul:vbox id="circuit-reload-content" flex="1"> + <html:button id="circuit-reload-button" + onclick="torbutton_new_circuit()"> + &torbutton.circuit_display.new_circuit; + </html:button> + <xul:hbox id="circuit-guard-note-container"> + </xul:hbox> + </xul:vbox> + </xul:hbox> + </panelview> +</overlay> diff --git a/src/chrome/locale/en/torbutton.dtd b/src/chrome/locale/en/torbutton.dtd index 7ccad6a..a0ef5f9 100644 --- a/src/chrome/locale/en/torbutton.dtd +++ b/src/chrome/locale/en/torbutton.dtd @@ -47,4 +47,5 @@ <!ENTITY torbutton.prefs.sec_limit_typography "Some fonts and math symbols are disabled."> <!ENTITY torbutton.prefs.sec_limit_graphics_and_typography "Some fonts, icons, math symbols, and images are disabled."> <!ENTITY torbutton.prefs.sec_click_to_play_media "Audio and video (HTML5 media) are click-to-play."> -<!ENTITY torbutton.circuit_display.title "Tor circuit for this site"> +<!ENTITY torbutton.circuit_display.title "Tor Circuit"> +<!ENTITY torbutton.circuit_display.new_circuit "New Circuit for this Site"> diff --git a/src/chrome/locale/en/torbutton.properties b/src/chrome/locale/en/torbutton.properties index 2391e69..468a532 100644 --- a/src/chrome/locale/en/torbutton.properties +++ b/src/chrome/locale/en/torbutton.properties @@ -2,9 +2,12 @@ torbutton.circuit_display.internet = Internet torbutton.circuit_display.ip_unknown = IP unknown torbutton.circuit_display.onion_site = Onion site torbutton.circuit_display.this_browser = This browser -torbutton.circuit_display.relay = relay +torbutton.circuit_display.relay = Relay torbutton.circuit_display.tor_bridge = Bridge torbutton.circuit_display.unknown_country = Unknown country +torbutton.circuit_display.guard = Guard +torbutton.circuit_display.guard_note = Your [Guard] node may not change. +torbutton.circuit_display.learn_more = Learn more torbutton.content_sizer.margin_tooltip = Tor Browser adds this margin to make the width and height of your window less distinctive, and thus reduces the ability of people to track you online. torbutton.panel.tooltip.disabled = Click to enable Tor torbutton.panel.tooltip.enabled = Click to disable Tor diff --git a/src/chrome/skin/relay.svg b/src/chrome/skin/relay.svg new file mode 100644 index 0000000..b2f5f16 --- /dev/null +++ b/src/chrome/skin/relay.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch --> + <title>relay</title> + <desc>Created with Sketch.</desc> + <defs></defs> + <g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-448.000000, -65.000000)"> + <path d="M462.904942,71.0268217 L467.575247,74.1403583 C468.095041,73.9073638 468.671277,73.7777778 469.277778,73.7777778 C471.57895,73.7777778 473.444444,75.6432726 473.444444,77.9444444 C473.444444,80.2456163 471.57895,82.1111111 469.277778,82.1111111 C468.296528,82.1111111 467.394661,81.7717448 466.682726,81.2041667 L462.234549,83.9842882 C462.366276,84.5801474 462.366276,85.197587 462.234549,85.7934462 L466.682726,88.5735677 C467.394661,88.006033 468.296528,87.6666667 469.277778,87.6666667 C471.57895,87.6666667 473.444444,89.5321615 473.444444,91.8333333 C473.444444,94.1345052 471.57895,96 469.277778,96 C466.976606,96 465.111111,94.1345052 465.111111,91.8333333 C465.111023,91.5291178 465.144147,91.2258017 465.209896,90.928776 L460.761719,88.1486545 C460.049783,88.7161892 459.147917,89.0555556 458.166667,89.0555556 C455.865495,89.0555556 454,87.1900608 454,84.8888889 C454,82.587717 455.865495,80.7222222 458.166667,80.7222222 C459.147917,80.7222222 460.049783,81.0615885 46 0.761719,81.6291233 L465.209896,78.8490017 C465.145399,78.5577257 465.111111,78.2551215 465.111111,77.9444444 C465.111111,77.2224682 465.29474,76.5433776 465.617841,75.95133 L461.36038,73.1130222 C460.739536,73.5327021 459.990981,73.7777778 459.185185,73.7777778 C457.037411,73.7777778 455.296296,72.0366629 455.296296,69.8888889 C455.296296,67.7411149 457.037411,66 459.185185,66 C461.332959,66 463.074074,67.7411149 463.074074,69.8888889 C463.074074,70.2848045 463.01491,70.6669018 462.904942,71.0268217 Z" id="Line-2" fill="#000000" fill-rule="nonzero"></path> + </g> +</svg> \ No newline at end of file diff --git a/src/chrome/skin/tor-circuit-display.css b/src/chrome/skin/tor-circuit-display.css index d3b5edc..71c283c 100644 --- a/src/chrome/skin/tor-circuit-display.css +++ b/src/chrome/skin/tor-circuit-display.css @@ -4,110 +4,134 @@ a domain, and a bulleted list.
Each bullet in the circuit node list is supposed to represent a Tor circuit node, and lines drawn between them to represent Tor network inter-relay connections. - -CSS for line between bullets is derived in part from https://jsfiddle.net/5JP8Q/ */
-/* The circuit-display div encloses all other HTML elements. - Color the background to make circuit display distinct - from the rest of the popup. */ -div#circuit-display { - background-color: #e8f4f4; - font-family: Arial; - width: 100%; - height: 100%; - padding: 8px; - /* Magically keep popup menu from clipping div: */ - display: table-cell; + +#circuit-display-content { + background-image: url(chrome://torbutton/skin/relay.svg); + background-position: 1em 1em; + background-repeat: no-repeat; + background-size: 24px auto; + background-color: rgba(255,255,255,0.4); + background-blend-mode: lighten; cursor: default; + padding: 0.5em 0px 0.5em; + padding-inline-end: 1em; + padding-inline-start: calc(2em + 24px); + width: 100%; }
-/* Format the title text. */ -div#circuit-display p#title { - font-size: 15px; - font-weight: bold; - color: #2c26a7; - margin: 0; +#circuit-display-content:-moz-locale-dir(rtl) { + background-position: calc(100% - 1em) 1em; +} + +#circuit-reload-content { + background-color: #f7f7f7; + cursor: default; + padding: 1em 0px 1em; + padding-inline-end: 3em; + padding-inline-start: calc(2em + 24px); + width: 100%; }
/* Format the domain text. */ -div#circuit-display p#domain { - font-size: 13px; - color: black; - margin: 4px; +#circuit-display-domain { + opacity: 0.8; +} + +#circuit-div { + position: relative; + margin-inline-start: 6px; }
/* Format the circuit node list. */ -ul#circuit-nodes { - font-family: Arial; - font-size: 14px; - color: black; - margin-top: 8px; - padding-left: 8px; - padding-right: 8px; -} - -/* Hide default bullets, and position pseudoelements (our new bullets) relative - to the list items. */ -ul#circuit-nodes li { +ul#circuit-display-nodes { + /* font-family: Arial; */ + font-size: 16px; + line-height: 26px; + /* color: black;*/ + margin-top: 4px; + margin-bottom: 2px; + padding-inline-start: 4px; +} + +/* Hide default bullets, and draw our own bullets */ +ul#circuit-display-nodes li { + background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2715%27%20height%3D%2740%27%3E%3Cg%20style%3D%27stroke%3A%234a4a4a%3Bstroke-width%3A3px%3Bfill%3Awhite%3B%27%3E%3Cline%20x1%3D%2750%25%27%20y1%3D%270%25%27%20x2%3D%2750%25%27%20y2%3D%27100%25%27%2F%3E%3Ccircle%20cx%3D%2750%25%27%20cy%3D%2750%25%27%20r%3D%274.5%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); + background-position: left center; + background-repeat: no-repeat; list-style: none; - position:relative; + padding-inline-start: 1.5em; }
-/* Pad the list item text at left or right to be tastefully - separated from bullets. */ -ul#circuit-nodes li:-moz-dir(ltr) { - padding-left: 18px; +ul#circuit-display-nodes li:-moz-locale-dir(rtl) { + background-position: right center; } -ul#circuit-nodes li:-moz-dir(rtl) { - padding-right: 18px; + +ul#circuit-display-nodes li:first-child { + background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2715%27%20height%3D%2740%27%3E%3Cg%20style%3D%27stroke%3A%234a4a4a%3Bstroke-width%3A3px%3Bfill%3Awhite%3B%27%3E%3Cline%20x1%3D%2750%25%27%20y1%3D%2750%25%27%20x2%3D%2750%25%27%20y2%3D%27100%25%27%2F%3E%3Ccircle%20cx%3D%2750%25%27%20cy%3D%2750%25%27%20r%3D%274.5%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +ul#circuit-display-nodes li:last-child { + background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2715%27%20height%3D%2740%27%3E%3Cg%20style%3D%27stroke%3A%234a4a4a%3Bstroke-width%3A3px%3Bfill%3Awhite%3B%27%3E%3Cline%20x1%3D%2750%25%27%20y1%3D%270%25%27%20x2%3D%2750%25%27%20y2%3D%2750%25%27%2F%3E%3Ccircle%20cx%3D%2750%25%27%20cy%3D%2750%25%27%20r%3D%274.5%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +.circuit-guard-help { + -moz-context-properties: fill, stroke; + fill: #6200a4; + stroke: #6200a4; +} + +.circuit-ip-address { + font-size: 80%; + opacity: 0.4; + padding-inline-start: 3px; }
-/* Drawn bullets, centered vertically for each list item. */ -ul#circuit-nodes li:after { - /* bullets */ - content: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2711%27%20height%3D%2711%27%3E%3Ccircle%20cx%3D%275.5%27%20cy%3D%275.5%27%20r%3D%274%27%20style%3D%27stroke%3A%20%23195021%3B%20stroke-width%3A%202px%3B%20fill%3A%20white%3B%27%2F%3E%3C%2Fsvg%3E"); - position: absolute; - top: 1px; +.circuit-guard-info { + font-size: 80%; + font-weight: bold; + color: #6200a4; + padding-inline-start: 3px; }
-/* Move the bullets to the far left or right, - depending on text directionality. */ -ul#circuit-nodes li:-moz-dir(ltr):after { - left: 0px; +#circuit-reload-button { + background-color: #0060df; + border-radius: 3px; + border: none; + color: white; + cursor: pointer; + font-size: 18px; + padding: 10px; } -ul#circuit-nodes li:-moz-dir(rtl):after { - right: 0px; + +#circuit-reload-button:hover { + background-color: #0058cf; }
-/* Draw a connecting vertical line through the bullets. */ -ul#circuit-nodes li:before { - content: ""; - position: absolute; - border-left: 3px solid #4d363a; - height: 100%; - width: 0px; +#circuit-reload-button:active { + background-color: #0050bf; }
-/* Position lines through the middle of the bullet. */ -ul#circuit-nodes li:-moz-dir(ltr):before { - left: 4px; +#circuit-guard-note-container { + margin-top: 10px; } -ul#circuit-nodes li:-moz-dir(rtl):before { - right: 4px; + +#circuit-guard-note-container div { + margin-left: 3px; + margin-right: 3px; }
-/* Shorten the first list item's line so that it starts under the bullet. */ -ul#circuit-nodes li:first-child:before { - top: 50%; +.circuit-guard-name { + font-weight: bold; + color: #6200a4; }
-/* Shorten the last list item's line so that it ends under the bullet. */ -ul#circuit-nodes li:last-child:before { - height: 50%; +.circuit-link { + cursor: pointer; + color: #0000ee; }
-/* Onion site relay node text should be gray. */ -ul#circuit-nodes li.relay { - color: gray; -} \ No newline at end of file +.circuit-link:hover { + text-decoration: underline; +}