diff --git a/browser/components/touchbar/MacTouchBar.js b/browser/components/touchbar/MacTouchBar.js index 977d5652f72d..0052ad054e53 100644 --- a/browser/components/touchbar/MacTouchBar.js +++ b/browser/components/touchbar/MacTouchBar.js @@ -237,6 +237,11 @@ const kBuiltInInputs = { }, }; +// We create a new flat object to cache strings. Since gBuiltInInputs is a +// tree, caching/retrieval of localized strings would otherwise require tree +// traversal. +var localizedStrings = {}; + const kHelperObservers = new Set([ "bookmark-icon-updated", "reader-mode-available", @@ -303,8 +308,6 @@ class TouchBarHelper { // icons) are loaded. We keep track of which inputs haven't updated and // run an update on them after the first location change. this._inputsNotUpdated = new Set(Object.keys(kBuiltInInputs)); - // This is a temporary workaround until bug 1596723 is resolved. - this._inputsNotUpdated.delete("SearchPopover"); return layoutItems; } @@ -351,8 +354,8 @@ class TouchBarHelper { // Skip localization if there is already a cached localized title or if // no title is needed. if ( - kBuiltInInputs[inputName].hasOwnProperty("localTitle") || - !kBuiltInInputs[inputName].hasOwnProperty("title") + !inputData.hasOwnProperty("title") || + localizedStrings[inputData.title] ) { return item; } @@ -360,7 +363,7 @@ class TouchBarHelper { // Async l10n fills in the localized input labels after the initial load. this._l10n.formatValue(item.key).then(result => { item.title = result; - kBuiltInInputs[inputName].localTitle = result; // Cache result. + localizedStrings[inputData.title] = result; // Cache result. // Checking TouchBarHelper.window since this callback can fire after all windows are closed. if (TouchBarHelper.window) { if (this._inputsNotUpdated) { @@ -475,12 +478,16 @@ class TouchBarHelper { ); break; case "intl:app-locales-changed": - // On locale change, refresh all inputs after loading new localTitle. this._searchPopover = null; - for (let input in kBuiltInInputs) { - delete input.localTitle; - } - this._updateTouchBarInputs(...kBuiltInInputs.keys()); + localizedStrings = {}; + + // This event can fire before this._l10n updates to switch languages, + // so all the new translations are in the old language. To avoid this, + // we need to reinitialize this._l10n. + this._l10n = new Localization(["browser/touchbar/touchbar.ftl"]); + helperProto._l10n = this._l10n; + + this._updateTouchBarInputs(...Object.keys(kBuiltInInputs)); break; case "quit-application": this.destructor(); @@ -523,7 +530,7 @@ helperProto._l10n = new Localization(["browser/touchbar/touchbar.ftl"]); class TouchBarInput { constructor(input) { this._key = input.key || input.title; - this._title = input.hasOwnProperty("localTitle") ? input.localTitle : ""; + this._title = localizedStrings[this._key] || ""; this._image = input.image; this._type = input.type; this._callback = input.callback; @@ -544,7 +551,7 @@ class TouchBarInput { initializedChild.type = input.type + "-" + initializedChild.type; this._children.push(initializedChild); // Skip l10n for inputs without a title or those already localized. - if (childData.title && childData.title != "") { + if (childData.title && !localizedStrings[childData.title]) { toLocalize.push(initializedChild); } } @@ -606,9 +613,14 @@ class TouchBarInput { /** * Apply Fluent l10n to child inputs. - * @param {Array} children An array of initialized TouchBarInputs. + * @param {Array} children + * An array of initialized TouchBarInputs. */ async _localizeChildren(children) { + if (!children || !children.length) { + return; + } + let titles = await helperProto._l10n.formatValues( children.map(child => ({ id: child.key })) ); @@ -617,7 +629,9 @@ class TouchBarInput { // results in titles match up with the inputs to be localized. children.forEach(function(child, index) { child.title = titles[index]; + localizedStrings[child.key] = child.title; }); + gTouchBarUpdater.updateTouchBarInputs(TouchBarHelper.baseWindow, children); } } diff --git a/widget/cocoa/nsTouchBar.mm b/widget/cocoa/nsTouchBar.mm index 1910df2ff650..129520c0b6a5 100644 --- a/widget/cocoa/nsTouchBar.mm +++ b/widget/cocoa/nsTouchBar.mm @@ -217,15 +217,12 @@ static const uint32_t kInputIconSize = 16; NSTouchBarItem* item = [self itemForIdentifier:[aInput nativeIdentifier]]; - // Update our canonical copy of the input. - [self replaceMappedLayoutItem:aInput]; - // If we can't immediately find item, there are three possibilities: - // * It is a button in a ScrollView, which can't be found with itemForIdentifier; or - // * It is contained within a popover; or + // * It is a button in a ScrollView, or + // * It is contained within a popover, or // * It simply does not exist. // We check for each possibility here. - if (!item) { + if (!self.mappedLayoutItems[[aInput nativeIdentifier]]) { if ([self maybeUpdateScrollViewChild:aInput]) { return true; } @@ -235,6 +232,9 @@ static const uint32_t kInputIconSize = 16; return false; } + // Update our canonical copy of the input. + [self replaceMappedLayoutItem:aInput]; + if ([aInput baseType] == TouchBarInputBaseType::kButton) { [(NSCustomTouchBarItem*)item setCustomizationLabel:[aInput title]]; [self updateButton:(NSCustomTouchBarItem*)item withIdentifier:[aInput nativeIdentifier]]; @@ -247,6 +247,9 @@ static const uint32_t kInputIconSize = 16; } else if ([aInput baseType] == TouchBarInputBaseType::kPopover) { [(NSPopoverTouchBarItem*)item setCustomizationLabel:[aInput title]]; [self updatePopover:(NSPopoverTouchBarItem*)item withIdentifier:[aInput nativeIdentifier]]; + for (TouchBarInput* child in [aInput children]) { + [(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] updateItem:child]; + } } else if ([aInput baseType] == TouchBarInputBaseType::kLabel) { [self updateLabel:(NSTextField*)item.view withIdentifier:[aInput nativeIdentifier]]; }