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">
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",
+ ),
+ )