Bug 1580599 - Add pseudo-locale configuration to the browser toolbox; r=platform-i18n-reviewers,devtools-reviewers,fluent-reviewers,nchevobbe,dminor

This commit adds support for setting the pseudo-locale for the browser
UI directly from the Browser Toolbox. This places the icons in the same
place as the "Disable Popup Autohide" command. This will make it easier
for Firefox developers to test that their UI is properly localized.

The SVGs were optimized for size using an optimizer that dropped the
path precision and any extra tags. I tested that they work correctly in
both light and dark modes.

Pseudo-localization is documented here:
https://firefox-source-docs.mozilla.org/l10n/fluent/tutorial.html#pseudolocalization

After this patch lands I'll follow-up with updating that documentation.

Differential Revision: https://phabricator.services.mozilla.com/D134420
This commit is contained in:
Greg Tatum 2022-01-10 17:09:17 +00:00
Родитель c6eeab3adf
Коммит 302855c2a4
10 изменённых файлов: 289 добавлений и 39 удалений

Просмотреть файл

@ -32,6 +32,7 @@ skip-if =
os == 'linux' && bits == 64 && debug # Bug 1604751
[browser_browser_toolbox_fission_inspector_webextension.js]
[browser_browser_toolbox_fission_inspector.js]
[browser_browser_toolbox_l10n_buttons.js]
[browser_browser_toolbox_print_preview.js]
[browser_browser_toolbox_rtl.js]
[browser_browser_toolbox_ruleview_stylesheet.js]

Просмотреть файл

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// There are shutdown issues for which multiple rejections are left uncaught.
// See bug 1018184 for resolving these issues.
const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm"
);
PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/);
// On debug test machine, it takes about 50s to run the test.
requestLongerTimeout(4);
/**
* In the browser toolbox there are options to switch the language to the "bidi" and
* "accented" languages. These are useful for making sure the browser is correctly
* localized. This test opens the browser toolbox, and checks that these buttons
* work.
*/
add_task(async function() {
const ToolboxTask = await initBrowserToolboxTask();
await ToolboxTask.importFunctions({ clickMeatballItem });
is(getPseudoLocale(), "", "Starts out as empty");
await ToolboxTask.spawn(null, () => clickMeatballItem("accented"));
is(getPseudoLocale(), "accented", "Enabled the accented pseudo-locale");
await ToolboxTask.spawn(null, () => clickMeatballItem("accented"));
is(getPseudoLocale(), "", "Disabled the accented pseudo-locale.");
await ToolboxTask.spawn(null, () => clickMeatballItem("bidi"));
is(getPseudoLocale(), "bidi", "Enabled the bidi pseudo-locale.");
await ToolboxTask.spawn(null, () => clickMeatballItem("bidi"));
is(getPseudoLocale(), "", "Disabled the bidi pseudo-locale.");
await ToolboxTask.spawn(null, () => clickMeatballItem("bidi"));
is(getPseudoLocale(), "bidi", "Enabled the bidi before closing.");
await ToolboxTask.destroy();
is(getPseudoLocale(), "", "After closing the pseudo-locale is disabled.");
});
/**
* Return the pseudo-locale preference of the debuggee browser (not the browser toolbox).
*
* Another option for this test would be to test the text and layout of the
* browser directly, but this could be brittle. Checking the preference will
* hopefully provide adequate coverage.
*/
function getPseudoLocale() {
return Services.prefs.getCharPref("intl.l10n.pseudo");
}
/**
* This function is a ToolboxTask and is cloned into the toolbox context. It opens the
* "meatball menu" in the browser toolbox, clicks one of the pseudo-locale
* options, and finally returns the pseudo-locale preference from the target browser.
*
* @param {"accented" | "bidi"} type
*/
function clickMeatballItem(type) {
return new Promise(resolve => {
/* global gToolbox */
dump(`Opening the meatball menu in the browser toolbox.\n`);
gToolbox.doc.getElementById("toolbox-meatball-menu-button").click();
gToolbox.doc.addEventListener(
"popupshown",
async () => {
const menuItem = gToolbox.doc.getElementById(
"toolbox-meatball-menu-pseudo-locale-" + type
);
dump(`Clicking the meatball menu item: "${type}".\n`);
menuItem.click();
// Request the pseudo-locale so that we know the preference actor is fully
// done setting the debuggee browser.
await gToolbox.getPseudoLocale();
resolve();
},
{ once: true }
);
});
}

