Loading browser/components/DesktopActorRegistry.sys.mjs +17 −0 Original line number Diff line number Diff line Loading @@ -128,6 +128,23 @@ let JSWINDOWACTORS = { remoteTypes: ["privilegedabout"], }, AboutManual: { parent: { esModuleURI: "moz-src:///browser/components/aboutmanual/AboutManualParent.sys.mjs", }, child: { esModuleURI: "moz-src:///browser/components/aboutmanual/AboutManualChild.sys.mjs", events: { ManualLocaleSelected: { wantUntrusted: true }, }, }, matches: ["about:manual"], }, AboutMessagePreview: { parent: { esModuleURI: "resource:///actors/AboutMessagePreviewParent.sys.mjs", Loading browser/components/about/AboutRedirector.cpp +36 −43 Original line number Diff line number Diff line Loading @@ -212,52 +212,45 @@ static nsAutoCString GetAboutModuleName(nsIURI* aURI) { return path; } static nsTHashSet<nsCStringHashKey> GetManualLocales() { nsTHashSet<nsCStringHashKey> locales; RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::APP); if (!zip) { // Probably a local build started with ./mach run return locales; static nsAutoCString GetManualChromeURI() { nsTArray<nsCString> availableLocales; nsCString availableLocalesStr; if (NS_SUCCEEDED(mozilla::Preferences::GetCString( "torbrowser.manual.available-locales", availableLocalesStr)) && availableLocalesStr.Length() > 0) { for (const nsACString& locale : availableLocalesStr.Split(',')) { availableLocales.AppendElement(locale); } UniquePtr<nsZipFind> find; const nsAutoCString prefix("chrome/browser/content/browser/manual/"); nsAutoCString needle = prefix; needle.Append("*.html"); if (NS_SUCCEEDED(zip->FindInit(needle.get(), getter_Transfers(find)))) { const char* entryName; uint16_t entryNameLen; while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) { // 5 is to remove the final `.html` const size_t length = entryNameLen - prefix.Length() - 5; locales.Insert(nsAutoCString(entryName + prefix.Length(), length)); } nsCString tryLocale; if (!NS_SUCCEEDED(mozilla::Preferences::GetCString("torbrowser.manual.locale", tryLocale)) || !availableLocales.Contains(tryLocale)) { nsTArray<nsCString> appLocales; intl::LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales); bool found = false; for (size_t i = 0; i < appLocales.Length(); i++) { tryLocale = appLocales[i]; if (availableLocales.Contains(tryLocale)) { found = true; break; } else if (tryLocale.Length() > 3 && tryLocale[2] == '-') { // Strip the region code and see if the lang matches. tryLocale.SetLength(2); if (availableLocales.Contains(tryLocale)) { found = true; break; } return locales; } static nsAutoCString GetManualChromeURI() { static nsTHashSet<nsCStringHashKey> locales = GetManualLocales(); nsAutoCString reqLocale; intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale); // Check every time the URL is needed in case the locale has changed. // It might help also if we start allowing to change language, e.g., with a // get parameter (see tor-browser#42675). if (!locales.Contains(reqLocale) && reqLocale.Length() > 2 && reqLocale[2] == '-') { // At the moment, codes in our manual output are either 2 letters (en) or // 5 letters (pt-BR) reqLocale.SetLength(2); } if (!locales.Contains(reqLocale)) { reqLocale = "en"; if (!found) { tryLocale.AssignLiteral("en"); } // %s is the language constexpr char model[] = "chrome://browser/content/manual/%s.html"; nsAutoCString url; url.AppendPrintf(model, reqLocale.get()); return url; } nsAutoCString uri; uri.AppendPrintf("chrome://browser/content/aboutmanual/aboutManual-%s.html", tryLocale.get()); return uri; } NS_IMETHODIMP Loading browser/components/aboutmanual/AboutManualChild.sys.mjs 0 → 100644 +15 −0 Original line number Diff line number Diff line /** * Actor child class for the about:manual page. */ export class AboutManualChild extends JSWindowActorChild { handleEvent(event) { switch (event.type) { case "ManualLocaleSelected": { const locale = event.detail; if (typeof locale === "string") { this.sendAsyncMessage("AboutManual:LocaleSelected", locale); } } } } } browser/components/aboutmanual/AboutManualParent.sys.mjs 0 → 100644 +21 −0 Original line number Diff line number Diff line /** * Actor parent class for the about:manual page. */ export class AboutManualParent extends JSWindowActorParent { receiveMessage(message) { switch (message.name) { case "AboutManual:LocaleSelected": { const locale = message.data; if ( Services.prefs .getStringPref("torbrowser.manual.available-locales", "") .split(",") .includes(locale) ) { Services.prefs.setStringPref("torbrowser.manual.locale", locale); this.browsingContext.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE); } } } } } browser/components/aboutmanual/content/aboutManual.js 0 → 100644 +67 −0 Original line number Diff line number Diff line "use strict"; function changePage(initialLoad) { let page; const hash = location.hash; const id = hash.startsWith("#") ? hash.substr(1) : null; let targetEl = null; if (id) { targetEl = document.getElementById(id); page = targetEl?.closest(".olm-page"); } if (!page) { if (!initialLoad) { // Make no change. return; } page = document.getElementById("index"); } const currPage = document.querySelector(".olm-page:not(.d-none)"); if (currPage === page) { // No change in page. return; } currPage?.classList.add("d-none"); page.classList.remove("d-none"); // If we are targeting an element that is not a page element, we want to // scroll it into view (the focus position should have already shifted to // point to it). // Otherwise, if we have no target or the target is a page, we want to scroll // to the top of the document so that the header is visible. const doScroll = () => { const scrollEl = targetEl && targetEl !== page ? targetEl : document.body; scrollEl.scrollIntoView({ behavior: "instant", block: "start", inline: "start", }); }; if (initialLoad) { window.addEventListener("load", doScroll, { once: true }); } else { doScroll(); } } window.addEventListener("DOMContentLoaded", () => { const langDropdown = document.getElementById("language-menu-dropdown"); for (const langButton of langDropdown.querySelectorAll("button")) { langButton.addEventListener("click", () => { dispatchEvent( new CustomEvent("ManualLocaleSelected", { // The button's lang attribute is expected to match the locale's code. detail: langButton.getAttribute("lang"), bubbles: true, }) ); }); } changePage(true); }); window.addEventListener("hashchange", () => { changePage(false); }); Loading
browser/components/DesktopActorRegistry.sys.mjs +17 −0 Original line number Diff line number Diff line Loading @@ -128,6 +128,23 @@ let JSWINDOWACTORS = { remoteTypes: ["privilegedabout"], }, AboutManual: { parent: { esModuleURI: "moz-src:///browser/components/aboutmanual/AboutManualParent.sys.mjs", }, child: { esModuleURI: "moz-src:///browser/components/aboutmanual/AboutManualChild.sys.mjs", events: { ManualLocaleSelected: { wantUntrusted: true }, }, }, matches: ["about:manual"], }, AboutMessagePreview: { parent: { esModuleURI: "resource:///actors/AboutMessagePreviewParent.sys.mjs", Loading
browser/components/about/AboutRedirector.cpp +36 −43 Original line number Diff line number Diff line Loading @@ -212,52 +212,45 @@ static nsAutoCString GetAboutModuleName(nsIURI* aURI) { return path; } static nsTHashSet<nsCStringHashKey> GetManualLocales() { nsTHashSet<nsCStringHashKey> locales; RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::APP); if (!zip) { // Probably a local build started with ./mach run return locales; static nsAutoCString GetManualChromeURI() { nsTArray<nsCString> availableLocales; nsCString availableLocalesStr; if (NS_SUCCEEDED(mozilla::Preferences::GetCString( "torbrowser.manual.available-locales", availableLocalesStr)) && availableLocalesStr.Length() > 0) { for (const nsACString& locale : availableLocalesStr.Split(',')) { availableLocales.AppendElement(locale); } UniquePtr<nsZipFind> find; const nsAutoCString prefix("chrome/browser/content/browser/manual/"); nsAutoCString needle = prefix; needle.Append("*.html"); if (NS_SUCCEEDED(zip->FindInit(needle.get(), getter_Transfers(find)))) { const char* entryName; uint16_t entryNameLen; while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) { // 5 is to remove the final `.html` const size_t length = entryNameLen - prefix.Length() - 5; locales.Insert(nsAutoCString(entryName + prefix.Length(), length)); } nsCString tryLocale; if (!NS_SUCCEEDED(mozilla::Preferences::GetCString("torbrowser.manual.locale", tryLocale)) || !availableLocales.Contains(tryLocale)) { nsTArray<nsCString> appLocales; intl::LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales); bool found = false; for (size_t i = 0; i < appLocales.Length(); i++) { tryLocale = appLocales[i]; if (availableLocales.Contains(tryLocale)) { found = true; break; } else if (tryLocale.Length() > 3 && tryLocale[2] == '-') { // Strip the region code and see if the lang matches. tryLocale.SetLength(2); if (availableLocales.Contains(tryLocale)) { found = true; break; } return locales; } static nsAutoCString GetManualChromeURI() { static nsTHashSet<nsCStringHashKey> locales = GetManualLocales(); nsAutoCString reqLocale; intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale); // Check every time the URL is needed in case the locale has changed. // It might help also if we start allowing to change language, e.g., with a // get parameter (see tor-browser#42675). if (!locales.Contains(reqLocale) && reqLocale.Length() > 2 && reqLocale[2] == '-') { // At the moment, codes in our manual output are either 2 letters (en) or // 5 letters (pt-BR) reqLocale.SetLength(2); } if (!locales.Contains(reqLocale)) { reqLocale = "en"; if (!found) { tryLocale.AssignLiteral("en"); } // %s is the language constexpr char model[] = "chrome://browser/content/manual/%s.html"; nsAutoCString url; url.AppendPrintf(model, reqLocale.get()); return url; } nsAutoCString uri; uri.AppendPrintf("chrome://browser/content/aboutmanual/aboutManual-%s.html", tryLocale.get()); return uri; } NS_IMETHODIMP Loading
browser/components/aboutmanual/AboutManualChild.sys.mjs 0 → 100644 +15 −0 Original line number Diff line number Diff line /** * Actor child class for the about:manual page. */ export class AboutManualChild extends JSWindowActorChild { handleEvent(event) { switch (event.type) { case "ManualLocaleSelected": { const locale = event.detail; if (typeof locale === "string") { this.sendAsyncMessage("AboutManual:LocaleSelected", locale); } } } } }
browser/components/aboutmanual/AboutManualParent.sys.mjs 0 → 100644 +21 −0 Original line number Diff line number Diff line /** * Actor parent class for the about:manual page. */ export class AboutManualParent extends JSWindowActorParent { receiveMessage(message) { switch (message.name) { case "AboutManual:LocaleSelected": { const locale = message.data; if ( Services.prefs .getStringPref("torbrowser.manual.available-locales", "") .split(",") .includes(locale) ) { Services.prefs.setStringPref("torbrowser.manual.locale", locale); this.browsingContext.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE); } } } } }
browser/components/aboutmanual/content/aboutManual.js 0 → 100644 +67 −0 Original line number Diff line number Diff line "use strict"; function changePage(initialLoad) { let page; const hash = location.hash; const id = hash.startsWith("#") ? hash.substr(1) : null; let targetEl = null; if (id) { targetEl = document.getElementById(id); page = targetEl?.closest(".olm-page"); } if (!page) { if (!initialLoad) { // Make no change. return; } page = document.getElementById("index"); } const currPage = document.querySelector(".olm-page:not(.d-none)"); if (currPage === page) { // No change in page. return; } currPage?.classList.add("d-none"); page.classList.remove("d-none"); // If we are targeting an element that is not a page element, we want to // scroll it into view (the focus position should have already shifted to // point to it). // Otherwise, if we have no target or the target is a page, we want to scroll // to the top of the document so that the header is visible. const doScroll = () => { const scrollEl = targetEl && targetEl !== page ? targetEl : document.body; scrollEl.scrollIntoView({ behavior: "instant", block: "start", inline: "start", }); }; if (initialLoad) { window.addEventListener("load", doScroll, { once: true }); } else { doScroll(); } } window.addEventListener("DOMContentLoaded", () => { const langDropdown = document.getElementById("language-menu-dropdown"); for (const langButton of langDropdown.querySelectorAll("button")) { langButton.addEventListener("click", () => { dispatchEvent( new CustomEvent("ManualLocaleSelected", { // The button's lang attribute is expected to match the locale's code. detail: langButton.getAttribute("lang"), bubbles: true, }) ); }); } changePage(true); }); window.addEventListener("hashchange", () => { changePage(false); });