Richard Pospesel pushed to branch tor-browser-102.6.0esr-12.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
4b8dca0d
by Pier Angelo Vendrame at 2023-01-09T20:34:21+00:00
-
7774a06e
by Henry Wilkes at 2023-01-09T20:34:31+00:00
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:
| ... | ... | @@ -8,7 +8,6 @@ |
| 8 | 8 | this.EXPORTED_SYMBOLS = ["AboutTBUpdateParent"];
|
| 9 | 9 | |
| 10 | 10 | const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
| 11 | -const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
|
| 12 | 11 | const { AppConstants } = ChromeUtils.import(
|
| 13 | 12 | "resource://gre/modules/AppConstants.jsm"
|
| 14 | 13 | );
|
| ... | ... | @@ -22,9 +21,9 @@ const kRequestUpdateMessageName = "FetchUpdateData"; |
| 22 | 21 | * implementation.
|
| 23 | 22 | */
|
| 24 | 23 | class AboutTBUpdateParent extends JSWindowActorParent {
|
| 25 | - receiveMessage(aMessage) {
|
|
| 24 | + async receiveMessage(aMessage) {
|
|
| 26 | 25 | if (aMessage.name == kRequestUpdateMessageName) {
|
| 27 | - return this.releaseNoteInfo;
|
|
| 26 | + return this.getReleaseNoteInfo();
|
|
| 28 | 27 | }
|
| 29 | 28 | return undefined;
|
| 30 | 29 | }
|
| ... | ... | @@ -51,7 +50,7 @@ class AboutTBUpdateParent extends JSWindowActorParent { |
| 51 | 50 | // On Mac OS, when building with --enable-tor-browser-data-outside-app-dir
|
| 52 | 51 | // to support Gatekeeper signing, the ChangeLog.txt file is located in
|
| 53 | 52 | // TorBrowser.app/Contents/Resources/TorBrowser/Docs/.
|
| 54 | - get releaseNoteInfo() {
|
|
| 53 | + async getReleaseNoteInfo() {
|
|
| 55 | 54 | let info = { moreInfoURL: this.moreInfoURL };
|
| 56 | 55 | |
| 57 | 56 | try {
|
| ... | ... | @@ -74,46 +73,72 @@ class AboutTBUpdateParent extends JSWindowActorParent { |
| 74 | 73 | f.append("Docs");
|
| 75 | 74 | f.append("ChangeLog.txt");
|
| 76 | 75 | |
| 77 | - let fs = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
|
|
| 78 | - Ci.nsIFileInputStream
|
|
| 79 | - );
|
|
| 80 | - fs.init(f, -1, 0, 0);
|
|
| 81 | - let s = NetUtil.readInputStreamToString(fs, fs.available());
|
|
| 82 | - fs.close();
|
|
| 83 | - |
|
| 84 | - // Truncate at the first empty line.
|
|
| 85 | - s = s.replace(/[\r\n][\r\n][\s\S]*$/m, "");
|
|
| 86 | - |
|
| 87 | - // Split into first line (version plus releaseDate) and
|
|
| 88 | - // remainder (releaseNotes).
|
|
| 89 | - // This first match() uses multiline mode with two capture groups:
|
|
| 90 | - // first line: (.*$)
|
|
| 91 | - // remaining lines: ([\s\S]+)
|
|
| 92 | - // [\s\S] matches all characters including end of line. This trick
|
|
| 93 | - // is needed because when using JavaScript regex in multiline mode,
|
|
| 94 | - // . does not match an end of line character.
|
|
| 95 | - let matchArray = s.match(/(.*$)\s*([\s\S]+)/m);
|
|
| 96 | - if (matchArray && matchArray.length == 3) {
|
|
| 97 | - info.releaseNotes = matchArray[2];
|
|
| 98 | - let line1 = matchArray[1];
|
|
| 99 | - // Extract the version and releaseDate. The first line looks like:
|
|
| 100 | - // Tor Browser 8.5 -- May 1 2019
|
|
| 101 | - // The regex uses two capture groups:
|
|
| 102 | - // text that does not include a hyphen: (^[^-]*)
|
|
| 103 | - // remaining text: (.*$)
|
|
| 104 | - // In between we match optional whitespace, one or more hyphens, and
|
|
| 105 | - // optional whitespace by using: \s*-+\s*
|
|
| 106 | - matchArray = line1.match(/(^[^-]*)\s*-+\s*(.*$)/);
|
|
| 107 | - if (matchArray && matchArray.length == 3) {
|
|
| 108 | - info.version = matchArray[1];
|
|
| 109 | - info.releaseDate = matchArray[2];
|
|
| 76 | + // NOTE: We load in the entire file, but only use the first few lines
|
|
| 77 | + // before the first blank line.
|
|
| 78 | + const logLines = (await IOUtils.readUTF8(f.path))
|
|
| 79 | + .replace(/\n\r?\n.*/ms, "")
|
|
| 80 | + .split(/\n\r?/);
|
|
| 81 | + |
|
| 82 | + // Read the first line to get the version and date.
|
|
| 83 | + // Assume everything after the last "-" is the date.
|
|
| 84 | + const firstLine = logLines.shift();
|
|
| 85 | + const match = firstLine?.match(/(.*)-+(.*)/);
|
|
| 86 | + if (match) {
|
|
| 87 | + info.version = match[1].trim();
|
|
| 88 | + info.releaseDate = match[2].trim();
|
|
| 89 | + } else {
|
|
| 90 | + // No date.
|
|
| 91 | + info.version = firstLine?.trim();
|
|
| 92 | + }
|
|
| 93 | + |
|
| 94 | + // We want to read the rest of the release notes as a tree. Each entry
|
|
| 95 | + // will contain the text for that line.
|
|
| 96 | + // We choose a negative index for the top node of this tree to ensure no
|
|
| 97 | + // line will appear less indented.
|
|
| 98 | + const topEntry = { indent: -1, children: undefined };
|
|
| 99 | + let prevEntry = topEntry;
|
|
| 100 | + |
|
| 101 | + for (let line of logLines) {
|
|
| 102 | + const indent = line.match(/^ */)[0];
|
|
| 103 | + line = line.trim();
|
|
| 104 | + if (line.startsWith("*")) {
|
|
| 105 | + // Treat as a bullet point.
|
|
| 106 | + let entry = {
|
|
| 107 | + text: line.replace(/^\*\s/, ""),
|
|
| 108 | + indent: indent.length,
|
|
| 109 | + };
|
|
| 110 | + let parentEntry;
|
|
| 111 | + if (entry.indent > prevEntry.indent) {
|
|
| 112 | + // A sub-list of the previous item.
|
|
| 113 | + prevEntry.children = [];
|
|
| 114 | + parentEntry = prevEntry;
|
|
| 115 | + } else {
|
|
| 116 | + // Same list or end of sub-list.
|
|
| 117 | + // Search for the first parent whose indent comes before ours.
|
|
| 118 | + parentEntry = prevEntry.parent;
|
|
| 119 | + while (entry.indent <= parentEntry.indent) {
|
|
| 120 | + parentEntry = parentEntry.parent;
|
|
| 121 | + }
|
|
| 122 | + }
|
|
| 123 | + entry.parent = parentEntry;
|
|
| 124 | + parentEntry.children.push(entry);
|
|
| 125 | + prevEntry = entry;
|
|
| 126 | + } else if (prevEntry === topEntry) {
|
|
| 127 | + // Unexpected, missing bullet point on first line.
|
|
| 128 | + // Place as its own bullet point instead, and set as prevEntry for the
|
|
| 129 | + // next loop.
|
|
| 130 | + prevEntry = { text: line, indent: indent.length, parent: topEntry };
|
|
| 131 | + topEntry.children = [prevEntry];
|
|
| 110 | 132 | } else {
|
| 111 | - info.version = line1; // Match failed: return entire line in version.
|
|
| 133 | + // Append to the previous bullet point.
|
|
| 134 | + prevEntry.text += ` ${line}`;
|
|
| 112 | 135 | }
|
| 113 | - } else {
|
|
| 114 | - info.releaseNotes = s; // Only one line: use as releaseNotes.
|
|
| 115 | 136 | }
|
| 116 | - } catch (e) {}
|
|
| 137 | + |
|
| 138 | + info.releaseNotes = topEntry.children;
|
|
| 139 | + } catch (e) {
|
|
| 140 | + Cu.reportError(e);
|
|
| 141 | + }
|
|
| 117 | 142 | |
| 118 | 143 | return info;
|
| 119 | 144 | }
|
| ... | ... | @@ -14,61 +14,54 @@ body { |
| 14 | 14 | font-family: Helvetica, Arial, sans-serif;
|
| 15 | 15 | color: var(--abouttor-text-color);
|
| 16 | 16 | background-color: var(--abouttor-bg-toron-color);
|
| 17 | - background-attachment: fixed;
|
|
| 18 | - background-size: 100% 100%;
|
|
| 19 | -}
|
|
| 20 | - |
|
| 21 | -a {
|
|
| 22 | - color: var(--abouttor-text-color);
|
|
| 23 | -}
|
|
| 24 | - |
|
| 25 | -.two-column-grid {
|
|
| 26 | - display: inline-grid;
|
|
| 17 | + margin-block: 40px;
|
|
| 18 | + margin-inline: 50px;
|
|
| 19 | + display: grid;
|
|
| 27 | 20 | grid-template-columns: auto auto;
|
| 28 | - grid-column-gap: 50px;
|
|
| 29 | - margin: 10px 0px 0px 50px;
|
|
| 21 | + align-items: baseline;
|
|
| 22 | + gap: 40px 50px;
|
|
| 30 | 23 | }
|
| 31 | 24 | |
| 32 | -.two-column-grid div {
|
|
| 33 | - margin-top: 40px;
|
|
| 34 | - align-self: baseline; /* Align baseline of text across the row. */
|
|
| 25 | +body > *:not([hidden]) {
|
|
| 26 | + display: contents;
|
|
| 35 | 27 | }
|
| 36 | 28 | |
| 37 | 29 | .label-column {
|
| 38 | - font-size: 14px;
|
|
| 39 | - font-weight: 400;
|
|
| 30 | + grid-column: 1;
|
|
| 40 | 31 | }
|
| 41 | 32 | |
| 42 | -/*
|
|
| 43 | - * Use a reduced top margin to bring the row that contains the
|
|
| 44 | - * "visit our website" link closer to the row that precedes it. This
|
|
| 45 | - * looks better because the "visit our website" row does not have a
|
|
| 46 | - * label in the left column.
|
|
| 47 | - */
|
|
| 48 | -div.more-info-row {
|
|
| 49 | - margin-top: 5px;
|
|
| 50 | - font-size: 14px;
|
|
| 33 | +.content {
|
|
| 34 | + grid-column: 2;
|
|
| 35 | + font-family: monospace;
|
|
| 36 | + line-height: 1.4;
|
|
| 37 | +}
|
|
| 38 | + |
|
| 39 | +.label-column, .content {
|
|
| 40 | + margin: 0;
|
|
| 41 | + padding: 0;
|
|
| 42 | + font-size: 1rem;
|
|
| 43 | + font-weight: normal;
|
|
| 51 | 44 | }
|
| 52 | 45 | |
| 53 | -#version-content {
|
|
| 54 | - font-size: 50px;
|
|
| 55 | - font-weight: 300;
|
|
| 46 | +a {
|
|
| 47 | + color: inherit;
|
|
| 56 | 48 | }
|
| 57 | 49 | |
| 58 | -body:not([havereleasedate]) .release-date-cell {
|
|
| 59 | - display: none;
|
|
| 50 | +.no-line-break {
|
|
| 51 | + white-space: nowrap;
|
|
| 60 | 52 | }
|
| 61 | 53 | |
| 62 | -#releasedate-content {
|
|
| 63 | - font-size: 17px;
|
|
| 54 | +ul {
|
|
| 55 | + padding-inline: 1em 0;
|
|
| 64 | 56 | }
|
| 65 | 57 | |
| 66 | -#releasenotes-label {
|
|
| 67 | - align-self: start; /* Anchor "Release Notes" label at the top. */
|
|
| 58 | +h3, h4 {
|
|
| 59 | + font-size: 1.1rem;
|
|
| 60 | + font-weight: bold;
|
|
| 68 | 61 | }
|
| 69 | 62 | |
| 70 | -#releasenotes-content {
|
|
| 71 | - font-family: monospace;
|
|
| 72 | - font-size: 15px;
|
|
| 73 | - white-space: pre;
|
|
| 63 | +h3.build-system-heading {
|
|
| 64 | + font-size: 1.5rem;
|
|
| 65 | + font-weight: normal;
|
|
| 66 | + margin-block-start: 3em;
|
|
| 74 | 67 | } |
| ... | ... | @@ -5,23 +5,105 @@ |
| 5 | 5 | |
| 6 | 6 | /* eslint-env mozilla/frame-script */
|
| 7 | 7 | |
| 8 | -// aData may contain the following string properties:
|
|
| 9 | -// version
|
|
| 10 | -// releaseDate
|
|
| 11 | -// moreInfoURL
|
|
| 12 | -// releaseNotes
|
|
| 13 | -function onUpdate(aData) {
|
|
| 14 | - document.getElementById("version-content").textContent = aData.version;
|
|
| 15 | - if (aData.releaseDate) {
|
|
| 16 | - document.body.setAttribute("havereleasedate", "true");
|
|
| 17 | - document.getElementById("releasedate-content").textContent =
|
|
| 18 | - aData.releaseDate;
|
|
| 8 | +/**
|
|
| 9 | + * An object representing a bullet point in the release notes.
|
|
| 10 | + *
|
|
| 11 | + * typedef {Object} ReleaseBullet
|
|
| 12 | + * @property {string} text - The text for this bullet point.
|
|
| 13 | + * @property {?Array<ReleaseBullet>} children - A sub-list of bullet points.
|
|
| 14 | + */
|
|
| 15 | + |
|
| 16 | +/**
|
|
| 17 | + * Fill an element with the given list of release bullet points.
|
|
| 18 | + *
|
|
| 19 | + * @param {Element} container - The element to fill with bullet points.
|
|
| 20 | + * @param {Array<ReleaseBullet>} bulletPoints - The list of bullet points.
|
|
| 21 | + * @param {string} [childTag="h3"] - The element tag name to use for direct
|
|
| 22 | + * children. Initially, the children are h3 sub-headings.
|
|
| 23 | + */
|
|
| 24 | +function fillReleaseNotes(container, bulletPoints, childTag = "h3") {
|
|
| 25 | + for (const { text, children } of bulletPoints) {
|
|
| 26 | + const childEl = document.createElement(childTag);
|
|
| 27 | + // Keep dashes like "[tor-browser]" on the same line by nowrapping the word.
|
|
| 28 | + for (const [index, part] of text.split(/(\S+-\S+)/).entries()) {
|
|
| 29 | + if (!part) {
|
|
| 30 | + continue;
|
|
| 31 | + }
|
|
| 32 | + const span = document.createElement("span");
|
|
| 33 | + span.textContent = part;
|
|
| 34 | + span.classList.toggle("no-line-break", index % 2);
|
|
| 35 | + childEl.appendChild(span);
|
|
| 36 | + }
|
|
| 37 | + container.appendChild(childEl);
|
|
| 38 | + if (children) {
|
|
| 39 | + if (childTag == "h3" && text.toLowerCase() === "build system") {
|
|
| 40 | + // Special case: treat the "Build System" heading's children as
|
|
| 41 | + // sub-headings.
|
|
| 42 | + childEl.classList.add("build-system-heading");
|
|
| 43 | + fillReleaseNotes(container, children, "h4");
|
|
| 44 | + } else {
|
|
| 45 | + const listEl = document.createElement("ul");
|
|
| 46 | + fillReleaseNotes(listEl, children, "li");
|
|
| 47 | + if (childTag == "li") {
|
|
| 48 | + // Insert within the "li" element.
|
|
| 49 | + childEl.appendChild(listEl);
|
|
| 50 | + } else {
|
|
| 51 | + container.appendChild(listEl);
|
|
| 52 | + }
|
|
| 53 | + }
|
|
| 54 | + }
|
|
| 55 | + }
|
|
| 56 | +}
|
|
| 57 | + |
|
| 58 | +/**
|
|
| 59 | + * Set the content for the specified container, or hide it if we have no
|
|
| 60 | + * content.
|
|
| 61 | + *
|
|
| 62 | + * @template C
|
|
| 63 | + * @param {string} containerId - The id for the container.
|
|
| 64 | + * @param {?C} content - The content for this container, or a falsey value if
|
|
| 65 | + * the container has no content.
|
|
| 66 | + * @param {function(contentEl: Elemenet, content: C)} [fillContent] - A function
|
|
| 67 | + * to fill the ".content" contentEl with the given 'content'. If unspecified,
|
|
| 68 | + * the 'content' will become the contentEl's textContent.
|
|
| 69 | + */
|
|
| 70 | +function setContent(containerId, content, fillContent) {
|
|
| 71 | + const container = document.getElementById(containerId);
|
|
| 72 | + if (!content) {
|
|
| 73 | + container.hidden = true;
|
|
| 74 | + return;
|
|
| 19 | 75 | }
|
| 76 | + const contentEl = container.querySelector(".content");
|
|
| 77 | + // Release notes are only in English.
|
|
| 78 | + contentEl.setAttribute("lang", "en-US");
|
|
| 79 | + contentEl.setAttribute("dir", "ltr");
|
|
| 80 | + if (fillContent) {
|
|
| 81 | + fillContent(contentEl, content);
|
|
| 82 | + } else {
|
|
| 83 | + contentEl.textContent = content;
|
|
| 84 | + }
|
|
| 85 | +}
|
|
| 86 | + |
|
| 87 | +/**
|
|
| 88 | + * Callback when we receive the update details.
|
|
| 89 | + *
|
|
| 90 | + * @param {Object} aData - The update details.
|
|
| 91 | + * @param {?string} aData.version - The update version.
|
|
| 92 | + * @param {?string} aData.releaseDate - The release date.
|
|
| 93 | + * @param {?string} aData.moreInfoURL - A URL for more info.
|
|
| 94 | + * @param {?Array<ReleaseBullet>} aData.releaseNotes - Release notes as bullet
|
|
| 95 | + * points.
|
|
| 96 | + */
|
|
| 97 | +function onUpdate(aData) {
|
|
| 98 | + setContent("version-row", aData.version);
|
|
| 99 | + setContent("releasedate-row", aData.releaseDate);
|
|
| 100 | + setContent("releasenotes", aData.releaseNotes, fillReleaseNotes);
|
|
| 101 | + |
|
| 20 | 102 | if (aData.moreInfoURL) {
|
| 21 | 103 | document.getElementById("infolink").setAttribute("href", aData.moreInfoURL);
|
| 104 | + } else {
|
|
| 105 | + document.getElementById("fullinfo").hidden = true;
|
|
| 22 | 106 | }
|
| 23 | - document.getElementById("releasenotes-content").textContent =
|
|
| 24 | - aData.releaseNotes;
|
|
| 25 | 107 | }
|
| 26 | 108 | |
| 27 | 109 | RPMSendQuery("FetchUpdateData").then(onUpdate); |
| ... | ... | @@ -21,19 +21,26 @@ |
| 21 | 21 | type="text/javascript"/>
|
| 22 | 22 | </head>
|
| 23 | 23 | <body dir="&locale.dir;">
|
| 24 | -<div class="two-column-grid">
|
|
| 25 | - <div class="label-column">&aboutTBUpdate.version;</div>
|
|
| 26 | - <div id="version-content"/>
|
|
| 27 | - |
|
| 28 | - <div class="label-column release-date-cell">&aboutTBUpdate.releaseDate;</div>
|
|
| 29 | - <div id="releasedate-content" class="release-date-cell"/>
|
|
| 30 | - |
|
| 31 | - <div class="more-info-row"/>
|
|
| 32 | - <div class="more-info-row">&aboutTBUpdate.linkPrefix;<a id="infolink">&aboutTBUpdate.linkLabel;</a>&aboutTBUpdate.linkSuffix;</div>
|
|
| 33 | - |
|
| 34 | - <div id="releasenotes-label"
|
|
| 35 | - class="label-column">&aboutTBUpdate.releaseNotes;</div>
|
|
| 36 | - <div id="releasenotes-content"></div>
|
|
| 37 | -</div>
|
|
| 24 | + <!-- NOTE: We don't use the <dl>, <dt> and <dd> elements to form name-value
|
|
| 25 | + - pairs because this semantics is relatively new, whilst firefox
|
|
| 26 | + - currently still maps these to the more limited "definitionlist", "term"
|
|
| 27 | + - and "definition" roles. -->
|
|
| 28 | + <div id="version-row">
|
|
| 29 | + <span class="label-column">&aboutTBUpdate.version;</span>
|
|
| 30 | + <span class="content"></span>
|
|
| 31 | + </div>
|
|
| 32 | + <div id="releasedate-row">
|
|
| 33 | + <span class="label-column">&aboutTBUpdate.releaseDate;</span>
|
|
| 34 | + <span class="content"></span>
|
|
| 35 | + </div>
|
|
| 36 | + <div id="fullinfo">
|
|
| 37 | + <p class="content">
|
|
| 38 | + &aboutTBUpdate.linkPrefix;<a id="infolink">&aboutTBUpdate.linkLabel;</a>&aboutTBUpdate.linkSuffix;
|
|
| 39 | + </p>
|
|
| 40 | + </div>
|
|
| 41 | + <section id="releasenotes">
|
|
| 42 | + <h2 class="label-column">&aboutTBUpdate.releaseNotes;</h2>
|
|
| 43 | + <div class="content"></div>
|
|
| 44 | + </section>
|
|
| 38 | 45 | </body>
|
| 39 | 46 | </html> |