Просмотреть файл

@ -71,6 +71,10 @@ class MeatballMenu extends PureComponent {
// (i.e. we're not in a browser toolbox).
disableAutohide: PropTypes.bool,
// Apply a pseudo-locale to the Firefox UI. This is only available in the browser
// toolbox. This value can be undefined, "accented", "bidi", "none".
pseudoLocale: PropTypes.string,
// Function to turn the options panel on / off.
toggleOptions: PropTypes.func.isRequired,
@ -80,6 +84,12 @@ class MeatballMenu extends PureComponent {
// Function to turn the disable pop-up autohide behavior on / off.
toggleNoAutohide: PropTypes.func,
// Manage the pseudo-localization for the Firefox UI.
// https://firefox-source-docs.mozilla.org/l10n/fluent/tutorial.html#pseudolocalization
disablePseudoLocale: PropTypes.func,
enableAccentedPseudoLocale: PropTypes.func,
enableBidiPseudoLocale: PropTypes.func,
// Bug 1709191 - The help shortcut key is localized without Fluent, and still needs
// to be migrated. This is the only remaining use of the legacy L10N object.
// Everything else should prefer the Fluent API.
@ -101,6 +111,7 @@ class MeatballMenu extends PureComponent {
//
// - The "Disable pop-up autohide" menu item being added after the Browser
// Toolbox is connected.
// - The pseudo locale options being added after the Browser Toolbox is connected.
// - The split console label changing between "Show Split Console" and "Hide
// Split Console".
// - The "Show/Hide Split Console" entry being added removed or removed.
@ -109,6 +120,7 @@ class MeatballMenu extends PureComponent {
// autohide" is active, but for completeness we handle them here.
const didChange =
typeof this.props.disableAutohide !== typeof prevProps.disableAutohide ||
this.props.pseudoLocale !== prevProps.pseudoLocale ||
this.props.currentToolId !== prevProps.currentToolId ||
this.props.isSplitConsoleActive !== prevProps.isSplitConsoleActive;
@ -180,6 +192,27 @@ class MeatballMenu extends PureComponent {
);
}
// Settings
items.push(
MenuItem({
id: "toolbox-meatball-menu-settings",
key: "settings",
l10nID: "toolbox-meatball-menu-settings-label",
// Bug 1709191 - The help key is localized without Fluent, and still needs to
// be migrated.
accelerator: this.props.L10N.getStr("toolbox.help.key"),
onClick: this.props.toggleOptions,
className: "iconic",
})
);
if (
typeof this.props.disableAutohide !== "undefined" ||
typeof this.props.pseudoLocale !== "undefined"
) {
items.push(hr({ key: "docs-separator-1" }));
}
// Disable pop-up autohide
//
// If |disableAutohide| is undefined, it means this feature is not available
@ -198,21 +231,43 @@ class MeatballMenu extends PureComponent {
);
}
// Settings
items.push(
MenuItem({
id: "toolbox-meatball-menu-settings",
key: "settings",
l10nID: "toolbox-meatball-menu-settings-label",
// Bug 1709191 - The help key is localized without Fluent, and still needs to
// be migrated.
accelerator: this.props.L10N.getStr("toolbox.help.key"),
onClick: this.props.toggleOptions,
className: "iconic",
})
);
// Pseudo-locales.
if (typeof this.props.pseudoLocale !== "undefined") {
const {
pseudoLocale,
enableAccentedPseudoLocale,
enableBidiPseudoLocale,
disablePseudoLocale,
} = this.props;
items.push(
MenuItem({
id: "toolbox-meatball-menu-pseudo-locale-accented",
key: "pseudo-locale-accented",
l10nID: "toolbox-meatball-menu-pseudo-locale-accented",
type: "checkbox",
checked: pseudoLocale === "accented",
onClick:
pseudoLocale === "accented"
? disablePseudoLocale
: enableAccentedPseudoLocale,
className: "iconic",
}),
MenuItem({
id: "toolbox-meatball-menu-pseudo-locale-bidi",
key: "pseudo-locale-bidi",
l10nID: "toolbox-meatball-menu-pseudo-locale-bidi",
type: "checkbox",
checked: pseudoLocale === "bidi",
onClick:
pseudoLocale === "bidi"
? disablePseudoLocale
: enableBidiPseudoLocale,
className: "iconic",
})
);
}
items.push(hr({ key: "docs-separator" }));
items.push(hr({ key: "docs-separator-2" }));
// Getting started
items.push(

Просмотреть файл

@ -37,6 +37,7 @@ class ToolboxController extends Component {
canCloseToolbox: true,
isSplitConsoleActive: false,
disableAutohide: undefined,
pseudoLocale: undefined,
canRender: false,
buttonIds: [],
checkedButtonsUpdated: () => {
@ -159,10 +160,20 @@ class ToolboxController extends Component {
this.setState({ isSplitConsoleActive });
}
/**
* @param {bool | undefined} disableAutohide
*/
setDisableAutohide(disableAutohide) {
this.setState({ disableAutohide });
}
/**
* @param {"bidi" | "accented" | "none" | undefined} pseudoLocale
*/
setPseudoLocale(pseudoLocale) {
this.setState({ pseudoLocale });
}
setPanelDefinitions(panelDefinitions) {
this.setState({ panelDefinitions }, this.updateButtonIds);
}

Просмотреть файл

@ -15,6 +15,7 @@ const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
const DISABLE_AUTOHIDE_PREF = "ui.popup.disable_autohide";
const FORCE_THEME_NOTIFICATION_PREF = "devtools.theme.force-auto-theme-info";
const SHOW_THEME_NOTIFICATION_PREF = "devtools.theme.show-auto-theme-info";
const PSEUDO_LOCALE_PREF = "intl.l10n.pseudo";
const HOST_HISTOGRAM = "DEVTOOLS_TOOLBOX_HOST";
const CURRENT_THEME_SCALAR = "devtools.current_theme";
const HTML_NS = "http://www.w3.org/1999/xhtml";
@ -286,6 +287,9 @@ function Toolbox(
this._toolUnregistered = this._toolUnregistered.bind(this);
this._refreshHostTitle = this._refreshHostTitle.bind(this);
this.toggleNoAutohide = this.toggleNoAutohide.bind(this);
this.disablePseudoLocale = () => this.changePseudoLocale("none");
this.enableAccentedPseudoLocale = () => this.changePseudoLocale("accented");
this.enableBidiPseudoLocale = () => this.changePseudoLocale("bidi");
this._updateFrames = this._updateFrames.bind(this);
this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
this.closeToolbox = this.closeToolbox.bind(this);
@ -1894,10 +1898,16 @@ Toolbox.prototype = {
definition.isTargetSupported(this.target) && definition.id !== "options"
);
// Do async lookup of disable pop-up auto-hide state.
if (this.disableAutohideAvailable) {
const disable = await this._isDisableAutohideEnabled();
this.component.setDisableAutohide(disable);
// Do async lookups for the target browser's state.
if (this.isBrowserChromeTarget) {
// Parallelize the asynchronous calls, so that the DOM is only updated once when
// updating the React components.
const [disableAutohide, pseudoLocale] = await Promise.all([
this._isDisableAutohideEnabled(),
this.getPseudoLocale(),
]);
this.component.setDisableAutohide(disableAutohide);
this.component.setPseudoLocale(pseudoLocale);
}
},
@ -1911,6 +1921,9 @@ Toolbox.prototype = {
toggleOptions: this.toggleOptions,
toggleSplitConsole: this.toggleSplitConsole,
toggleNoAutohide: this.toggleNoAutohide,
disablePseudoLocale: this.disablePseudoLocale,
enableAccentedPseudoLocale: this.enableAccentedPseudoLocale,
enableBidiPseudoLocale: this.enableBidiPseudoLocale,
closeToolbox: this.closeToolbox,
focusButton: this._onToolbarFocus,
toolbox: this,
@ -3232,21 +3245,64 @@ Toolbox.prototype = {
* client. See the definition of the preference actor for more information.
*/
get preferenceFront() {
const frontPromise = this.commands.client.mainRoot.getFront("preference");
frontPromise.then(front => {
// Set the _preferenceFront property to allow the resetPreferences toolbox method
// to cleanup the preference set when the toolbox is closed.
this._preferenceFront = front;
});
return frontPromise;
if (!this._preferenceFrontRequest) {
// Set the _preferenceFrontRequest property to allow the resetPreference toolbox
// method to cleanup the preference set when the toolbox is closed.
this._preferenceFrontRequest = this.commands.client.mainRoot.getFront(
"preference"
);
}
return this._preferenceFrontRequest;
},
// Is the disable auto-hide of pop-ups feature available in this context?
get disableAutohideAvailable() {
// The auto-hide of pop-ups feature and pseudo-localization require targeting
// browser chrome.
get isBrowserChromeTarget() {
return this.target.chrome;
},
/**
* See: https://firefox-source-docs.mozilla.org/l10n/fluent/tutorial.html#pseudolocalization
*
* @param {"bidi" | "accented" | "none"} pseudoLocale
*/
async changePseudoLocale(pseudoLocale) {
await this.isOpen;
const prefFront = await this.preferenceFront;
if (pseudoLocale === "none") {
await prefFront.clearUserPref(PSEUDO_LOCALE_PREF);
} else {
await prefFront.setCharPref(PSEUDO_LOCALE_PREF, pseudoLocale);
}
this.component.setPseudoLocale(pseudoLocale);
this._pseudoLocaleChanged = true;
},
/**
* Returns the pseudo-locale when the target is browser chrome, otherwise undefined.
*
* @returns {"bidi" | "accented" | "none" | undefined}
*/
async getPseudoLocale() {
// Ensure that the tools are open and the feature is available in this
// context.
await this.isOpen;
if (!this.isBrowserChromeTarget) {
return undefined;
}
const prefFront = await this.preferenceFront;
const locale = await prefFront.getCharPref(PSEUDO_LOCALE_PREF);
switch (locale) {
case "bidi":
case "accented":
return locale;
default:
return "none";
}
},
async toggleNoAutohide() {
const front = await this.preferenceFront;
@ -3254,7 +3310,7 @@ Toolbox.prototype = {
front.setBoolPref(DISABLE_AUTOHIDE_PREF, toggledValue);
if (this.disableAutohideAvailable) {
if (this.isBrowserChromeTarget) {
this.component.setDisableAutohide(toggledValue);
}
this._autohideHasBeenToggled = true;
@ -3264,7 +3320,7 @@ Toolbox.prototype = {
// Ensure that the tools are open and the feature is available in this
// context.
await this.isOpen;
if (!this.disableAutohideAvailable) {
if (!this.isBrowserChromeTarget) {
return false;
}
@ -3967,8 +4023,12 @@ Toolbox.prototype = {
this.browserRequire = null;
this._toolNames = null;
// Reset preferences set by the toolbox
outstanding.push(this.resetPreference());
// Reset preferences set by the toolbox, then remove the preference front.
outstanding.push(
this.resetPreference().then(() => {
this._preferenceFrontRequest = null;
})
);
this.commands.targetCommand.unwatchTargets({
types: this.commands.targetCommand.ALL_TYPES,
@ -4181,17 +4241,25 @@ Toolbox.prototype = {
* Reset preferences set by the toolbox.
*/
async resetPreference() {
if (!this._preferenceFront) {
if (
// No preferences have been changed, so there is nothing to reset.
!this._preferenceFrontRequest ||
// Did any pertinent prefs actually change? For autohide and the pseudo-locale,
// only reset prefs in the Browser Toolbox if it's been toggled in the UI
// (don't reset the pref if it was already set before opening)
(!this._autohideHasBeenToggled && !this._pseudoLocaleChanged)
) {
return;
}
// Only reset the autohide pref in the Browser Toolbox if it's been toggled
// in the UI (don't reset the pref if it was already set before opening)
if (this._autohideHasBeenToggled) {
await this._preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
}
const preferenceFront = await this.preferenceFront;
this._preferenceFront = null;
if (this._autohideHasBeenToggled) {
await preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
}
if (this._pseudoLocaleChanged) {
await preferenceFront.clearUserPref(PSEUDO_LOCALE_PREF);
}
},
// HAR Automation

Просмотреть файл

@ -185,6 +185,8 @@ devtools.jar:
skin/images/command-measure.svg (themes/images/command-measure.svg)
skin/images/command-noautohide.svg (themes/images/command-noautohide.svg)
skin/images/command-chevron.svg (themes/images/command-chevron.svg)
skin/images/command-accented.svg (themes/images/command-accented.svg)
skin/images/command-bidi.svg (themes/images/command-bidi.svg)
skin/images/rules-view-light-mode-simulation.svg (themes/images/rules-view-light-mode-simulation.svg)
skin/images/rules-view-dark-mode-simulation.svg (themes/images/rules-view-dark-mode-simulation.svg)
skin/images/rules-view-print-simulation.svg (themes/images/rules-view-print-simulation.svg)

Просмотреть файл

@ -26,4 +26,7 @@ toolbox-meatball-menu-community-label = Community…
# contributors.
toolbox-meatball-menu-noautohide-label = Disable Popup Auto-Hide
toolbox-meatball-menu-pseudo-locale-accented = Enable “accented” locale
toolbox-meatball-menu-pseudo-locale-bidi = Enable “bidi” locale
##

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0c0c0d">
<path d="M13 1a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3h10ZM3 3c-.54 0-1 .46-1 1v8a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V4c0-.54-.46-1-1-1H3Z"/>
<path d="M6.42 8.48a1.7 1.7 0 0 0-.1-.48 1.19 1.19 0 0 0-.6-.68c-.16-.07-.32-.1-.5-.1s-.35.03-.5.1c-.15.06-.28.15-.39.26a1.32 1.32 0 0 0-.36.9h2.45Zm-2.45.7c0 .18.02.35.08.53.05.17.13.32.23.45s.24.23.4.3c.16.08.35.12.57.12.3 0 .56-.06.74-.2.2-.13.33-.33.42-.59h1A1.98 1.98 0 0 1 6 11.31c-.23.07-.48.1-.74.1-.37 0-.7-.06-1-.18a2.06 2.06 0 0 1-1.18-1.31 3.22 3.22 0 0 1 0-1.98c.12-.3.27-.57.47-.8a2.2 2.2 0 0 1 2.7-.52c.3.16.54.36.74.62a2.73 2.73 0 0 1 .5 1.93H3.97Zm1.66-4.65h1.25L5.57 5.85H4.8l.83-1.32ZM11.96 8.48a1.7 1.7 0 0 0-.1-.48 1.19 1.19 0 0 0-.6-.68c-.16-.07-.32-.1-.5-.1s-.35.03-.5.1c-.15.06-.28.15-.39.26a1.32 1.32 0 0 0-.36.9h2.45Zm-2.45.7c0 .18.02.35.07.53.06.17.14.32.24.45.1.13.24.23.4.3.16.08.35.12.57.12.3 0 .55-.06.74-.2.19-.13.33-.33.42-.59h1a1.98 1.98 0 0 1-1.42 1.52c-.23.07-.48.1-.74.1-.38 0-.7-.06-1-.18a2.06 2.06 0 0 1-1.18-1.31 3.22 3.22 0 0 1 0-1.98c.11-.3.27-.57.47-.8a2.2 2.2 0 0 1 2.7-.52c.29.16.54.36.73.62a2.73 2.73 0 0 1 .5 1.93h-3.5Zm1.66-4.65h1.24l-1.3 1.32h-.77l.83-1.32Z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0c0c0d">
<path d="M13 1a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3h10ZM3 3c-.54 0-1 .46-1 1v8a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V4c0-.54-.46-1-1-1H3Z"/>
<path d="M3.6 6.83h1.07v1h.02c.03-.14.1-.28.2-.41a1.9 1.9 0 0 1 .8-.63c.17-.07.34-.1.51-.1a2.47 2.47 0 0 1 .43.03v1.1a5.41 5.41 0 0 0-.49-.05c-.2 0-.37.04-.54.12-.18.07-.32.18-.45.33-.13.16-.23.34-.3.56a2.4 2.4 0 0 0-.11.76V12H3.6V6.83Zm3.43 0h.86V5.28h1.14v1.55h1.03v.85H9.03v2.76c0 .12 0 .22.02.31 0 .09.03.16.07.22.03.06.09.1.16.13.08.04.18.05.3.05a5.76 5.76 0 0 0 .48-.04v.88a64.42 64.42 0 0 0-.74.06c-.3 0-.54-.03-.72-.09a.97.97 0 0 1-.43-.25.86.86 0 0 1-.21-.41 3.12 3.12 0 0 1-.07-.57V7.68h-.86v-.85Zm3.95-1.97h1.14V12h-1.14V4.86Z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Просмотреть файл

@ -384,6 +384,14 @@
--menuitem-icon-image: url("chrome://devtools/skin/images/command-noautohide.svg");
}
#toolbox-meatball-menu-pseudo-locale-accented {
--menuitem-icon-image: url("chrome://devtools/skin/images/command-accented.svg");
}
#toolbox-meatball-menu-pseudo-locale-bidi {
--menuitem-icon-image: url("chrome://devtools/skin/images/command-bidi.svg");
}
#toolbox-meatball-menu-settings {
--menuitem-icon-image: url("chrome://devtools/skin/images/settings.svg");
}