Richard Pospesel pushed to branch tor-browser-102.6.0esr-12.5-1 at The Tor Project / Applications / Tor Browser
Commits: 9714187f by Henry Wilkes at 2023-01-09T20:07:59+00:00 fixup! Bug 16940: After update, load local change notes.
We make a number of related changed to improve the semantics and accessibility of the about:tbupdate page:
+ We parse and show the release notes as a bullet list. + We use paragraphs, headings and bullet list to structure content. + We stop using fixed "px" font sizes and use relative sizes instead. + Add a lang="en-US" and dir="ltr" attribute to the english text.
- - - - -
4 changed files:
- browser/actors/AboutTBUpdateParent.jsm - browser/base/content/abouttbupdate/aboutTBUpdate.css - browser/base/content/abouttbupdate/aboutTBUpdate.js - browser/base/content/abouttbupdate/aboutTBUpdate.xhtml
Changes:
===================================== browser/actors/AboutTBUpdateParent.jsm ===================================== @@ -8,7 +8,6 @@ this.EXPORTED_SYMBOLS = ["AboutTBUpdateParent"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); const { AppConstants } = ChromeUtils.import( "resource://gre/modules/AppConstants.jsm" ); @@ -74,41 +73,72 @@ class AboutTBUpdateParent extends JSWindowActorParent { f.append("Docs"); f.append("ChangeLog.txt");
- let s = await IOUtils.readUTF8(f.path); - - // Truncate at the first empty line. - s = s.replace(/[\r\n][\r\n][\s\S]*$/m, ""); - - // Split into first line (version plus releaseDate) and - // remainder (releaseNotes). - // This first match() uses multiline mode with two capture groups: - // first line: (.*$) - // remaining lines: ([\s\S]+) - // [\s\S] matches all characters including end of line. This trick - // is needed because when using JavaScript regex in multiline mode, - // . does not match an end of line character. - let matchArray = s.match(/(.*$)\s*([\s\S]+)/m); - if (matchArray && matchArray.length == 3) { - info.releaseNotes = matchArray[2]; - let line1 = matchArray[1]; - // Extract the version and releaseDate. The first line looks like: - // Tor Browser 8.5 -- May 1 2019 - // The regex uses two capture groups: - // text that does not include a hyphen: (^[^-]*) - // remaining text: (.*$) - // In between we match optional whitespace, one or more hyphens, and - // optional whitespace by using: \s*-+\s* - matchArray = line1.match(/(^[^-]*)\s*-+\s*(.*$)/); - if (matchArray && matchArray.length == 3) { - info.version = matchArray[1]; - info.releaseDate = matchArray[2]; + // NOTE: We load in the entire file, but only use the first few lines + // before the first blank line. + const logLines = (await IOUtils.readUTF8(f.path)) + .replace(/\n\r?\n.*/ms, "") + .split(/\n\r?/); + + // Read the first line to get the version and date. + // Assume everything after the last "-" is the date. + const firstLine = logLines.shift(); + const match = firstLine?.match(/(.*)-+(.*)/); + if (match) { + info.version = match[1].trim(); + info.releaseDate = match[2].trim(); + } else { + // No date. + info.version = firstLine?.trim(); + } + + // We want to read the rest of the release notes as a tree. Each entry + // will contain the text for that line. + // We choose a negative index for the top node of this tree to ensure no + // line will appear less indented. + const topEntry = { indent: -1, children: undefined }; + let prevEntry = topEntry; + + for (let line of logLines) { + const indent = line.match(/^ */)[0]; + line = line.trim(); + if (line.startsWith("*")) { + // Treat as a bullet point. + let entry = { + text: line.replace(/^*\s/, ""), + indent: indent.length, + }; + let parentEntry; + if (entry.indent > prevEntry.indent) { + // A sub-list of the previous item. + prevEntry.children = []; + parentEntry = prevEntry; + } else { + // Same list or end of sub-list. + // Search for the first parent whose indent comes before ours. + parentEntry = prevEntry.parent; + while (entry.indent <= parentEntry.indent) { + parentEntry = parentEntry.parent; + } + } + entry.parent = parentEntry; + parentEntry.children.push(entry); + prevEntry = entry; + } else if (prevEntry === topEntry) { + // Unexpected, missing bullet point on first line. + // Place as its own bullet point instead, and set as prevEntry for the + // next loop. + prevEntry = { text: line, indent: indent.length, parent: topEntry }; + topEntry.children = [prevEntry]; } else { - info.version = line1; // Match failed: return entire line in version. + // Append to the previous bullet point. + prevEntry.text += ` ${line}`; } - } else { - info.releaseNotes = s; // Only one line: use as releaseNotes. } - } catch (e) {} + + info.releaseNotes = topEntry.children; + } catch (e) { + Cu.reportError(e); + }
return info; }
===================================== browser/base/content/abouttbupdate/aboutTBUpdate.css ===================================== @@ -14,61 +14,54 @@ body { font-family: Helvetica, Arial, sans-serif; color: var(--abouttor-text-color); background-color: var(--abouttor-bg-toron-color); - background-attachment: fixed; - background-size: 100% 100%; -} - -a { - color: var(--abouttor-text-color); -} - -.two-column-grid { - display: inline-grid; + margin-block: 40px; + margin-inline: 50px; + display: grid; grid-template-columns: auto auto; - grid-column-gap: 50px; - margin: 10px 0px 0px 50px; + align-items: baseline; + gap: 40px 50px; }
-.two-column-grid div { - margin-top: 40px; - align-self: baseline; /* Align baseline of text across the row. */ +body > *:not([hidden]) { + display: contents; }
.label-column { - font-size: 14px; - font-weight: 400; + grid-column: 1; }
-/* - * Use a reduced top margin to bring the row that contains the - * "visit our website" link closer to the row that precedes it. This - * looks better because the "visit our website" row does not have a - * label in the left column. - */ -div.more-info-row { - margin-top: 5px; - font-size: 14px; +.content { + grid-column: 2; + font-family: monospace; + line-height: 1.4; +} + +.label-column, .content { + margin: 0; + padding: 0; + font-size: 1rem; + font-weight: normal; }
-#version-content { - font-size: 50px; - font-weight: 300; +a { + color: inherit; }
-body:not([havereleasedate]) .release-date-cell { - display: none; +.no-line-break { + white-space: nowrap; }
-#releasedate-content { - font-size: 17px; +ul { + padding-inline: 1em 0; }
-#releasenotes-label { - align-self: start; /* Anchor "Release Notes" label at the top. */ +h3, h4 { + font-size: 1.1rem; + font-weight: bold; }
-#releasenotes-content { - font-family: monospace; - font-size: 15px; - white-space: pre; +h3.build-system-heading { + font-size: 1.5rem; + font-weight: normal; + margin-block-start: 3em; }
===================================== browser/base/content/abouttbupdate/aboutTBUpdate.js ===================================== @@ -5,23 +5,105 @@
/* eslint-env mozilla/frame-script */
-// aData may contain the following string properties: -// version -// releaseDate -// moreInfoURL -// releaseNotes -function onUpdate(aData) { - document.getElementById("version-content").textContent = aData.version; - if (aData.releaseDate) { - document.body.setAttribute("havereleasedate", "true"); - document.getElementById("releasedate-content").textContent = - aData.releaseDate; +/** + * An object representing a bullet point in the release notes. + * + * typedef {Object} ReleaseBullet + * @property {string} text - The text for this bullet point. + * @property {?Array<ReleaseBullet>} children - A sub-list of bullet points. + */ + +/** + * Fill an element with the given list of release bullet points. + * + * @param {Element} container - The element to fill with bullet points. + * @param {Array<ReleaseBullet>} bulletPoints - The list of bullet points. + * @param {string} [childTag="h3"] - The element tag name to use for direct + * children. Initially, the children are h3 sub-headings. + */ +function fillReleaseNotes(container, bulletPoints, childTag = "h3") { + for (const { text, children } of bulletPoints) { + const childEl = document.createElement(childTag); + // Keep dashes like "[tor-browser]" on the same line by nowrapping the word. + for (const [index, part] of text.split(/(\S+-\S+)/).entries()) { + if (!part) { + continue; + } + const span = document.createElement("span"); + span.textContent = part; + span.classList.toggle("no-line-break", index % 2); + childEl.appendChild(span); + } + container.appendChild(childEl); + if (children) { + if (childTag == "h3" && text.toLowerCase() === "build system") { + // Special case: treat the "Build System" heading's children as + // sub-headings. + childEl.classList.add("build-system-heading"); + fillReleaseNotes(container, children, "h4"); + } else { + const listEl = document.createElement("ul"); + fillReleaseNotes(listEl, children, "li"); + if (childTag == "li") { + // Insert within the "li" element. + childEl.appendChild(listEl); + } else { + container.appendChild(listEl); + } + } + } + } +} + +/** + * Set the content for the specified container, or hide it if we have no + * content. + * + * @template C + * @param {string} containerId - The id for the container. + * @param {?C} content - The content for this container, or a falsey value if + * the container has no content. + * @param {function(contentEl: Elemenet, content: C)} [fillContent] - A function + * to fill the ".content" contentEl with the given 'content'. If unspecified, + * the 'content' will become the contentEl's textContent. + */ +function setContent(containerId, content, fillContent) { + const container = document.getElementById(containerId); + if (!content) { + container.hidden = true; + return; } + const contentEl = container.querySelector(".content"); + // Release notes are only in English. + contentEl.setAttribute("lang", "en-US"); + contentEl.setAttribute("dir", "ltr"); + if (fillContent) { + fillContent(contentEl, content); + } else { + contentEl.textContent = content; + } +} + +/** + * Callback when we receive the update details. + * + * @param {Object} aData - The update details. + * @param {?string} aData.version - The update version. + * @param {?string} aData.releaseDate - The release date. + * @param {?string} aData.moreInfoURL - A URL for more info. + * @param {?Array<ReleaseBullet>} aData.releaseNotes - Release notes as bullet + * points. + */ +function onUpdate(aData) { + setContent("version-row", aData.version); + setContent("releasedate-row", aData.releaseDate); + setContent("releasenotes", aData.releaseNotes, fillReleaseNotes); + if (aData.moreInfoURL) { document.getElementById("infolink").setAttribute("href", aData.moreInfoURL); + } else { + document.getElementById("fullinfo").hidden = true; } - document.getElementById("releasenotes-content").textContent = - aData.releaseNotes; }
RPMSendQuery("FetchUpdateData").then(onUpdate);
===================================== browser/base/content/abouttbupdate/aboutTBUpdate.xhtml ===================================== @@ -21,19 +21,26 @@ type="text/javascript"/> </head> <body dir="&locale.dir;"> -<div class="two-column-grid"> - <div class="label-column">&aboutTBUpdate.version;</div> - <div id="version-content"/> - - <div class="label-column release-date-cell">&aboutTBUpdate.releaseDate;</div> - <div id="releasedate-content" class="release-date-cell"/> - - <div class="more-info-row"/> - <div class="more-info-row">&aboutTBUpdate.linkPrefix;<a id="infolink">&aboutTBUpdate.linkLabel;</a>&aboutTBUpdate.linkSuffix;</div> - - <div id="releasenotes-label" - class="label-column">&aboutTBUpdate.releaseNotes;</div> - <div id="releasenotes-content"></div> -</div> + <!-- NOTE: We don't use the <dl>, <dt> and <dd> elements to form name-value + - pairs because this semantics is relatively new, whilst firefox + - currently still maps these to the more limited "definitionlist", "term" + - and "definition" roles. --> + <div id="version-row"> + <span class="label-column">&aboutTBUpdate.version;</span> + <span class="content"></span> + </div> + <div id="releasedate-row"> + <span class="label-column">&aboutTBUpdate.releaseDate;</span> + <span class="content"></span> + </div> + <div id="fullinfo"> + <p class="content"> + &aboutTBUpdate.linkPrefix;<a id="infolink">&aboutTBUpdate.linkLabel;</a>&aboutTBUpdate.linkSuffix; + </p> + </div> + <section id="releasenotes"> + <h2 class="label-column">&aboutTBUpdate.releaseNotes;</h2> + <div class="content"></div> + </section> </body> </html>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/9714187f...