Commits:
-
34750d3a
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! BB 41916: Letterboxing preferences UI
BB 44959: Swap about:manual link.
-
1f471dc9
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! BB 40925: Implemented the Security Level component
BB 44959: Swap about:manual link.
-
2064e1c8
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! TB 11698: Incorporate Tor Browser Manual pages into Tor Browser
TB 44959: Integrate the new manual.
-
631c2c54
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! TB 30237: Add v3 onion services client authentication prompt
TB 44959: Swap about:manual link.
-
da12add2
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! TB 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
TB 44959: Swap about:manual link.
-
21d6cfe7
by Henry Wilkes at 2026-07-28T13:15:52+00:00
fixup! Add TorStrings module for localization
TB 44959: Swap about:manual link.
18 changed files:
Changes:
browser/components/DesktopActorRegistry.sys.mjs
| ... |
... |
@@ -126,6 +126,23 @@ let JSWINDOWACTORS = { |
|
126
|
126
|
remoteTypes: ["privilegedabout"],
|
|
127
|
127
|
},
|
|
128
|
128
|
|
|
|
129
|
+ AboutManual: {
|
|
|
130
|
+ parent: {
|
|
|
131
|
+ esModuleURI:
|
|
|
132
|
+ "moz-src:///browser/components/aboutmanual/AboutManualParent.sys.mjs",
|
|
|
133
|
+ },
|
|
|
134
|
+ child: {
|
|
|
135
|
+ esModuleURI:
|
|
|
136
|
+ "moz-src:///browser/components/aboutmanual/AboutManualChild.sys.mjs",
|
|
|
137
|
+
|
|
|
138
|
+ events: {
|
|
|
139
|
+ ManualLocaleSelected: { wantUntrusted: true },
|
|
|
140
|
+ },
|
|
|
141
|
+ },
|
|
|
142
|
+
|
|
|
143
|
+ matches: ["about:manual"],
|
|
|
144
|
+ },
|
|
|
145
|
+
|
|
129
|
146
|
AboutMessagePreview: {
|
|
130
|
147
|
parent: {
|
|
131
|
148
|
esModuleURI: "resource:///actors/AboutMessagePreviewParent.sys.mjs",
|
browser/components/about/AboutRedirector.cpp
| ... |
... |
@@ -212,52 +212,45 @@ static nsAutoCString GetAboutModuleName(nsIURI* aURI) { |
|
212
|
212
|
return path;
|
|
213
|
213
|
}
|
|
214
|
214
|
|
|
215
|
|
-static nsTHashSet<nsCStringHashKey> GetManualLocales() {
|
|
216
|
|
- nsTHashSet<nsCStringHashKey> locales;
|
|
217
|
|
- RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::APP);
|
|
218
|
|
- if (!zip) {
|
|
219
|
|
- // Probably a local build started with ./mach run
|
|
220
|
|
- return locales;
|
|
221
|
|
- }
|
|
222
|
|
- UniquePtr<nsZipFind> find;
|
|
223
|
|
- const nsAutoCString prefix("chrome/browser/content/browser/manual/");
|
|
224
|
|
- nsAutoCString needle = prefix;
|
|
225
|
|
- needle.Append("*.html");
|
|
226
|
|
- if (NS_SUCCEEDED(zip->FindInit(needle.get(), getter_Transfers(find)))) {
|
|
227
|
|
- const char* entryName;
|
|
228
|
|
- uint16_t entryNameLen;
|
|
229
|
|
- while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
|
|
230
|
|
- // 5 is to remove the final `.html`
|
|
231
|
|
- const size_t length = entryNameLen - prefix.Length() - 5;
|
|
232
|
|
- locales.Insert(nsAutoCString(entryName + prefix.Length(), length));
|
|
233
|
|
- }
|
|
234
|
|
- }
|
|
235
|
|
- return locales;
|
|
236
|
|
-}
|
|
237
|
|
-
|
|
238
|
215
|
static nsAutoCString GetManualChromeURI() {
|
|
239
|
|
- static nsTHashSet<nsCStringHashKey> locales = GetManualLocales();
|
|
240
|
|
-
|
|
241
|
|
- nsAutoCString reqLocale;
|
|
242
|
|
- intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale);
|
|
243
|
|
- // Check every time the URL is needed in case the locale has changed.
|
|
244
|
|
- // It might help also if we start allowing to change language, e.g., with a
|
|
245
|
|
- // get parameter (see tor-browser#42675).
|
|
246
|
|
- if (!locales.Contains(reqLocale) && reqLocale.Length() > 2 &&
|
|
247
|
|
- reqLocale[2] == '-') {
|
|
248
|
|
- // At the moment, codes in our manual output are either 2 letters (en) or
|
|
249
|
|
- // 5 letters (pt-BR)
|
|
250
|
|
- reqLocale.SetLength(2);
|
|
|
216
|
+ nsTArray<nsCString> availableLocales;
|
|
|
217
|
+ nsCString availableLocalesStr;
|
|
|
218
|
+ if (NS_SUCCEEDED(mozilla::Preferences::GetCString(
|
|
|
219
|
+ "torbrowser.manual.available-locales", availableLocalesStr)) &&
|
|
|
220
|
+ availableLocalesStr.Length() > 0) {
|
|
|
221
|
+ for (const nsACString& locale : availableLocalesStr.Split(',')) {
|
|
|
222
|
+ availableLocales.AppendElement(locale);
|
|
|
223
|
+ }
|
|
251
|
224
|
}
|
|
252
|
|
- if (!locales.Contains(reqLocale)) {
|
|
253
|
|
- reqLocale = "en";
|
|
|
225
|
+ nsCString tryLocale;
|
|
|
226
|
+ if (!NS_SUCCEEDED(mozilla::Preferences::GetCString("torbrowser.manual.locale",
|
|
|
227
|
+ tryLocale)) ||
|
|
|
228
|
+ !availableLocales.Contains(tryLocale)) {
|
|
|
229
|
+ nsTArray<nsCString> appLocales;
|
|
|
230
|
+ intl::LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
|
|
|
231
|
+ bool found = false;
|
|
|
232
|
+ for (size_t i = 0; i < appLocales.Length(); i++) {
|
|
|
233
|
+ tryLocale = appLocales[i];
|
|
|
234
|
+ if (availableLocales.Contains(tryLocale)) {
|
|
|
235
|
+ found = true;
|
|
|
236
|
+ break;
|
|
|
237
|
+ } else if (tryLocale.Length() > 3 && tryLocale[2] == '-') {
|
|
|
238
|
+ // Strip the region code and see if the lang matches.
|
|
|
239
|
+ tryLocale.SetLength(2);
|
|
|
240
|
+ if (availableLocales.Contains(tryLocale)) {
|
|
|
241
|
+ found = true;
|
|
|
242
|
+ break;
|
|
|
243
|
+ }
|
|
|
244
|
+ }
|
|
|
245
|
+ }
|
|
|
246
|
+ if (!found) {
|
|
|
247
|
+ tryLocale.AssignLiteral("en");
|
|
|
248
|
+ }
|
|
254
|
249
|
}
|
|
255
|
|
-
|
|
256
|
|
- // %s is the language
|
|
257
|
|
- constexpr char model[] = "chrome://browser/content/manual/%s.html";
|
|
258
|
|
- nsAutoCString url;
|
|
259
|
|
- url.AppendPrintf(model, reqLocale.get());
|
|
260
|
|
- return url;
|
|
|
250
|
+ nsAutoCString uri;
|
|
|
251
|
+ uri.AppendPrintf("chrome://browser/content/aboutmanual/aboutManual-%s.html",
|
|
|
252
|
+ tryLocale.get());
|
|
|
253
|
+ return uri;
|
|
261
|
254
|
}
|
|
262
|
255
|
|
|
263
|
256
|
NS_IMETHODIMP
|
browser/components/aboutmanual/AboutManualChild.sys.mjs
|
|
1
|
+/**
|
|
|
2
|
+ * Actor child class for the about:manual page.
|
|
|
3
|
+ */
|
|
|
4
|
+export class AboutManualChild extends JSWindowActorChild {
|
|
|
5
|
+ handleEvent(event) {
|
|
|
6
|
+ switch (event.type) {
|
|
|
7
|
+ case "ManualLocaleSelected": {
|
|
|
8
|
+ const locale = event.detail;
|
|
|
9
|
+ if (typeof locale === "string") {
|
|
|
10
|
+ this.sendAsyncMessage("AboutManual:LocaleSelected", locale);
|
|
|
11
|
+ }
|
|
|
12
|
+ }
|
|
|
13
|
+ }
|
|
|
14
|
+ }
|
|
|
15
|
+} |
browser/components/aboutmanual/AboutManualParent.sys.mjs
|
|
1
|
+/**
|
|
|
2
|
+ * Actor parent class for the about:manual page.
|
|
|
3
|
+ */
|
|
|
4
|
+export class AboutManualParent extends JSWindowActorParent {
|
|
|
5
|
+ receiveMessage(message) {
|
|
|
6
|
+ switch (message.name) {
|
|
|
7
|
+ case "AboutManual:LocaleSelected": {
|
|
|
8
|
+ const locale = message.data;
|
|
|
9
|
+ if (
|
|
|
10
|
+ Services.prefs
|
|
|
11
|
+ .getStringPref("torbrowser.manual.available-locales", "")
|
|
|
12
|
+ .split(",")
|
|
|
13
|
+ .includes(locale)
|
|
|
14
|
+ ) {
|
|
|
15
|
+ Services.prefs.setStringPref("torbrowser.manual.locale", locale);
|
|
|
16
|
+ this.browsingContext.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
|
|
|
17
|
+ }
|
|
|
18
|
+ }
|
|
|
19
|
+ }
|
|
|
20
|
+ }
|
|
|
21
|
+} |
browser/components/aboutmanual/content/aboutManual.js
|
|
1
|
+"use strict";
|
|
|
2
|
+
|
|
|
3
|
+function changePage(initialLoad) {
|
|
|
4
|
+ let page;
|
|
|
5
|
+ const hash = location.hash;
|
|
|
6
|
+ const id = hash.startsWith("#") ? hash.substr(1) : null;
|
|
|
7
|
+ let targetEl = null;
|
|
|
8
|
+ if (id) {
|
|
|
9
|
+ targetEl = document.getElementById(id);
|
|
|
10
|
+ page = targetEl?.closest(".olm-page");
|
|
|
11
|
+ }
|
|
|
12
|
+ if (!page) {
|
|
|
13
|
+ if (!initialLoad) {
|
|
|
14
|
+ // Make no change.
|
|
|
15
|
+ return;
|
|
|
16
|
+ }
|
|
|
17
|
+ page = document.getElementById("index");
|
|
|
18
|
+ }
|
|
|
19
|
+ const currPage = document.querySelector(".olm-page:not(.d-none)");
|
|
|
20
|
+ if (currPage === page) {
|
|
|
21
|
+ // No change in page.
|
|
|
22
|
+ return;
|
|
|
23
|
+ }
|
|
|
24
|
+
|
|
|
25
|
+ currPage?.classList.add("d-none");
|
|
|
26
|
+ page.classList.remove("d-none");
|
|
|
27
|
+
|
|
|
28
|
+ // If we are targeting an element that is not a page element, we want to
|
|
|
29
|
+ // scroll it into view (the focus position should have already shifted to
|
|
|
30
|
+ // point to it).
|
|
|
31
|
+ // Otherwise, if we have no target or the target is a page, we want to scroll
|
|
|
32
|
+ // to the top of the document so that the header is visible.
|
|
|
33
|
+ const doScroll = () => {
|
|
|
34
|
+ const scrollEl = targetEl && targetEl !== page ? targetEl : document.body;
|
|
|
35
|
+ scrollEl.scrollIntoView({
|
|
|
36
|
+ behavior: "instant",
|
|
|
37
|
+ block: "start",
|
|
|
38
|
+ inline: "start",
|
|
|
39
|
+ });
|
|
|
40
|
+ };
|
|
|
41
|
+ if (initialLoad) {
|
|
|
42
|
+ window.addEventListener("load", doScroll, { once: true });
|
|
|
43
|
+ } else {
|
|
|
44
|
+ doScroll();
|
|
|
45
|
+ }
|
|
|
46
|
+}
|
|
|
47
|
+
|
|
|
48
|
+window.addEventListener("DOMContentLoaded", () => {
|
|
|
49
|
+ const langDropdown = document.getElementById("language-menu-dropdown");
|
|
|
50
|
+ for (const langButton of langDropdown.querySelectorAll("button")) {
|
|
|
51
|
+ langButton.addEventListener("click", () => {
|
|
|
52
|
+ dispatchEvent(
|
|
|
53
|
+ new CustomEvent("ManualLocaleSelected", {
|
|
|
54
|
+ // The button's lang attribute is expected to match the locale's code.
|
|
|
55
|
+ detail: langButton.getAttribute("lang"),
|
|
|
56
|
+ bubbles: true,
|
|
|
57
|
+ })
|
|
|
58
|
+ );
|
|
|
59
|
+ });
|
|
|
60
|
+ }
|
|
|
61
|
+
|
|
|
62
|
+ changePage(true);
|
|
|
63
|
+});
|
|
|
64
|
+
|
|
|
65
|
+window.addEventListener("hashchange", () => {
|
|
|
66
|
+ changePage(false);
|
|
|
67
|
+}); |
browser/components/aboutmanual/jar.mn
|
|
1
|
+browser.jar:
|
|
|
2
|
+ content/browser/aboutmanual/aboutManual.js (content/aboutManual.js) |
browser/components/aboutmanual/moz.build
|
|
1
|
+JAR_MANIFESTS += ["jar.mn"]
|
|
|
2
|
+
|
|
|
3
|
+MOZ_SRC_FILES += [
|
|
|
4
|
+ "AboutManualChild.sys.mjs",
|
|
|
5
|
+ "AboutManualParent.sys.mjs",
|
|
|
6
|
+] |
browser/components/moz.build
| ... |
... |
@@ -26,6 +26,7 @@ with Files("controlcenter/**"): |
|
26
|
26
|
|
|
27
|
27
|
DIRS += [
|
|
28
|
28
|
"about",
|
|
|
29
|
+ "aboutmanual",
|
|
29
|
30
|
"aboutlogins",
|
|
30
|
31
|
"abouttor",
|
|
31
|
32
|
"aboutwelcome",
|
browser/components/onionservices/content/authPopup.inc.xhtml
| ... |
... |
@@ -6,7 +6,7 @@ |
|
6
|
6
|
<label
|
|
7
|
7
|
class="text-link popup-notification-learnmore-link"
|
|
8
|
8
|
is="text-link"
|
|
9
|
|
- href="">"about:manual#onion-services_onion-service-authentication"
|
|
|
9
|
+ href="">"about:manual#features__onion-services___onion-service-auth"
|
|
10
|
10
|
useoriginprincipal="true"
|
|
11
|
11
|
data-l10n-id="onion-site-authentication-prompt-learn-more"
|
|
12
|
12
|
/>
|
browser/components/preferences/config/passwords-autofill.mjs
| ... |
... |
@@ -792,7 +792,7 @@ SettingGroupManager.registerGroups({ |
|
792
|
792
|
onionSiteAuthentication: {
|
|
793
|
793
|
l10nId: "onion-site-authentication-group",
|
|
794
|
794
|
headingLevel: 2,
|
|
795
|
|
- supportPage: "tor-manual:onion-services_onion-service-authentication",
|
|
|
795
|
+ supportPage: "tor-manual:features__onion-services___onion-service-auth",
|
|
796
|
796
|
items: [
|
|
797
|
797
|
{
|
|
798
|
798
|
id: "onionSiteSavedKeys",
|
browser/components/preferences/letterboxing.inc.xhtml
| ... |
... |
@@ -10,7 +10,7 @@ |
|
10
|
10
|
<html:span data-l10n-id="letterboxing-overview"></html:span>
|
|
11
|
11
|
<html:a
|
|
12
|
12
|
is="moz-support-link"
|
|
13
|
|
- support-page="tor-manual:anti-fingerprinting_letterboxing"
|
|
|
13
|
+ support-page="tor-manual:features__fingerprinting-protections___letterboxing"
|
|
14
|
14
|
data-l10n-id="letterboxing-learn-more"
|
|
15
|
15
|
></html:a>
|
|
16
|
16
|
</description>
|
browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
| ... |
... |
@@ -18,7 +18,7 @@ |
|
18
|
18
|
<html:a
|
|
19
|
19
|
is="moz-support-link"
|
|
20
|
20
|
id="securityLevel-learnMore"
|
|
21
|
|
- support-page="tor-manual:security-settings"
|
|
|
21
|
+ support-page="tor-manual:features__security-levels"
|
|
22
|
22
|
data-l10n-id="security-level-panel-learn-more-link"
|
|
23
|
23
|
></html:a>
|
|
24
|
24
|
<html:img id="securityLevel-background-image" alt="" />
|
browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml
| ... |
... |
@@ -13,7 +13,7 @@ |
|
13
|
13
|
></html:span>
|
|
14
|
14
|
<html:a
|
|
15
|
15
|
is="moz-support-link"
|
|
16
|
|
- support-page="tor-manual:security-settings"
|
|
|
16
|
+ support-page="tor-manual:features__security-levels"
|
|
17
|
17
|
data-l10n-id="security-level-preferences-learn-more-link"
|
|
18
|
18
|
></html:a>
|
|
19
|
19
|
</description>
|
browser/components/torpreferences/config/connection.mjs
| ... |
... |
@@ -13,7 +13,7 @@ SettingGroupManager.registerGroups({ |
|
13
|
13
|
connectionStatus: {
|
|
14
|
14
|
inProgress: true,
|
|
15
|
15
|
l10nId: "tor-connection-internet-status-group",
|
|
16
|
|
- supportPage: "tor-manual:about",
|
|
|
16
|
+ supportPage: "tor-manual:getting-started__about-tor-browser",
|
|
17
|
17
|
headingLevel: 2,
|
|
18
|
18
|
items: [
|
|
19
|
19
|
{
|
browser/components/torpreferences/content/connectionPane.inc.xhtml
| ... |
... |
@@ -18,7 +18,7 @@ |
|
18
|
18
|
<label
|
|
19
|
19
|
class="learnMore text-link"
|
|
20
|
20
|
is="text-link"
|
|
21
|
|
- href="about:manual#about"
|
|
|
21
|
+ href="about:manual#getting-started__about-tor-browser"
|
|
22
|
22
|
useoriginprincipal="true"
|
|
23
|
23
|
data-l10n-id="tor-connection-browser-learn-more-link"
|
|
24
|
24
|
/>
|
| ... |
... |
@@ -129,7 +129,7 @@ |
|
129
|
129
|
<label
|
|
130
|
130
|
class="learnMore text-link"
|
|
131
|
131
|
is="text-link"
|
|
132
|
|
- href=""idiff left right deletion" style="background-color: #fac5cd;">bridges"
|
|
|
132
|
+ href=""idiff left right addition" style="background-color: #c7f0d2;">circumvention__unblocking-tor"
|
|
133
|
133
|
useoriginprincipal="true"
|
|
134
|
134
|
data-l10n-id="tor-bridges-learn-more-link"
|
|
135
|
135
|
/>
|
browser/components/torpreferences/content/provideBridgeDialog.xhtml
| ... |
... |
@@ -37,7 +37,7 @@ |
|
37
|
37
|
<label
|
|
38
|
38
|
is="text-link"
|
|
39
|
39
|
class="learnMore text-link"
|
|
40
|
|
- href="about:manual#bridges"
|
|
|
40
|
+ href="about:manual#circumvention__unblocking-tor"
|
|
41
|
41
|
useoriginprincipal="true"
|
|
42
|
42
|
data-l10n-id="user-provide-bridge-dialog-learn-more"
|
|
43
|
43
|
/>
|
toolkit/content/aboutNetError.mjs
| ... |
... |
@@ -372,7 +372,7 @@ function initOnionError() { |
|
372
|
372
|
const learnMore = document.getElementById("learnMoreContainer");
|
|
373
|
373
|
learnMore.hidden = false;
|
|
374
|
374
|
const learnMoreLink = document.getElementById("learnMoreLink");
|
|
375
|
|
- learnMoreLink.href = "about:manual#onion-services";
|
|
|
375
|
+ learnMoreLink.href = "about:manual#features__onion-services";
|
|
376
|
376
|
|
|
377
|
377
|
setFocus("#netErrorButtonContainer > .try-again");
|
|
378
|
378
|
|
toolkit/modules/TorStrings.sys.mjs
| ... |
... |
@@ -243,7 +243,7 @@ const Loader = { |
|
243
|
243
|
);
|
|
244
|
244
|
return {
|
|
245
|
245
|
...tsb.getStrings(strings),
|
|
246
|
|
- learnMoreURL: "about:manual#onion-services",
|
|
|
246
|
+ learnMoreURL: "about:manual#features__onion-services",
|
|
247
|
247
|
// XUL popups cannot open about: URLs, but we are online when showing the notification, so just use the online version
|
|
248
|
248
|
learnMoreURLNotification: `https://tb-manual.torproject.org/${getLocale()}/onion-services/`,
|
|
249
|
249
|
};
|
|