Bug 1522012 - Implement Touch Bar's native customization window and remove ui.touchbar.layout preference. r=spohl,mikedeboer,fluent-reviewers,Pike

This patch also fixes the Home and Sidebar Touch Bar buttons, since using them after customizing showed that they no longer worked.

Differential Revision: https://phabricator.services.mozilla.com/D35085

--HG--
extra : moz-landing-system : lando
This commit is contained in:
harry 2019-07-06 21:10:33 +00:00
Родитель e9127d9614
Коммит f56a5c0ec4
19 изменённых файлов: 215 добавлений и 91 удалений

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

@ -532,6 +532,7 @@
<menuitem id="menu_mac_hide_app" label="&hideThisAppCmdMac2.label;" key="key_hideThisAppCmdMac"/>
<menuitem id="menu_mac_hide_others" label="&hideOtherAppsCmdMac.label;" key="key_hideOtherAppsCmdMac"/>
<menuitem id="menu_mac_show_all" label="&showAllAppsCmdMac.label;"/>
<menuitem id="menu_mac_touch_bar" label="&touchBarCmdMac.label;"/>
#endif
</menupopup>
</menu>

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

@ -69,6 +69,17 @@ XPCOMUtils.defineLazyPreferenceGetter(
"gCosmeticAnimationsEnabled",
"toolkit.cosmeticAnimations.enabled"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"gTouchBarUpdater",
"@mozilla.org/widget/touchbarupdater;1",
"nsITouchBarUpdater"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gCosmeticAnimationsEnabled",
"toolkit.cosmeticAnimations.enabled"
);
let gDebug;
XPCOMUtils.defineLazyGetter(this, "log", () => {
@ -387,6 +398,7 @@ CustomizeMode.prototype = {
this._updateResetButton();
this._updateUndoResetButton();
this._updateTouchBarButton();
this._skipSourceNodeCheck =
Services.prefs.getPrefType(kSkipSourceNodePref) ==
@ -1596,6 +1608,18 @@ CustomizeMode.prototype = {
undoResetButton.hidden = !CustomizableUI.canUndoReset;
},
_updateTouchBarButton() {
if (AppConstants.platform != "macosx") {
return;
}
let touchBarButton = this.$("customization-touchbar-button");
let touchBarSpacer = this.$("customization-touchbar-spacer");
let isTouchBarInitialized = gTouchBarUpdater.isTouchBarInitialized();
touchBarButton.hidden = !isTouchBarInitialized;
touchBarSpacer.hidden = !isTouchBarInitialized;
},
handleEvent(aEvent) {
switch (aEvent.type) {
case "toolbarvisibilitychange":
@ -2691,6 +2715,13 @@ CustomizeMode.prototype = {
event.view.gCustomizeMode._moveDownloadsButtonToNavBar = checkbox.checked;
},
customizeTouchBar() {
let updater = Cc["@mozilla.org/widget/touchbarupdater;1"].getService(
Ci.nsITouchBarUpdater
);
updater.enterCustomizeMode();
},
togglePong(enabled) {
// It's possible we're toggling for a reason other than hitting
// the button (we might be exiting, for example), so make sure that

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

@ -111,6 +111,14 @@
hidden="true"/>
<spacer id="customization-footer-spacer"/>
#ifdef XP_MACOSX
<button id="customization-touchbar-button"
class="customizationmode-button"
hidden="true"
oncommand="gCustomizeMode.customizeTouchBar();"
data-l10n-id="customize-mode-touchbar-cmd"/>
<spacer hidden="true" id="customization-touchbar-spacer"/>
#endif
<button id="customization-undo-reset-button"
class="customizationmode-button"
hidden="true"

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

@ -182,6 +182,8 @@ skip-if = verify
[browser_tabbar_big_widgets.js]
[browser_remote_tabs_button.js]
skip-if = (verify && debug && (os == 'linux' || os == 'mac'))
[browser_touchbar_customization.js]
skip-if = (os == "linux" || os == "win")
[browser_widget_animation.js]
[browser_lwt_telemetry.js]

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

@ -0,0 +1,21 @@
/* 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/. */
"use strict";
// Checks if the Customize Touch Bar button appears when a Touch Bar is
// initialized.
add_task(async function customizeTouchBarButtonAppears() {
let updater = Cc["@mozilla.org/widget/touchbarupdater;1"].getService(
Ci.nsITouchBarUpdater
);
// This value will be reset to its default the next time a window is opened.
updater.setTouchBarInitialized(true);
await startCustomizing();
let touchbarButton = document.querySelector("#customization-touchbar-button");
ok(!touchbarButton.hidden, "Customize Touch Bar button is not hidden.");
let touchbarSpacer = document.querySelector("#customization-touchbar-spacer");
ok(!touchbarSpacer.hidden, "Customize Touch Bar spacer is not hidden.");
await endCustomizing();
});

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

@ -77,7 +77,14 @@ const kBuiltInInputs = {
title: "home",
image: "home.pdf",
type: "button",
callback: () => execCommand("Browser:Home", "Home"),
callback: () => {
let win = BrowserWindowTracker.getTopWindow();
win.BrowserHome();
let telemetry = Services.telemetry.getHistogramById(
"TOUCHBAR_BUTTON_PRESSES"
);
telemetry.add("Home");
},
},
Fullscreen: {
title: "fullscreen",
@ -98,10 +105,17 @@ const kBuiltInInputs = {
callback: () => execCommand("cmd_newNavigatorTabNoEvent", "NewTab"),
},
Sidebar: {
title: "open-bookmarks-sidebar",
title: "open-sidebar",
image: "sidebar-left.pdf",
type: "button",
callback: () => execCommand("viewBookmarksSidebar", "Sidebar"),
callback: () => {
let win = BrowserWindowTracker.getTopWindow();
win.SidebarUI.toggle();
let telemetry = Services.telemetry.getHistogramById(
"TOUCHBAR_BUTTON_PRESSES"
);
telemetry.add("Sidebar");
},
},
AddBookmark: {
title: "add-bookmark",
@ -150,13 +164,6 @@ class TouchBarHelper {
for (let topic of kHelperObservers) {
Services.obs.addObserver(this, topic);
}
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_touchBarLayout",
"ui.touchbar.layout",
"Back,Forward,Reload,OpenLocation,NewTab,Share"
);
}
destructor() {
@ -174,13 +181,12 @@ class TouchBarHelper {
return activeTitle;
}
get layout() {
let prefArray = this.storedLayout;
get allItems() {
let layoutItems = Cc["@mozilla.org/array;1"].createInstance(
Ci.nsIMutableArray
);
for (let inputName of prefArray) {
for (let inputName of Object.keys(kBuiltInInputs)) {
if (typeof kBuiltInInputs[inputName].context == "function") {
inputName = kBuiltInInputs[inputName].context();
}
@ -202,24 +208,6 @@ class TouchBarHelper {
return this.window.BookmarkingUI;
}
/**
* Returns a string array of the user's Touch Bar layout preference.
* Set by a pref ui.touchbar.layout and returned in an array.
*/
get storedLayout() {
let prefArray = this._touchBarLayout.split(",");
prefArray = prefArray.map(str => str.trim());
// Remove duplicates.
prefArray = Array.from(new Set(prefArray));
// Remove unimplemented/mispelled inputs.
prefArray = prefArray.filter(input =>
Object.keys(kBuiltInInputs).includes(input)
);
this._storedLayout = prefArray;
return this._storedLayout;
}
getTouchBarInput(inputName) {
// inputName might be undefined if an input's context() returns undefined.
if (!inputName || !kBuiltInInputs.hasOwnProperty(inputName)) {
@ -238,7 +226,6 @@ class TouchBarHelper {
let inputData = kBuiltInInputs[inputName];
let item = new TouchBarInput(inputData);
// Skip localization if there is already a cached localized title.
if (kBuiltInInputs[inputName].hasOwnProperty("localTitle")) {
return item;
@ -277,12 +264,6 @@ class TouchBarHelper {
let inputs = [];
for (let inputName of inputNames) {
// Updating Touch Bar items isn't cheap in terms of performance, so
// only consider this input if it's actually part of the layout.
if (!this._storedLayout.includes(inputName)) {
continue;
}
let input = this.getTouchBarInput(inputName);
if (!input) {
continue;
@ -322,10 +303,10 @@ class TouchBarHelper {
break;
case "intl:app-locales-changed":
// On locale change, refresh all inputs after loading new localTitle.
for (let inputName of this._storedLayout) {
delete kBuiltInInputs[inputName].localTitle;
for (let input in kBuiltInInputs) {
delete input.localTitle;
}
this._updateTouchBarInputs(...this._storedLayout);
this._updateTouchBarInputs(...kBuiltInInputs.keys());
break;
case "quit-application":
this.destructor();

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

@ -2,8 +2,6 @@
* 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/. */
const PREF_NAME = "ui.touchbar.layout";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
@ -31,39 +29,19 @@ function is_element_hidden(aElement, aMsg) {
ok(BrowserTestUtils.is_hidden(aElement), aMsg);
}
/**
* Sets the pref to contain errors. .layout should contain only the
* non-erroneous elements.
*/
add_task(async function setWrongPref() {
registerCleanupFunction(function() {
Services.prefs.deleteBranch(PREF_NAME);
});
let wrongValue = "Back, Back, Forwrd, NewTab, Unimplemented,";
let correctValue = ["back", "new-tab"];
let testValue = [];
Services.prefs.setStringPref(PREF_NAME, wrongValue);
let layoutItems = Cc["@mozilla.org/array;1"].createInstance(
Ci.nsIMutableArray
);
layoutItems = TouchBarHelper.layout;
for (let i = 0; i < layoutItems.length; i++) {
let input = layoutItems.queryElementAt(i, Ci.nsITouchBarInput);
testValue.push(input.key);
}
Assert.equal(
testValue.toString(),
correctValue.toString(),
"Pref should filter out incorrect inputs."
);
});
/**
* Tests if our bookmark button updates with our event.
*/
add_task(async function updateBookmarkButton() {
// We first check the default state. This also serves the purpose of forcing
// nsITouchBarHelper to load on Macs without Touch Bars so that it will be
// listening for "bookmark-icon-updated".
Assert.equal(
TouchBarHelper.getTouchBarInput("AddBookmark").image,
"bookmark.pdf",
"AddBookmark image should be unfilled bookmark after event."
);
Services.obs.notifyObservers(null, "bookmark-icon-updated", "starred");
Assert.equal(
TouchBarHelper.getTouchBarInput("AddBookmark").image,

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

@ -43,3 +43,5 @@ customize-mode-undo-cmd =
.label = Undo
customize-mode-lwthemes-my-themes =
.value = My Themes
customize-mode-touchbar-cmd =
.label = Customize Touch Bar…

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

@ -12,9 +12,9 @@ fullscreen = Fullscreen
find = Find
new-tab = New tab
add-bookmark = Add bookmark
open-bookmarks-sidebar = Open Bookmarks Sidebar
reader-view = Reader View
# Meant to match the string displayed in an empty URL bar.
open-location = Search or enter address
share = Share
close-window = Close Window
open-sidebar = Sidebars

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

@ -49,3 +49,5 @@
<!ENTITY hideOtherAppsCmdMac.commandkey "H">
<!ENTITY showAllAppsCmdMac.label "Show All">
<!ENTITY touchBarCmdMac.label "Customize Touch Bar…">

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

@ -208,10 +208,6 @@ pref("ui.menu.incremental_search.timeout", 1000);
// If true, all popups won't hide automatically on blur
pref("ui.popup.disable_autohide", false);
#ifdef XP_MACOSX
pref("ui.touchbar.layout", "Back,Forward,Reload,OpenLocation,NewTab,Share");
#endif
// 0 = default: always, except in high contrast mode
// 1 = always
// 2 = never

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

@ -63,6 +63,8 @@ int32_t gXULModalLevel = 0;
// also made app-modal.) See nsCocoaWindow::SetModal().
nsCocoaWindowList* gGeckoAppModalWindowList = NULL;
BOOL sTouchBarIsInitialized = NO;
// defined in nsMenuBarX.mm
extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
@ -2797,6 +2799,9 @@ static NSImage* GetMenuMaskImage() {
- (NSTouchBar*)makeTouchBar {
mTouchBar = [[nsTouchBar alloc] init];
if (mTouchBar) {
sTouchBarIsInitialized = YES;
}
return mTouchBar;
}

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

@ -20,6 +20,7 @@
#include "nsThreadUtils.h"
#include "nsIContent.h"
#include "nsITouchBarUpdater.h"
#include "nsIWidget.h"
#include "mozilla/dom/Document.h"
#include "nsIAppStartup.h"
@ -35,6 +36,9 @@ NSMenu* sApplicationMenu = nil;
BOOL sApplicationMenuIsFallback = NO;
BOOL gSomeMenuBarPainted = NO;
// defined in nsCocoaWindow.mm.
extern BOOL sTouchBarIsInitialized;
// We keep references to the first quit and pref item content nodes we find, which
// will be from the hidden window. We use these when the document for the current
// window does not have a quit or pref item. We don't need strong refs here because
@ -520,6 +524,7 @@ void nsMenuBarX::AquifyMenuBar() {
HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_app"), nullptr);
HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_others"), nullptr);
HideItem(domDoc, NS_LITERAL_STRING("menu_mac_show_all"), nullptr);
HideItem(domDoc, NS_LITERAL_STRING("menu_mac_touch_bar"), nullptr);
}
}
@ -625,6 +630,8 @@ nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu) {
= Hide Others = <- menu_mac_hide_others
= Show All = <- menu_mac_show_all
========================
= Customize Touch Bar… = <- menu_mac_touch_bar
========================
= Quit = <- menu_FileQuitItem
========================
@ -740,6 +747,28 @@ nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu) {
// Add a separator after the hide/show menus if at least one exists
if (addHideShowSeparator) [sApplicationMenu addItem:[NSMenuItem separatorItem]];
BOOL addTouchBarSeparator = NO;
// Add Touch Bar customization menu item.
itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_touch_bar"),
@selector(menuItemHit:), eCommand_ID_TouchBar,
nsMenuBarX::sNativeEventTarget);
if (itemBeingAdded) {
[sApplicationMenu addItem:itemBeingAdded];
// We hide the menu item on Macs that don't have a Touch Bar.
if (!sTouchBarIsInitialized) {
[itemBeingAdded setHidden:YES];
} else {
addTouchBarSeparator = YES;
}
[itemBeingAdded release];
itemBeingAdded = nil;
}
// Add a separator after the Touch Bar menu item if it exists
if (addTouchBarSeparator) [sApplicationMenu addItem:[NSMenuItem separatorItem]];
// Add quit menu item
itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_FileQuitItem"),
@selector(menuItemHit:), eCommand_ID_Quit,
@ -825,6 +854,12 @@ static BOOL gMenuItemsExecuteCommands = YES;
@end
#if !defined(MAC_OS_X_VERSION_10_12_2) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12_2
@interface NSApplication (TouchBarMenu)
- (IBAction)toggleTouchBarCustomizationPalette:(id)sender;
@end
#endif
//
// Objective-C class used as action target for menu items
//
@ -875,6 +910,9 @@ static BOOL gMenuItemsExecuteCommands = YES;
} else if (tag == eCommand_ID_ShowAll) {
[NSApp unhideAllApplications:sender];
return;
} else if (tag == eCommand_ID_TouchBar) {
[NSApp toggleTouchBarCustomizationPalette:sender];
return;
} else if (tag == eCommand_ID_Quit) {
nsIContent* mostSpecificContent = sQuitItemContent;
if (menuBar && menuBar->mQuitItemContent) mostSpecificContent = menuBar->mQuitItemContent;

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

@ -69,7 +69,8 @@ enum {
eCommand_ID_HideOthers = 5,
eCommand_ID_ShowAll = 6,
eCommand_ID_Update = 7,
eCommand_ID_Last = 8
eCommand_ID_TouchBar = 8,
eCommand_ID_Last = 9
};
#endif // nsMenuBaseX_h_

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

@ -39,9 +39,12 @@ __attribute__((weak_import)) @interface NSCustomTouchBarItem : NSTouchBarItem
@protocol NSTouchBarDelegate
@end
typedef NSString* NSTouchBarCustomizationIdentifier;
__attribute__((weak_import)) @interface NSTouchBar : NSObject
@property(strong) NSArray<NSTouchBarItemIdentifier>* defaultItemIdentifiers;
@property(strong) id<NSTouchBarDelegate> delegate;
@property(strong) NSTouchBarCustomizationIdentifier customizationIdentifier;
@property(strong) NSArray<NSTouchBarItemIdentifier>* customizationAllowedItemIdentifiers;
- (NSTouchBarItem*)itemForIdentifier:(NSTouchBarItemIdentifier)aIdentifier;
@end

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

@ -38,21 +38,25 @@ static char sIdentifierAssociationKey;
}
self.delegate = self;
// This customization identifier is how users' custom layouts are saved by macOS.
// If this changes, all users' layouts would be reset to the default layout.
self.customizationIdentifier = @"com.mozilla.firefox.touchbar.defaultbar";
self.mappedLayoutItems = [NSMutableDictionary dictionary];
nsCOMPtr<nsIArray> layoutItems;
nsCOMPtr<nsIArray> allItems;
nsresult rv = mTouchBarHelper->GetLayout(getter_AddRefs(layoutItems));
if (NS_FAILED(rv) || !layoutItems) {
nsresult rv = mTouchBarHelper->GetAllItems(getter_AddRefs(allItems));
if (NS_FAILED(rv) || !allItems) {
return nil;
}
uint32_t itemCount = 0;
layoutItems->GetLength(&itemCount);
// This is copied to self.defaultItemIdentifiers. Required since
// [self.mappedLayoutItems allKeys] does not preserve order.
NSMutableArray* orderedLayoutIdentifiers = [NSMutableArray arrayWithCapacity:itemCount];
allItems->GetLength(&itemCount);
// This is copied to self.customizationAllowedItemIdentifiers. Required since
// [self.mappedItems allKeys] does not preserve order.
// One slot is added for the spacer item.
NSMutableArray* orderedIdentifiers = [NSMutableArray arrayWithCapacity:itemCount + 1];
for (uint32_t i = 0; i < itemCount; ++i) {
nsCOMPtr<nsITouchBarInput> input = do_QueryElementAt(layoutItems, i);
nsCOMPtr<nsITouchBarInput> input = do_QueryElementAt(allItems, i);
if (!input) {
continue;
}
@ -61,10 +65,19 @@ static char sIdentifierAssociationKey;
// Add new input to dictionary for lookup of properties in delegate.
self.mappedLayoutItems[[convertedInput nativeIdentifier]] = convertedInput;
orderedLayoutIdentifiers[i] = [convertedInput nativeIdentifier];
orderedIdentifiers[i] = [convertedInput nativeIdentifier];
}
[orderedIdentifiers addObject:@"NSTouchBarItemIdentifierFlexibleSpace"];
self.defaultItemIdentifiers = [orderedLayoutIdentifiers copy];
NSArray* defaultItemIdentifiers = @[
[CustomButtonIdentifier stringByAppendingPathExtension:@"back"],
[CustomButtonIdentifier stringByAppendingPathExtension:@"forward"],
[CustomButtonIdentifier stringByAppendingPathExtension:@"reload"],
[CustomMainButtonIdentifier stringByAppendingPathExtension:@"open-location"],
[CustomButtonIdentifier stringByAppendingPathExtension:@"new-tab"], ShareScrubberIdentifier
];
self.defaultItemIdentifiers = [defaultItemIdentifiers copy];
self.customizationAllowedItemIdentifiers = [orderedIdentifiers copy];
}
return self;
@ -77,7 +90,7 @@ static char sIdentifierAssociationKey;
}
[self.defaultItemIdentifiers release];
[self.customizationAllowedItemIdentifiers release];
[self.mappedLayoutItems removeAllObjects];
[self.mappedLayoutItems release];
[super dealloc];

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

@ -13,10 +13,16 @@
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
// defined in nsCocoaWindow.mm.
extern BOOL sTouchBarIsInitialized;
#if !defined(MAC_OS_X_VERSION_10_12_2) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12_2
@interface BaseWindow (NSTouchBarProvider)
@property(strong) NSTouchBar* touchBar;
@end
@interface NSApplication (TouchBarMenu)
- (IBAction)toggleTouchBarCustomizationPalette:(id)sender;
@end
#endif
NS_IMPL_ISUPPORTS(nsTouchBarUpdater, nsITouchBarUpdater);
@ -49,3 +55,22 @@ nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
return NS_OK;
}
NS_IMETHODIMP
nsTouchBarUpdater::EnterCustomizeMode() {
[NSApp toggleTouchBarCustomizationPalette:(id)this];
return NS_OK;
}
NS_IMETHODIMP
nsTouchBarUpdater::IsTouchBarInitialized(bool* aResult) {
*aResult = sTouchBarIsInitialized;
return NS_OK;
}
// NOTE: This method is for internal unit tests only.
NS_IMETHODIMP
nsTouchBarUpdater::SetTouchBarInitialized(bool aIsInitialized) {
sTouchBarIsInitialized = aIsInitialized;
return NS_OK;
}

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

@ -23,10 +23,10 @@ interface nsITouchBarHelper : nsISupports
readonly attribute AString activeTitle;
/**
* Returns the layout for the Touch Bar in an nsIArray
* Returns all available Touch Bar Inputs in an nsIArray
* of nsITouchBarInput objects.
*/
attribute nsIArray layout;
attribute nsIArray allItems;
/**
* Returns the requested TouchBarInput.

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

@ -17,4 +17,21 @@ interface nsITouchBarUpdater : nsISupports
* Updates an array of nsITouchBarInputs in the specified window.
*/
void updateTouchBarInputs(in nsIBaseWindow aWindow, in Array<nsITouchBarInput> aInputs);
/**
* Enter the native Touch Bar customization window.
*/
void enterCustomizeMode();
/**
* Checks if the user is using a Touch Bar-compatible Mac.
*/
boolean isTouchBarInitialized();
/**
* Sets whether the Touch Bar is initialized.
* NOTE: This method is for internal unit tests only! Normally, the system
* sets this value after a Touch Bar is initialized on compatible Macs.
*/
void setTouchBarInitialized(in boolean aIsInitialized);
};