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
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 | );
|
... | ... | @@ -74,41 +73,72 @@ class AboutTBUpdateParent extends JSWindowActorParent { |
74 | 73 | f.append("Docs");
|
75 | 74 | f.append("ChangeLog.txt");
|
76 | 75 | |
77 | - let s = await IOUtils.readUTF8(f.path);
|
|
78 | - |
|
79 | - // Truncate at the first empty line.
|
|
80 | - s = s.replace(/[\r\n][\r\n][\s\S]*$/m, "");
|
|
81 | - |
|
82 | - // Split into first line (version plus releaseDate) and
|
|
83 | - // remainder (releaseNotes).
|
|
84 | - // This first match() uses multiline mode with two capture groups:
|
|
85 | - // first line: (.*$)
|
|
86 | - // remaining lines: ([\s\S]+)
|
|
87 | - // [\s\S] matches all characters including end of line. This trick
|
|
88 | - // is needed because when using JavaScript regex in multiline mode,
|
|
89 | - // . does not match an end of line character.
|
|
90 | - let matchArray = s.match(/(.*$)\s*([\s\S]+)/m);
|
|
91 | - if (matchArray && matchArray.length == 3) {
|
|
92 | - info.releaseNotes = matchArray[2];
|
|
93 | - let line1 = matchArray[1];
|
|
94 | - // Extract the version and releaseDate. The first line looks like:
|
|
95 | - // Tor Browser 8.5 -- May 1 2019
|
|
96 | - // The regex uses two capture groups:
|
|
97 | - // text that does not include a hyphen: (^[^-]*)
|
|
98 | - // remaining text: (.*$)
|
|
99 | - // In between we match optional whitespace, one or more hyphens, and
|
|
100 | - // optional whitespace by using: \s*-+\s*
|
|
101 | - matchArray = line1.match(/(^[^-]*)\s*-+\s*(.*$)/);
|
|
102 | - if (matchArray && matchArray.length == 3) {
|
|
103 | - info.version = matchArray[1];
|
|
104 | - 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];
|
|
105 | 132 | } else {
|
106 | - info.version = line1; // Match failed: return entire line in version.
|
|
133 | + // Append to the previous bullet point.
|
|
134 | + prevEntry.text += ` ${line}`;
|
|
107 | 135 | }
|
108 | - } else {
|
|
109 | - info.releaseNotes = s; // Only one line: use as releaseNotes.
|
|
110 | 136 | }
|
111 | - } catch (e) {}
|
|
137 | + |
|
138 | + info.releaseNotes = topEntry.children;
|
|
139 | + } catch (e) {
|
|
140 | + Cu.reportError(e);
|
|
141 | + }
|
|
112 | 142 | |
113 | 143 | return info;
|
114 | 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> |