diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index bcd26daf614e..f348ee4f37b5 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -459,9 +459,12 @@ #endif data-l10n-id="menu-help"> + #ifdef MOZ_UPDATER #endif @@ -469,36 +472,52 @@ oncommand="openHelpLink('firefox-help')" onclick="checkForMiddleClick(this, event);" data-l10n-id="menu-help-product" + appmenu-data-l10n-id="appmenu-help-product" #ifdef XP_MACOSX key="key_openHelpMac"/> #else /> #endif + oncommand="openTourPage();" + data-l10n-id="menu-help-show-tour" + appmenu-data-l10n-id="appmenu-help-show-tour"/> + command="cmd_help_importFromAnotherBrowser" + data-l10n-id="menu-help-import-from-another-browser" + appmenu-data-l10n-id="appmenu-help-import-from-another-browser"/> + onclick="checkForMiddleClick(this, event);" + data-l10n-id="menu-help-keyboard-shortcuts" + appmenu-data-l10n-id="appmenu-help-keyboard-shortcuts"/> + onclick="checkForMiddleClick(this, event);" + data-l10n-id="menu-help-troubleshooting-info" + appmenu-data-l10n-id="appmenu-help-troubleshooting-info"/> + onclick="checkForMiddleClick(this, event);" + data-l10n-id="menu-help-feedback-page" + appmenu-data-l10n-id="appmenu-help-feedback-page"/> + oncommand="safeModeRestart();" + data-l10n-id="menu-help-safe-mode-without-addons" + appmenu-data-l10n-id="appmenu-help-safe-mode-without-addons"/> diff --git a/browser/components/customizableui/content/panelUI.js b/browser/components/customizableui/content/panelUI.js index 5e8609ab8afb..020b89d7a31a 100644 --- a/browser/components/customizableui/content/panelUI.js +++ b/browser/components/customizableui/content/panelUI.js @@ -799,7 +799,7 @@ const PanelUI = { let helpMenu = document.getElementById("menu_HelpPopup"); let items = this.getElementsByTagName("vbox")[0]; - let attrs = ["command", "oncommand", "onclick", "label", "key", "disabled"]; + let attrs = ["command", "oncommand", "onclick", "key", "disabled"]; // Remove all buttons from the view while (items.firstChild) { @@ -823,6 +823,12 @@ const PanelUI = { } button.setAttribute(attrName, node.getAttribute(attrName)); } + + // We have AppMenu-specific strings for the Help menu. By convention, + // their localization IDs are set on "appmenu-data-l10n-id" attributes. + let l10nId = node.getAttribute("appmenu-data-l10n-id"); + button.setAttribute("data-l10n-id", l10nId); + if (node.id) { button.id = "appMenu_" + node.id; } diff --git a/browser/components/customizableui/test/browser.ini b/browser/components/customizableui/test/browser.ini index 7c42f249d9fc..7fba89253894 100644 --- a/browser/components/customizableui/test/browser.ini +++ b/browser/components/customizableui/test/browser.ini @@ -150,6 +150,7 @@ skip-if = os == "linux" # linux doesn't get drag space (no tabsintitlebar) [browser_drag_outside_palette.js] [browser_exit_background_customize_mode.js] [browser_flexible_space_area.js] +[browser_help_panel_cloning.js] [browser_insert_before_moved_node.js] [browser_library_after_appMenu.js] [browser_menubar_visibility.js] diff --git a/browser/components/customizableui/test/browser_help_panel_cloning.js b/browser/components/customizableui/test/browser_help_panel_cloning.js new file mode 100644 index 000000000000..0afe99e274fc --- /dev/null +++ b/browser/components/customizableui/test/browser_help_panel_cloning.js @@ -0,0 +1,95 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* global PanelUI */ + +ChromeUtils.import( + "resource://testing-common/CustomizableUITestUtils.jsm", + this +); + +let gAppMenuStrings = new Localization( + ["branding/brand.ftl", "browser/appmenu.ftl"], + true +); + +const CLONED_ATTRS = ["command", "oncommand", "onclick", "key", "disabled"]; + +/** + * Tests that the Help panel inside of the AppMenu properly clones + * the items from the Help menupopup. Also ensures that the AppMenu + * string variants for those menuitems exist inside of appmenu.ftl. + */ +add_task(async function test_help_panel_cloning() { + await gCUITestUtils.openMainMenu(); + registerCleanupFunction(async () => { + await gCUITestUtils.hideMainMenu(); + }); + + // Showing the Help panel should be enough to get the menupopup to + // populate itself. + let anchor = document.getElementById("PanelUI-menu-button"); + PanelUI.showHelpView(anchor); + + let appMenuHelpSubview = document.getElementById("PanelUI-helpView"); + await BrowserTestUtils.waitForEvent(appMenuHelpSubview, "ViewShowing"); + + let helpMenuPopup = document.getElementById("menu_HelpPopup"); + let helpMenuPopupItems = helpMenuPopup.querySelectorAll("menuitem"); + + for (let helpMenuPopupItem of helpMenuPopupItems) { + if (helpMenuPopupItem.hidden) { + continue; + } + + let appMenuHelpId = "appMenu_" + helpMenuPopupItem.id; + info(`Checking ${appMenuHelpId}`); + + let appMenuHelpItem = appMenuHelpSubview.querySelector(`#${appMenuHelpId}`); + Assert.ok(appMenuHelpItem, "Should have found a cloned AppMenu help item"); + + let appMenuHelpItemL10nId = appMenuHelpItem.dataset.l10nId; + // There is a convention that the Help menu item should have an + // appmenu-data-l10n-id attribute set as the AppMenu-specific localization + // id. + Assert.equal( + helpMenuPopupItem.getAttribute("appmenu-data-l10n-id"), + appMenuHelpItemL10nId, + "Help menuitem supplied a data-l10n-id for the AppMenu Help item" + ); + + let [strings] = gAppMenuStrings.formatMessagesSync([ + { id: appMenuHelpItemL10nId }, + ]); + Assert.ok(strings, "Should have found strings for the AppMenu help item"); + + // Make sure the CLONED_ATTRs are actually cloned. + for (let attr of CLONED_ATTRS) { + if (attr == "oncommand" && helpMenuPopupItem.hasAttribute("command")) { + // If the original element had a "command" attribute set, then the + // cloned element will have its "oncommand" attribute set to equal + // the "oncommand" attribute of the pointed to via the + // original's "command" attribute once it is inserted into the DOM. + // + // This is by virtue of the broadcasting ability of XUL + // elements. + let commandNode = document.getElementById( + helpMenuPopupItem.getAttribute("command") + ); + Assert.equal( + commandNode.getAttribute("oncommand"), + appMenuHelpItem.getAttribute("oncommand"), + "oncommand was properly cloned." + ); + } else { + Assert.equal( + helpMenuPopupItem.getAttribute(attr), + appMenuHelpItem.getAttribute(attr), + `${attr} attribute was cloned.` + ); + } + } + } +}); diff --git a/browser/locales/en-US/browser/appmenu.ftl b/browser/locales/en-US/browser/appmenu.ftl index 3fc42e5d08bd..d5a1235374e4 100644 --- a/browser/locales/en-US/browser/appmenu.ftl +++ b/browser/locales/en-US/browser/appmenu.ftl @@ -40,3 +40,52 @@ whatsnew-panel-header = What’s New whatsnew-panel-footer-checkbox = .label = Notify about new features .accesskey = f + +## Help panel + +appmenu-about = + .label = About { -brand-shorter-name } + .accesskey = A +appmenu-help-product = + .label = { -brand-shorter-name } Help + .accesskey = H +appmenu-help-show-tour = + .label = { -brand-shorter-name } Tour + .accesskey = o +appmenu-help-import-from-another-browser = + .label = Import From Another Browser… + .accesskey = I +appmenu-help-keyboard-shortcuts = + .label = Keyboard Shortcuts + .accesskey = K +appmenu-help-troubleshooting-info = + .label = Troubleshooting Information + .accesskey = T +appmenu-help-feedback-page = + .label = Submit Feedback… + .accesskey = S + +## appmenu-help-safe-mode-without-addons and appmenu-help-safe-mode-without-addons +## are mutually exclusive, so it's possible to use the same accesskey for both. + +appmenu-help-safe-mode-without-addons = + .label = Restart With Add-ons Disabled… + .accesskey = R +appmenu-help-safe-mode-with-addons = + .label = Restart With Add-ons Enabled + .accesskey = R + +## appmenu-help-report-deceptive-site and appmenu-help-not-deceptive +## are mutually exclusive, so it's possible to use the same accesskey for both. + +appmenu-help-report-deceptive-site = + .label = Report Deceptive Site… + .accesskey = D +appmenu-help-not-deceptive = + .label = This Isn’t a Deceptive Site… + .accesskey = D + +## + +appmenu-help-check-for-update = + .label = Check for Updates… diff --git a/browser/locales/en-US/browser/menubar.ftl b/browser/locales/en-US/browser/menubar.ftl index 347da145a0d2..0040475f0b07 100644 --- a/browser/locales/en-US/browser/menubar.ftl +++ b/browser/locales/en-US/browser/menubar.ftl @@ -286,6 +286,14 @@ menu-window-bring-all-to-front = ## Help Menu +# NOTE: For Engineers, any additions or changes to Help menu strings should +# also be reflected in the related strings in appmenu.ftl. Those strings, by +# convention, will have the same ID as these, but prefixed with "app". +# Example: appmenu-help-product +# +# These strings are duplicated to allow for different casing depending on +# where the strings appear. + menu-help = .label = Help .accesskey = H diff --git a/python/l10n/fluent_migrations/bug_1683419_forking_help_menu.py b/python/l10n/fluent_migrations/bug_1683419_forking_help_menu.py new file mode 100644 index 000000000000..65e6edbe4731 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1683419_forking_help_menu.py @@ -0,0 +1,55 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import absolute_import +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1683419 - Fork the Help menu strings for use in the AppMenu, part {index}.""" + ctx.add_transforms( + "browser/browser/appmenu.ftl", + "browser/browser/appmenu.ftl", + transforms_from( + """ +appmenu-about = + .label = { COPY_PATTERN(from_path, "menu-about.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-about.accesskey") } +appmenu-help-product = + .label = { COPY_PATTERN(from_path, "menu-help-product.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-product.accesskey") } +appmenu-help-show-tour = + .label = { COPY_PATTERN(from_path, "menu-help-show-tour.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-show-tour.accesskey") } +appmenu-help-import-from-another-browser = + .label = { COPY_PATTERN(from_path, "menu-help-import-from-another-browser.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-import-from-another-browser.accesskey") } +appmenu-help-keyboard-shortcuts = + .label = { COPY_PATTERN(from_path, "menu-help-keyboard-shortcuts.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-keyboard-shortcuts.accesskey") } +appmenu-help-troubleshooting-info = + .label = { COPY_PATTERN(from_path, "menu-help-troubleshooting-info.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-troubleshooting-info.accesskey") } +appmenu-help-feedback-page = + .label = { COPY_PATTERN(from_path, "menu-help-feedback-page.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-feedback-page.accesskey") } +appmenu-help-safe-mode-without-addons = + .label = { COPY_PATTERN(from_path, "menu-help-safe-mode-without-addons.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-safe-mode-without-addons.accesskey") } +appmenu-help-safe-mode-with-addons = + .label = { COPY_PATTERN(from_path, "menu-help-safe-mode-with-addons.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-safe-mode-with-addons.accesskey") } +appmenu-help-report-deceptive-site = + .label = { COPY_PATTERN(from_path, "menu-help-report-deceptive-site.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-report-deceptive-site.accesskey") } +appmenu-help-not-deceptive = + .label = { COPY_PATTERN(from_path, "menu-help-not-deceptive.label") } + .accesskey = { COPY_PATTERN(from_path, "menu-help-not-deceptive.accesskey") } +appmenu-help-check-for-update = + .label = { COPY_PATTERN(from_path, "menu-help-check-for-update.label") } +""", + from_path="browser/browser/menubar.ftl", + ), + )