Commit 06cbca85 authored by Sidney San Martín's avatar Sidney San Martín Committed by Commit Bot

[Mac] [Reland] On the Touch Bar, use individual back/forward buttons instead of semented control.

This relands the original change, fixing a use-after-free when
BrowserWindowDefaultTouchBar outlives its profile.

Original change's description:

> This is a design tweak which also enables the back and forward buttons
> to be placed separately. Existing customized Touch Bars which contain
> the grouped item still work; see the code comment for details.
>
> I used Cocoa bindings for the buttons' enabled states instead of holding
> pointers to them because I initially thought that the Touch Bar might allow the
> group and individual buttons to coexist. Turns out no, but I kept the KVO
> implementation because I like it.
>
> It also includes a couple of other changes that came up while writing
> this patch and during code review:
>
> - The unit tests no longer look for a hard-coded list of items in a
>   specific order. Instead, they verify that BrowserWindowDefaultTouchBar
>   can create the items it claims to be able to create, and that no
>   known-valid identifiers have stopped working.
>
> - BrowserWindowDefaultTouchBar returns nil if it doesn't know about an
>   identifier, instead of returning an empty Touch Bar item.
>
> Bug: 937935
> Change-Id: I8cdc6347bc667b0a24fa24fe2c6a56661747ac98
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1534238
> Commit-Queue: Sidney San Martín <sdy@chromium.org>
> Reviewed-by: Leonard Grey <lgrey@chromium.org>
> Auto-Submit: Sidney San Martín <sdy@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#649899}

Bug: 937935, 945772
Change-Id: I77bf9b6d14bef875f271f8fbd4468e6cdc30434e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1568846
Commit-Queue: Sidney San Martín <sdy@chromium.org>
Reviewed-by: default avatarLeonard Grey <lgrey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#651851}
parent 8f822dff
...@@ -14,10 +14,6 @@ class BookmarkTabHelperObserver; ...@@ -14,10 +14,6 @@ class BookmarkTabHelperObserver;
class Browser; class Browser;
@class BrowserWindowTouchBarController; @class BrowserWindowTouchBarController;
namespace content {
class WebContents;
}
// Provides a default touch bar for the browser window. This class implements // Provides a default touch bar for the browser window. This class implements
// the NSTouchBarDelegate and handles the items in the touch bar. // the NSTouchBarDelegate and handles the items in the touch bar.
API_AVAILABLE(macos(10.12.2)) API_AVAILABLE(macos(10.12.2))
...@@ -29,18 +25,18 @@ API_AVAILABLE(macos(10.12.2)) ...@@ -29,18 +25,18 @@ API_AVAILABLE(macos(10.12.2))
// True if the current page is starred. Used by star touch bar button. // True if the current page is starred. Used by star touch bar button.
@property(nonatomic, assign) BOOL isStarred; @property(nonatomic, assign) BOOL isStarred;
// Designated initializer. // True if the back button is enabled.
- (instancetype)initWithBrowser:(Browser*)browser @property(nonatomic, assign) BOOL canGoBack;
controller:(BrowserWindowTouchBarController*)controller;
// Creates and returns a touch bar for the browser window. // True if the forward button is enabled.
- (NSTouchBar*)makeTouchBar; @property(nonatomic, assign) BOOL canGoForward;
@property(nonatomic, assign) BrowserWindowTouchBarController* controller;
- (void)updateWebContents:(content::WebContents*)contents; @property(nonatomic) Browser* browser;
// Updates the back/forward button. Called when creating the touch bar or when // Creates and returns a touch bar for the browser window.
// the back and forward commands have changed. - (NSTouchBar*)makeTouchBar;
- (void)updateBackForwardControl;
- (BrowserWindowTouchBarController*)controller; - (BrowserWindowTouchBarController*)controller;
...@@ -49,6 +45,11 @@ API_AVAILABLE(macos(10.12.2)) ...@@ -49,6 +45,11 @@ API_AVAILABLE(macos(10.12.2))
// Private methods exposed for testing. // Private methods exposed for testing.
@interface BrowserWindowDefaultTouchBar (ExposedForTesting) @interface BrowserWindowDefaultTouchBar (ExposedForTesting)
@property(readonly, class) NSString* reloadOrStopItemIdentifier;
@property(readonly, class) NSString* backItemIdentifier;
@property(readonly, class) NSString* forwardItemIdentifier;
@property(readonly, class) NSString* fullscreenOriginItemIdentifier;
// Updates the reload/stop button. Called when creating the touch bar or the // Updates the reload/stop button. Called when creating the touch bar or the
// page load state has been updated. // page load state has been updated.
- (void)updateReloadStopButton; - (void)updateReloadStopButton;
...@@ -56,10 +57,6 @@ API_AVAILABLE(macos(10.12.2)) ...@@ -56,10 +57,6 @@ API_AVAILABLE(macos(10.12.2))
// Returns the reload/stop button on the touch bar. Creates it if it's null. // Returns the reload/stop button on the touch bar. Creates it if it's null.
- (NSButton*)reloadStopButton; - (NSButton*)reloadStopButton;
// Returns the back/forward segmented control on the touch bar. Creates it if
// it's null.
- (NSSegmentedControl*)backForwardControl;
// Returns the bridge object that BrowserWindowDefaultTouchBar uses to receive // Returns the bridge object that BrowserWindowDefaultTouchBar uses to receive
// notifications. // notifications.
- (BookmarkTabHelperObserver*)bookmarkTabObserver; - (BookmarkTabHelperObserver*)bookmarkTabObserver;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_command_ids.h"
#include "chrome/app/vector_icons/vector_icons.h" #include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/command_observer.h" #include "chrome/browser/command_observer.h"
#include "chrome/browser/command_updater.h" #include "chrome/browser/command_updater.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
...@@ -32,6 +33,7 @@ ...@@ -32,6 +33,7 @@
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "components/url_formatter/url_formatter.h" #include "components/url_formatter/url_formatter.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#import "skia/ext/skia_utils_mac.h" #import "skia/ext/skia_utils_mac.h"
...@@ -51,7 +53,8 @@ NSString* const kBrowserWindowTouchBarId = @"browser-window"; ...@@ -51,7 +53,8 @@ NSString* const kBrowserWindowTouchBarId = @"browser-window";
NSString* const kTabFullscreenTouchBarId = @"tab-fullscreen"; NSString* const kTabFullscreenTouchBarId = @"tab-fullscreen";
// Touch bar items identifiers. // Touch bar items identifiers.
NSString* const kBackForwardTouchId = @"BACK-FWD"; NSString* const kBackTouchId = @"BACK";
NSString* const kForwardTouchId = @"FORWARD";
NSString* const kReloadOrStopTouchId = @"RELOAD-STOP"; NSString* const kReloadOrStopTouchId = @"RELOAD-STOP";
NSString* const kHomeTouchId = @"HOME"; NSString* const kHomeTouchId = @"HOME";
NSString* const kSearchTouchId = @"SEARCH"; NSString* const kSearchTouchId = @"SEARCH";
...@@ -59,9 +62,11 @@ NSString* const kStarTouchId = @"BOOKMARK"; ...@@ -59,9 +62,11 @@ NSString* const kStarTouchId = @"BOOKMARK";
NSString* const kNewTabTouchId = @"NEW-TAB"; NSString* const kNewTabTouchId = @"NEW-TAB";
NSString* const kFullscreenOriginLabelTouchId = @"FULLSCREEN-ORIGIN-LABEL"; NSString* const kFullscreenOriginLabelTouchId = @"FULLSCREEN-ORIGIN-LABEL";
// The button indexes in the back and forward segment control. // This is a combined back and forward control which can no longer be selected
const int kBackSegmentIndex = 0; // but may be in an existing customized Touch Bar. It now represents a group
const int kForwardSegmentIndex = 1; // containing the back and forward buttons, and adding the back or forward
// buttons to the Touch Bar individually magically decomposes the group.
NSString* const kBackForwardTouchId = @"BACK-FWD";
// Touch bar icon colors values. // Touch bar icon colors values.
const SkColor kTouchBarDefaultIconColor = SK_ColorWHITE; const SkColor kTouchBarDefaultIconColor = SK_ColorWHITE;
...@@ -95,7 +100,7 @@ NSButton* CreateTouchBarButton(const gfx::VectorIcon& icon, ...@@ -95,7 +100,7 @@ NSButton* CreateTouchBarButton(const gfx::VectorIcon& icon,
target:owner target:owner
action:@selector(executeCommand:)]; action:@selector(executeCommand:)];
button.tag = command; button.tag = command;
[button setAccessibilityLabel:l10n_util::GetNSString(tooltip_id)]; button.accessibilityTitle = l10n_util::GetNSString(tooltip_id);
return button; return button;
} }
...@@ -128,6 +133,8 @@ ui::TouchBarAction TouchBarActionFromCommand(int command) { ...@@ -128,6 +133,8 @@ ui::TouchBarAction TouchBarActionFromCommand(int command) {
class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge
: public CommandObserver, : public CommandObserver,
public BookmarkTabHelperObserver, public BookmarkTabHelperObserver,
public TabStripModelObserver,
public content::NotificationObserver,
public content::WebContentsObserver { public content::WebContentsObserver {
public: public:
TouchBarNotificationBridge(BrowserWindowDefaultTouchBar* owner, TouchBarNotificationBridge(BrowserWindowDefaultTouchBar* owner,
...@@ -135,48 +142,93 @@ class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge ...@@ -135,48 +142,93 @@ class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge
: owner_(owner), browser_(browser), contents_(nullptr) { : owner_(owner), browser_(browser), contents_(nullptr) {
TabStripModel* model = browser_->tab_strip_model(); TabStripModel* model = browser_->tab_strip_model();
DCHECK(model); DCHECK(model);
model->AddObserver(this);
UpdateWebContents(model->GetActiveWebContents()); UpdateWebContents(model->GetActiveWebContents());
auto* command_controller = browser->command_controller();
command_controller->AddCommandObserver(IDC_BACK, this);
owner.canGoBack = command_controller->IsCommandEnabled(IDC_BACK);
command_controller->AddCommandObserver(IDC_FORWARD, this);
owner.canGoBack = command_controller->IsCommandEnabled(IDC_FORWARD);
auto* profile = browser->profile();
auto* prefs = profile->GetPrefs();
show_home_button_.Init(
prefs::kShowHomeButton, prefs,
base::BindRepeating(&TouchBarNotificationBridge::UpdateTouchBar,
base::Unretained(this)));
profile_pref_registrar_.Init(prefs);
profile_pref_registrar_.Add(
DefaultSearchManager::kDefaultSearchProviderDataPrefName,
base::BindRepeating(&TouchBarNotificationBridge::UpdateTouchBar,
base::Unretained(this)));
notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
content::Source<Profile>(profile));
} }
bool show_home_button() { return show_home_button_.GetValue(); }
~TouchBarNotificationBridge() override { ~TouchBarNotificationBridge() override {
if (contents_) browser_->tab_strip_model()->RemoveObserver(this);
BookmarkTabHelper::FromWebContents(contents_)->RemoveObserver(this); UpdateWebContents(nullptr);
} }
void UpdateTouchBar() { [[owner_ controller] invalidateTouchBar]; } void UpdateTouchBar() { [[owner_ controller] invalidateTouchBar]; }
void UpdateWebContents(content::WebContents* new_contents) { void UpdateWebContents(content::WebContents* new_contents) {
if (contents_) { if (contents_ == new_contents)
return;
if (contents_)
BookmarkTabHelper::FromWebContents(contents_)->RemoveObserver(this); BookmarkTabHelper::FromWebContents(contents_)->RemoveObserver(this);
}
contents_ = new_contents; contents_ = new_contents;
Observe(contents_);
bool is_starred = false; // Stop observing the old WebContents and start observing the new one (if
if (contents_) { // nonnull). Qualified to disambiguate from NotificationObserver::Observe().
BookmarkTabHelper* helper = BookmarkTabHelper::FromWebContents(contents_); WebContentsObserver::Observe(contents_);
helper->AddObserver(this);
is_starred = helper->is_starred(); BookmarkTabHelper* bookmark_helper =
} contents_ ? BookmarkTabHelper::FromWebContents(contents_) : nullptr;
if (bookmark_helper)
bookmark_helper->AddObserver(this);
[owner_ setIsPageLoading:contents_ && contents_->IsLoading()]; owner_.isPageLoading = contents_ && contents_->IsLoading();
[owner_ setIsStarred:is_starred]; owner_.isStarred = bookmark_helper && bookmark_helper->is_starred();
UpdateTouchBar();
} }
// BookmarkTabHelperObserver: // BookmarkTabHelperObserver:
void URLStarredChanged(content::WebContents* web_contents, void URLStarredChanged(content::WebContents* web_contents,
bool starred) override { bool starred) override {
DCHECK(web_contents == contents_); DCHECK(web_contents == contents_);
[owner_ setIsStarred:starred]; owner_.isStarred = starred;
} }
protected: protected:
// CommandObserver: // CommandObserver:
void EnabledStateChangedForCommand(int command, bool enabled) override { void EnabledStateChangedForCommand(int command, bool enabled) override {
DCHECK(command == IDC_BACK || command == IDC_FORWARD); DCHECK(command == IDC_BACK || command == IDC_FORWARD);
[owner_ updateBackForwardControl]; if (command == IDC_BACK)
owner_.canGoBack = enabled;
else if (command == IDC_FORWARD)
owner_.canGoForward = enabled;
}
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
UpdateWebContents(selection.new_contents);
}
// NotificationObserver:
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override {
owner_.browser = nullptr;
} }
// WebContentsObserver: // WebContentsObserver:
...@@ -187,52 +239,40 @@ class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge ...@@ -187,52 +239,40 @@ class API_AVAILABLE(macos(10.12.2)) TouchBarNotificationBridge
void DidStartLoading() override { void DidStartLoading() override {
DCHECK(contents_ && contents_->IsLoading()); DCHECK(contents_ && contents_->IsLoading());
[owner_ setIsPageLoading:YES]; owner_.isPageLoading = YES;
} }
void DidStopLoading() override { void DidStopLoading() override {
DCHECK(contents_ && !contents_->IsLoading()); DCHECK(contents_ && !contents_->IsLoading());
[owner_ setIsPageLoading:NO]; owner_.isPageLoading = NO;
} }
void WebContentsDestroyed() override { UpdateWebContents(nullptr); }
private: private:
BrowserWindowDefaultTouchBar* owner_; // Weak. BrowserWindowDefaultTouchBar* owner_; // Weak.
Browser* browser_; // Weak. Browser* browser_; // Weak.
content::WebContents* contents_; // Weak. content::WebContents* contents_; // Weak.
// Used to monitor the optional home button pref.
BooleanPrefMember show_home_button_;
content::NotificationRegistrar notification_registrar_;
PrefChangeRegistrar profile_pref_registrar_;
DISALLOW_COPY_AND_ASSIGN(TouchBarNotificationBridge); DISALLOW_COPY_AND_ASSIGN(TouchBarNotificationBridge);
}; };
id<NSAccessibility> ToNSAccessibility(id object) {
return [object conformsToProtocol:@protocol(NSAccessibility)] ? object : nil;
}
} // namespace } // namespace
@interface BrowserWindowDefaultTouchBar () { @interface BrowserWindowDefaultTouchBar () {
// Used to execute commands such as navigating back and forward.
CommandUpdater* commandUpdater_; // Weak, owned by Browser.
// The browser associated with the touch bar.
Browser* browser_; // Weak.
BrowserWindowTouchBarController* controller_; // Weak.
// Used to monitor the optional home button pref.
BooleanPrefMember showHomeButton_;
// Used to listen for default search engine pref changes.
PrefChangeRegistrar profilePrefRegistrar_;
// Used to receive and handle notifications. // Used to receive and handle notifications.
std::unique_ptr<TouchBarNotificationBridge> notificationBridge_; std::unique_ptr<TouchBarNotificationBridge> notificationBridge_;
// The stop/reload button in the touch bar. // The stop/reload button in the touch bar.
base::scoped_nsobject<NSButton> reloadStopButton_; base::scoped_nsobject<NSButton> reloadStopButton_;
// The back/forward segmented control in the touch bar.
base::scoped_nsobject<NSSegmentedControl> backForwardControl_;
// The starred button in the touch bar. // The starred button in the touch bar.
base::scoped_nsobject<NSButton> starredButton_; base::scoped_nsobject<NSButton> starredButton_;
} }
...@@ -240,9 +280,6 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -240,9 +280,6 @@ id<NSAccessibility> ToNSAccessibility(id object) {
// Creates and returns a touch bar for tab fullscreen mode. // Creates and returns a touch bar for tab fullscreen mode.
- (NSTouchBar*)createTabFullscreenTouchBar; - (NSTouchBar*)createTabFullscreenTouchBar;
// Sets up the back and forward segmented control.
- (void)setupBackForwardControl;
// Updates the starred button in the touch bar. // Updates the starred button in the touch bar.
- (void)updateStarredButton; - (void)updateStarredButton;
...@@ -255,35 +292,10 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -255,35 +292,10 @@ id<NSAccessibility> ToNSAccessibility(id object) {
@synthesize isPageLoading = isPageLoading_; @synthesize isPageLoading = isPageLoading_;
@synthesize isStarred = isStarred_; @synthesize isStarred = isStarred_;
@synthesize canGoBack = canGoBack_;
- (instancetype)initWithBrowser:(Browser*)browser @synthesize canGoForward = canGoForward_;
controller:(BrowserWindowTouchBarController*)controller { @synthesize controller = controller_;
if ((self = [super init])) { @synthesize browser = browser_;
DCHECK(browser);
browser_ = browser;
controller_ = controller;
notificationBridge_.reset(new TouchBarNotificationBridge(self, browser));
commandUpdater_ = browser->command_controller();
commandUpdater_->AddCommandObserver(IDC_BACK, notificationBridge_.get());
commandUpdater_->AddCommandObserver(IDC_FORWARD, notificationBridge_.get());
PrefService* prefs = browser->profile()->GetPrefs();
showHomeButton_.Init(
prefs::kShowHomeButton, prefs,
base::BindRepeating(&TouchBarNotificationBridge::UpdateTouchBar,
base::Unretained(notificationBridge_.get())));
profilePrefRegistrar_.Init(prefs);
profilePrefRegistrar_.Add(
DefaultSearchManager::kDefaultSearchProviderDataPrefName,
base::BindRepeating(&TouchBarNotificationBridge::UpdateTouchBar,
base::Unretained(notificationBridge_.get())));
}
return self;
}
- (NSTouchBar*)makeTouchBar { - (NSTouchBar*)makeTouchBar {
// When in tab or extension fullscreen, we should show a touch bar containing // When in tab or extension fullscreen, we should show a touch bar containing
...@@ -301,12 +313,12 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -301,12 +313,12 @@ id<NSAccessibility> ToNSAccessibility(id object) {
setCustomizationIdentifier:ui::GetTouchBarId(kBrowserWindowTouchBarId)]; setCustomizationIdentifier:ui::GetTouchBarId(kBrowserWindowTouchBarId)];
[touchBar setDelegate:self]; [touchBar setDelegate:self];
NSMutableArray* customIdentifiers = [NSMutableArray arrayWithCapacity:7]; NSMutableArray<NSString*>* customIdentifiers = [NSMutableArray array];
NSMutableArray* defaultIdentifiers = [NSMutableArray arrayWithCapacity:6]; NSMutableArray<NSString*>* defaultIdentifiers = [NSMutableArray array];
NSArray* touchBarItems = @[ NSArray<NSString*>* touchBarItems = @[
kBackForwardTouchId, kReloadOrStopTouchId, kHomeTouchId, kSearchTouchId, kBackTouchId, kForwardTouchId, kReloadOrStopTouchId, kHomeTouchId,
kStarTouchId, kNewTabTouchId kSearchTouchId, kStarTouchId, kNewTabTouchId
]; ];
for (NSString* item in touchBarItems) { for (NSString* item in touchBarItems) {
...@@ -315,8 +327,10 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -315,8 +327,10 @@ id<NSAccessibility> ToNSAccessibility(id object) {
[customIdentifiers addObject:itemIdentifier]; [customIdentifiers addObject:itemIdentifier];
// Don't add the home button if it's not shown in the toolbar. // Don't add the home button if it's not shown in the toolbar.
if (showHomeButton_.GetValue() || ![item isEqualTo:kHomeTouchId]) if (item == kHomeTouchId && !notificationBridge_->show_home_button())
[defaultIdentifiers addObject:itemIdentifier]; continue;
[defaultIdentifiers addObject:itemIdentifier];
} }
[customIdentifiers addObject:NSTouchBarItemIdentifierFlexibleSpace]; [customIdentifiers addObject:NSTouchBarItemIdentifierFlexibleSpace];
...@@ -332,14 +346,41 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -332,14 +346,41 @@ id<NSAccessibility> ToNSAccessibility(id object) {
if (!touchBar) if (!touchBar)
return nil; return nil;
if ([identifier hasSuffix:kBackForwardTouchId]) {
auto* items = @[
[touchBar itemForIdentifier:ui::GetTouchBarItemId(
kBrowserWindowTouchBarId, kBackTouchId)],
[touchBar
itemForIdentifier:ui::GetTouchBarItemId(kBrowserWindowTouchBarId,
kForwardTouchId)],
];
auto groupItem = [NSGroupTouchBarItem groupItemWithIdentifier:identifier
items:items];
[groupItem setCustomizationLabel:
l10n_util::GetNSString(
IDS_TOUCH_BAR_BACK_FORWARD_CUSTOMIZATION_LABEL)];
return groupItem;
}
base::scoped_nsobject<NSCustomTouchBarItem> touchBarItem( base::scoped_nsobject<NSCustomTouchBarItem> touchBarItem(
[[ui::NSCustomTouchBarItem() alloc] initWithIdentifier:identifier]); [[ui::NSCustomTouchBarItem() alloc] initWithIdentifier:identifier]);
if ([identifier hasSuffix:kBackForwardTouchId]) { if ([identifier hasSuffix:kBackTouchId]) {
[self updateBackForwardControl]; auto* button = CreateTouchBarButton(vector_icons::kBackArrowIcon, self,
[touchBarItem setView:backForwardControl_.get()]; IDC_BACK, IDS_ACCNAME_BACK);
[touchBarItem setCustomizationLabel: [button bind:@"enabled" toObject:self withKeyPath:@"canGoBack" options:nil];
l10n_util::GetNSString( [touchBarItem setView:button];
IDS_TOUCH_BAR_BACK_FORWARD_CUSTOMIZATION_LABEL)]; [touchBarItem
setCustomizationLabel:l10n_util::GetNSString(IDS_ACCNAME_BACK)];
} else if ([identifier hasSuffix:kForwardTouchId]) {
auto* button = CreateTouchBarButton(vector_icons::kForwardArrowIcon, self,
IDC_FORWARD, IDS_ACCNAME_FORWARD);
[button bind:@"enabled"
toObject:self
withKeyPath:@"canGoForward"
options:nil];
[touchBarItem setView:button];
[touchBarItem
setCustomizationLabel:l10n_util::GetNSString(IDS_ACCNAME_FORWARD)];
} else if ([identifier hasSuffix:kReloadOrStopTouchId]) { } else if ([identifier hasSuffix:kReloadOrStopTouchId]) {
[self updateReloadStopButton]; [self updateReloadStopButton];
[touchBarItem setView:reloadStopButton_.get()]; [touchBarItem setView:reloadStopButton_.get()];
...@@ -398,6 +439,8 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -398,6 +439,8 @@ id<NSAccessibility> ToNSAccessibility(id object) {
[touchBarItem [touchBarItem
setView:[NSTextField labelWithAttributedString:attributedString.get()]]; setView:[NSTextField labelWithAttributedString:attributedString.get()]];
} else {
return nil;
} }
return touchBarItem.autorelease(); return touchBarItem.autorelease();
...@@ -412,61 +455,12 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -412,61 +455,12 @@ id<NSAccessibility> ToNSAccessibility(id object) {
return touchBar.autorelease(); return touchBar.autorelease();
} }
- (void)setupBackForwardControl { - (void)setBrowser:(Browser*)browser {
NSMutableArray* images = [NSMutableArray arrayWithArray:@[ if (browser_ == browser)
CreateNSImageFromIcon(vector_icons::kBackArrowIcon), return;
CreateNSImageFromIcon(vector_icons::kForwardArrowIcon) browser_ = browser;
]]; notificationBridge_.reset(
browser_ ? new TouchBarNotificationBridge(self, browser_) : nullptr);
// Offset the icons so that it matches the height of the other Touch Bar
// items.
const int kIconYOffset = 2;
for (NSUInteger i = 0; i < [images count]; i++) {
NSImage* image = [images objectAtIndex:i];
NSSize size = [image size];
size.height += kIconYOffset;
NSImage* offsettedImage = [[[NSImage alloc] initWithSize:size] autorelease];
[offsettedImage lockFocus];
[image drawInRect:NSMakeRect(0, 0, size.width, size.height - kIconYOffset)];
[offsettedImage unlockFocus];
[images replaceObjectAtIndex:i withObject:offsettedImage];
}
NSSegmentedControl* control = [NSSegmentedControl
segmentedControlWithImages:images
trackingMode:NSSegmentSwitchTrackingMomentary
target:self
action:@selector(backOrForward:)];
// Use the accessibility protocol to get the children.
// Use NSAccessibilityUnignoredDescendant to be sure we start with
// the correct object.
id<NSAccessibility> segmentElement =
ToNSAccessibility(NSAccessibilityUnignoredDescendant(control));
DCHECK(segmentElement);
NSArray<id<NSAccessibility>>* segments = segmentElement.accessibilityChildren;
ToNSAccessibility(segments[0]).accessibilityTitle =
l10n_util::GetNSString(IDS_ACCNAME_BACK);
ToNSAccessibility(segments[1]).accessibilityTitle =
l10n_util::GetNSString(IDS_ACCNAME_FORWARD);
backForwardControl_.reset([control retain]);
}
- (void)updateWebContents:(content::WebContents*)contents {
notificationBridge_->UpdateWebContents(contents);
}
- (void)updateBackForwardControl {
if (!backForwardControl_)
[self setupBackForwardControl];
[backForwardControl_ setSegmentStyle:NSSegmentStyleSeparated];
[backForwardControl_ setEnabled:commandUpdater_->IsCommandEnabled(IDC_BACK)
forSegment:kBackSegmentIndex];
[backForwardControl_ setEnabled:commandUpdater_->IsCommandEnabled(IDC_FORWARD)
forSegment:kForwardSegmentIndex];
} }
- (void)updateStarredButton { - (void)updateStarredButton {
...@@ -485,10 +479,6 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -485,10 +479,6 @@ id<NSAccessibility> ToNSAccessibility(id object) {
[starredButton_ setAccessibilityLabel:l10n_util::GetNSString(tooltipId)]; [starredButton_ setAccessibilityLabel:l10n_util::GetNSString(tooltipId)];
} }
- (BrowserWindowTouchBarController*)controller {
return controller_;
}
- (NSView*)searchTouchBarView { - (NSView*)searchTouchBarView {
TemplateURLService* templateUrlService = TemplateURLService* templateUrlService =
TemplateURLServiceFactory::GetForProfile(browser_->profile()); TemplateURLServiceFactory::GetForProfile(browser_->profile());
...@@ -532,18 +522,10 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -532,18 +522,10 @@ id<NSAccessibility> ToNSAccessibility(id object) {
return searchButton; return searchButton;
} }
- (void)backOrForward:(id)sender {
NSSegmentedControl* control = sender;
int command =
[control selectedSegment] == kBackSegmentIndex ? IDC_BACK : IDC_FORWARD;
LogTouchBarUMA(TouchBarActionFromCommand(command));
commandUpdater_->ExecuteCommand(command);
}
- (void)executeCommand:(id)sender { - (void)executeCommand:(id)sender {
int command = [sender tag]; int command = [sender tag];
ui::LogTouchBarUMA(TouchBarActionFromCommand(command)); ui::LogTouchBarUMA(TouchBarActionFromCommand(command));
commandUpdater_->ExecuteCommand(command); browser_->command_controller()->ExecuteCommand(command);
} }
- (void)setIsPageLoading:(BOOL)isPageLoading { - (void)setIsPageLoading:(BOOL)isPageLoading {
...@@ -561,6 +543,23 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -561,6 +543,23 @@ id<NSAccessibility> ToNSAccessibility(id object) {
// Private methods exposed for testing. // Private methods exposed for testing.
@implementation BrowserWindowDefaultTouchBar (ExposedForTesting) @implementation BrowserWindowDefaultTouchBar (ExposedForTesting)
+ (NSString*)reloadOrStopItemIdentifier {
return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kReloadOrStopTouchId);
}
+ (NSString*)backItemIdentifier {
return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kBackTouchId);
}
+ (NSString*)forwardItemIdentifier {
return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kForwardTouchId);
}
+ (NSString*)fullscreenOriginItemIdentifier {
return ui::GetTouchBarItemId(kTabFullscreenTouchBarId,
kFullscreenOriginLabelTouchId);
}
- (void)updateReloadStopButton { - (void)updateReloadStopButton {
const gfx::VectorIcon& icon = const gfx::VectorIcon& icon =
isPageLoading_ ? kNavigateStopIcon : vector_icons::kReloadIcon; isPageLoading_ ? kNavigateStopIcon : vector_icons::kReloadIcon;
...@@ -586,13 +585,6 @@ id<NSAccessibility> ToNSAccessibility(id object) { ...@@ -586,13 +585,6 @@ id<NSAccessibility> ToNSAccessibility(id object) {
return reloadStopButton_.get(); return reloadStopButton_.get();
} }
- (NSSegmentedControl*)backForwardControl {
if (!backForwardControl_)
[self updateBackForwardControl];
return backForwardControl_.get();
}
- (BookmarkTabHelperObserver*)bookmarkTabObserver { - (BookmarkTabHelperObserver*)bookmarkTabObserver {
return notificationBridge_.get(); return notificationBridge_.get();
} }
......
...@@ -18,33 +18,14 @@ ...@@ -18,33 +18,14 @@
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h" #include "testing/gtest_mac.h"
#import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/OCMock/OCMock.h"
#import "ui/base/cocoa/touch_bar_util.h" #import "ui/base/cocoa/touch_bar_util.h"
#include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/l10n/l10n_util_mac.h"
namespace {
// Touch bar identifiers.
NSString* const kBrowserWindowTouchBarId = @"browser-window";
NSString* const kTabFullscreenTouchBarId = @"tab-fullscreen";
// Touch bar items identifiers.
NSString* const kBackForwardTouchId = @"BACK-FWD";
NSString* const kReloadOrStopTouchId = @"RELOAD-STOP";
NSString* const kHomeTouchId = @"HOME";
NSString* const kSearchTouchId = @"SEARCH";
NSString* const kStarTouchId = @"BOOKMARK";
NSString* const kNewTabTouchId = @"NEW-TAB";
NSString* const kFullscreenOriginLabelTouchId = @"FULLSCREEN-ORIGIN-LABEL";
// The button indexes in the back and forward segment control.
const int kBackSegmentIndex = 0;
const int kForwardSegmentIndex = 1;
} // namespace
class BrowserWindowDefaultTouchBarUnitTest : public CocoaProfileTest { class BrowserWindowDefaultTouchBarUnitTest : public CocoaProfileTest {
public: public:
void SetUp() override { void SetUp() override {
...@@ -53,75 +34,117 @@ class BrowserWindowDefaultTouchBarUnitTest : public CocoaProfileTest { ...@@ -53,75 +34,117 @@ class BrowserWindowDefaultTouchBarUnitTest : public CocoaProfileTest {
command_updater_ = browser()->command_controller(); command_updater_ = browser()->command_controller();
browser()->tab_strip_model()->AppendWebContents(
content::WebContentsTester::CreateTestWebContents(profile(), nullptr),
true);
if (@available(macOS 10.12.2, *)) { if (@available(macOS 10.12.2, *)) {
touch_bar_.reset([[BrowserWindowDefaultTouchBar alloc] touch_bar_.reset([[BrowserWindowDefaultTouchBar alloc] init]);
initWithBrowser:browser() touch_bar_.get().browser = browser();
controller:nil]);
} }
} }
NSString* GetFullscreenTouchBarItemId(NSString* id) {
return ui::GetTouchBarItemId(kTabFullscreenTouchBarId, id);
}
NSString* GetBrowserTouchBarItemId(NSString* id) {
return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, id);
}
void UpdateCommandEnabled(int id, bool enabled) { void UpdateCommandEnabled(int id, bool enabled) {
command_updater_->UpdateCommandEnabled(id, enabled); command_updater_->UpdateCommandEnabled(id, enabled);
} }
void TearDown() override { void TearDown() override {
if (@available(macOS 10.12.2, *)) if (@available(macOS 10.12.2, *)) {
touch_bar_.get().browser = nullptr;
touch_bar_.reset(); touch_bar_.reset();
}
CocoaProfileTest::TearDown(); CocoaProfileTest::TearDown();
} }
CommandUpdater* command_updater_; // Weak, owned by Browser. CommandUpdater* command_updater_; // Weak, owned by Browser.
content::RenderViewHostTestEnabler rvh_test_enabler_;
API_AVAILABLE(macos(10.12.2)) API_AVAILABLE(macos(10.12.2))
base::scoped_nsobject<BrowserWindowDefaultTouchBar> touch_bar_; base::scoped_nsobject<BrowserWindowDefaultTouchBar> touch_bar_;
}; };
// Tests to check if the touch bar contains the correct items. // Test if any known identifiers no longer work. See the message in the test;
// these identifiers may be written out to disk on users' computers if they
// customize the Touch Bar, and the corresponding items will disappear if they
// can no longer be created.
TEST_F(BrowserWindowDefaultTouchBarUnitTest, HistoricTouchBarItems) {
if (@available(macOS 10.12.2, *)) {
NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
for (NSString* item_identifier : {
@"BACK-FWD",
@"BACK",
@"FORWARD",
@"RELOAD-STOP",
@"HOME",
@"SEARCH",
@"BOOKMARK",
@"NEW-TAB",
}) {
auto identifier =
ui::GetTouchBarItemId(@"browser-window", item_identifier);
EXPECT_NE(nil, [touch_bar itemForIdentifier:identifier])
<< "BrowserWindowDefaultTouchBar didn't return a Touch Bar item for "
"an identifier that was once available ("
<< identifier.UTF8String
<< "). If a user's customized Touch Bar includes this item, it will "
"disappear! Do not update or remove entries in this list just to "
"make the test pass; keep supporting old identifiers when "
"possible, even if they're no longer part of the set of "
"default/customizable items.";
}
}
}
// Tests if BrowserWindowDefaultTouchBar can produce the items it says it can
// and, for each kind of bar, also verify that the advertised/customizable lists
// include some representative items (if not, the lists might be wrong.)
TEST_F(BrowserWindowDefaultTouchBarUnitTest, TouchBarItems) { TEST_F(BrowserWindowDefaultTouchBarUnitTest, TouchBarItems) {
if (@available(macOS 10.12.2, *)) { if (@available(macOS 10.12.2, *)) {
auto test_default_identifiers =
[&](NSSet* expected_identifiers) API_AVAILABLE(macos(10.12.2)) {
NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
NSMutableSet<NSString*>* advertised_identifiers = [NSMutableSet set];
[advertised_identifiers
addObjectsFromArray:touch_bar.defaultItemIdentifiers];
[advertised_identifiers
addObjectsFromArray:touch_bar
.customizationAllowedItemIdentifiers];
[advertised_identifiers
addObjectsFromArray:touch_bar
.customizationRequiredItemIdentifiers];
EXPECT_TRUE(
[expected_identifiers isSubsetOfSet:advertised_identifiers])
<< "Didn't find the expected identifiers "
<< expected_identifiers.description.UTF8String
<< " in the set of advertised identifiers "
<< advertised_identifiers.description.UTF8String << ".";
for (NSString* identifier in advertised_identifiers) {
EXPECT_NE(nil, [touch_bar itemForIdentifier:identifier])
<< "Didn't get a touch bar item for " << identifier.UTF8String;
}
};
// Set to tab fullscreen. // Set to tab fullscreen.
FullscreenController* fullscreen_controller = FullscreenController* fullscreen_controller =
browser()->exclusive_access_manager()->fullscreen_controller(); browser()->exclusive_access_manager()->fullscreen_controller();
fullscreen_controller->set_is_tab_fullscreen_for_testing(true); fullscreen_controller->set_is_tab_fullscreen_for_testing(true);
EXPECT_TRUE(fullscreen_controller->IsTabFullscreen()); EXPECT_TRUE(fullscreen_controller->IsTabFullscreen());
// The touch bar should only contain an item that displays the origin of the // The fullscreen Touch Bar should include *at least* these items.
// tab content fullscreen. test_default_identifiers([NSSet setWithArray:@[
NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; BrowserWindowDefaultTouchBar.fullscreenOriginItemIdentifier,
NSArray* touch_bar_items = [touch_bar itemIdentifiers]; ]]);
EXPECT_TRUE(
[touch_bar_items containsObject:GetFullscreenTouchBarItemId(
kFullscreenOriginLabelTouchId)]);
EXPECT_EQ(1u, [touch_bar_items count]);
// Exit fullscreen. // Exit fullscreen.
fullscreen_controller->set_is_tab_fullscreen_for_testing(false); fullscreen_controller->set_is_tab_fullscreen_for_testing(false);
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen()); EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
PrefService* prefs = profile()->GetPrefs(); // The default Touch Bar should include *at least* these items.
DCHECK(prefs); test_default_identifiers([NSSet setWithArray:@[
prefs->SetBoolean(prefs::kShowHomeButton, true); BrowserWindowDefaultTouchBar.backItemIdentifier,
touch_bar_items = [[touch_bar_ makeTouchBar] itemIdentifiers]; BrowserWindowDefaultTouchBar.forwardItemIdentifier,
EXPECT_TRUE([touch_bar_items BrowserWindowDefaultTouchBar.reloadOrStopItemIdentifier,
containsObject:GetBrowserTouchBarItemId(kBackForwardTouchId)]); ]]);
EXPECT_TRUE([touch_bar_items
containsObject:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]);
EXPECT_TRUE([touch_bar_items
containsObject:GetBrowserTouchBarItemId(kHomeTouchId)]);
EXPECT_TRUE([touch_bar_items
containsObject:GetBrowserTouchBarItemId(kSearchTouchId)]);
EXPECT_TRUE([touch_bar_items
containsObject:GetBrowserTouchBarItemId(kStarTouchId)]);
EXPECT_TRUE([touch_bar_items
containsObject:GetBrowserTouchBarItemId(kNewTabTouchId)]);
} }
} }
...@@ -131,56 +154,69 @@ TEST_F(BrowserWindowDefaultTouchBarUnitTest, ReloadOrStopTouchBarItem) { ...@@ -131,56 +154,69 @@ TEST_F(BrowserWindowDefaultTouchBarUnitTest, ReloadOrStopTouchBarItem) {
NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
[touch_bar_ setIsPageLoading:NO]; [touch_bar_ setIsPageLoading:NO];
NSTouchBarItem* item = [touch_bar_ NSTouchBarItem* item =
touchBar:touch_bar [touch_bar itemForIdentifier:BrowserWindowDefaultTouchBar
makeItemForIdentifier:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]; .reloadOrStopItemIdentifier];
EXPECT_EQ(IDC_RELOAD, [[item view] tag]); EXPECT_EQ(IDC_RELOAD, [[item view] tag]);
[touch_bar_ setIsPageLoading:YES]; [touch_bar_ setIsPageLoading:YES];
item = [touch_bar_ touchBar:touch_bar item = [touch_bar itemForIdentifier:BrowserWindowDefaultTouchBar
makeItemForIdentifier:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]; .reloadOrStopItemIdentifier];
EXPECT_EQ(IDC_STOP, [[item view] tag]); EXPECT_EQ(IDC_STOP, [[item view] tag]);
} }
} }
// Tests to see if the back/forward items on the touch bar is in sync with the // Tests if the back button on the touch bar is in sync with the back command.
// back and forward commands. TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackCommandUpdate) {
TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackForwardCommandUpdate) {
if (@available(macOS 10.12.2, *)) { if (@available(macOS 10.12.2, *)) {
NSSegmentedControl* back_forward_control = [touch_bar_ backForwardControl]; NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
NSTouchBarItem* item = [touch_bar
itemForIdentifier:BrowserWindowDefaultTouchBar.backItemIdentifier];
NSButton* button = base::mac::ObjCCast<NSButton>(item.view);
UpdateCommandEnabled(IDC_BACK, true); UpdateCommandEnabled(IDC_BACK, true);
UpdateCommandEnabled(IDC_FORWARD, true); EXPECT_TRUE(button.enabled);
EXPECT_TRUE([back_forward_control isEnabledForSegment:kBackSegmentIndex]);
EXPECT_TRUE(
[back_forward_control isEnabledForSegment:kForwardSegmentIndex]);
UpdateCommandEnabled(IDC_BACK, false); UpdateCommandEnabled(IDC_BACK, false);
EXPECT_FALSE([back_forward_control isEnabledForSegment:kBackSegmentIndex]); EXPECT_FALSE(button.enabled);
EXPECT_TRUE( }
[back_forward_control isEnabledForSegment:kForwardSegmentIndex]); }
// Tests if the forward button on the touch bar is in sync with the forward
// command.
TEST_F(BrowserWindowDefaultTouchBarUnitTest, ForwardCommandUpdate) {
if (@available(macOS 10.12.2, *)) {
NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
NSTouchBarItem* item = [touch_bar
itemForIdentifier:BrowserWindowDefaultTouchBar.forwardItemIdentifier];
NSButton* button = base::mac::ObjCCast<NSButton>(item.view);
UpdateCommandEnabled(IDC_FORWARD, true);
EXPECT_TRUE(button.enabled);
UpdateCommandEnabled(IDC_FORWARD, false); UpdateCommandEnabled(IDC_FORWARD, false);
EXPECT_FALSE([back_forward_control isEnabledForSegment:kBackSegmentIndex]); EXPECT_FALSE(button.enabled);
EXPECT_FALSE(
[back_forward_control isEnabledForSegment:kForwardSegmentIndex]);
} }
} }
TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackForwardAccessibilityLabels) { TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackAccessibilityLabel) {
if (@available(macOS 10.12.2, *)) { if (@available(macOS 10.12.2, *)) {
NSSegmentedControl* control = touch_bar_.get().backForwardControl; NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
id<NSAccessibility> cell = NSAccessibilityUnignoredDescendant(control); NSTouchBarItem* item = [touch_bar
ASSERT_TRUE([cell conformsToProtocol:@protocol(NSAccessibility)]); itemForIdentifier:BrowserWindowDefaultTouchBar.backItemIdentifier];
id<NSAccessibility> view = item.view;
id<NSAccessibility> back = cell.accessibilityChildren[0]; ASSERT_TRUE([view conformsToProtocol:@protocol(NSAccessibility)]);
EXPECT_TRUE([back conformsToProtocol:@protocol(NSAccessibility)]); EXPECT_NSEQ(view.accessibilityTitle,
EXPECT_NSEQ(back.accessibilityTitle,
l10n_util::GetNSString(IDS_ACCNAME_BACK)); l10n_util::GetNSString(IDS_ACCNAME_BACK));
}
}
id<NSAccessibility> forward = cell.accessibilityChildren[1]; TEST_F(BrowserWindowDefaultTouchBarUnitTest, ForwardAccessibilityLabel) {
EXPECT_TRUE([forward conformsToProtocol:@protocol(NSAccessibility)]); if (@available(macOS 10.12.2, *)) {
EXPECT_NSEQ(forward.accessibilityTitle, NSTouchBar* touch_bar = [touch_bar_ makeTouchBar];
NSTouchBarItem* item = [touch_bar
itemForIdentifier:BrowserWindowDefaultTouchBar.forwardItemIdentifier];
id<NSAccessibility> view = item.view;
ASSERT_TRUE([view conformsToProtocol:@protocol(NSAccessibility)]);
EXPECT_NSEQ(view.accessibilityTitle,
l10n_util::GetNSString(IDS_ACCNAME_FORWARD)); l10n_util::GetNSString(IDS_ACCNAME_FORWARD));
} }
} }
...@@ -32,9 +32,6 @@ API_AVAILABLE(macos(10.12.2)) ...@@ -32,9 +32,6 @@ API_AVAILABLE(macos(10.12.2))
// nil. // nil.
- (void)invalidateTouchBar; - (void)invalidateTouchBar;
- (void)updateWebContents:(content::WebContents*)contents;
- (content::WebContents*)webContents;
@end @end
@interface BrowserWindowTouchBarController (ExposedForTesting) @interface BrowserWindowTouchBarController (ExposedForTesting)
......
...@@ -19,66 +19,9 @@ ...@@ -19,66 +19,9 @@
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#import "ui/base/cocoa/touch_bar_util.h" #import "ui/base/cocoa/touch_bar_util.h"
class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge
: public TabStripModelObserver,
public content::WebContentsObserver {
public:
WebContentsNotificationBridge(BrowserWindowTouchBarController* owner,
Browser* browser)
: owner_(owner), browser_(browser), contents_(nullptr) {
TabStripModel* model = browser_->tab_strip_model();
DCHECK(model);
model->AddObserver(this);
UpdateWebContents(model->GetActiveWebContents());
}
~WebContentsNotificationBridge() override {
TabStripModel* model = browser_->tab_strip_model();
if (model)
model->RemoveObserver(this);
}
void UpdateWebContents(content::WebContents* new_contents) {
contents_ = new_contents;
Observe(contents_);
[owner_ updateWebContents:contents_];
}
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (tab_strip_model->empty() || !selection.active_tab_changed())
return;
UpdateWebContents(selection.new_contents);
contents_ = selection.new_contents;
}
content::WebContents* contents() const { return contents_; }
protected:
// WebContentsObserver:
void WebContentsDestroyed() override {
// Clean up if the web contents is being destroyed.
UpdateWebContents(nullptr);
}
private:
BrowserWindowTouchBarController* owner_; // Weak.
Browser* browser_; // Weak.
content::WebContents* contents_; // Weak.
};
@interface BrowserWindowTouchBarController () { @interface BrowserWindowTouchBarController () {
NSWindow* window_; // Weak. NSWindow* window_; // Weak.
// Used to receive and handle notifications.
std::unique_ptr<WebContentsNotificationBridge> notificationBridge_;
base::scoped_nsobject<BrowserWindowDefaultTouchBar> defaultTouchBar_; base::scoped_nsobject<BrowserWindowDefaultTouchBar> defaultTouchBar_;
base::scoped_nsobject<WebTextfieldTouchBarController> webTextfieldTouchBar_; base::scoped_nsobject<WebTextfieldTouchBarController> webTextfieldTouchBar_;
...@@ -92,12 +35,9 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge ...@@ -92,12 +35,9 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge
DCHECK(browser); DCHECK(browser);
window_ = window; window_ = window;
notificationBridge_ = defaultTouchBar_.reset([[BrowserWindowDefaultTouchBar alloc] init]);
std::make_unique<WebContentsNotificationBridge>(self, browser); defaultTouchBar_.get().controller = self;
defaultTouchBar_.get().browser = browser;
defaultTouchBar_.reset([[BrowserWindowDefaultTouchBar alloc]
initWithBrowser:browser
controller:self]);
webTextfieldTouchBar_.reset( webTextfieldTouchBar_.reset(
[[WebTextfieldTouchBarController alloc] initWithController:self]); [[WebTextfieldTouchBarController alloc] initWithController:self]);
} }
...@@ -105,6 +45,11 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge ...@@ -105,6 +45,11 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge
return self; return self;
} }
- (void)dealloc {
defaultTouchBar_.get().browser = nullptr;
[super dealloc];
}
- (void)invalidateTouchBar { - (void)invalidateTouchBar {
DCHECK([window_ respondsToSelector:@selector(setTouchBar:)]); DCHECK([window_ respondsToSelector:@selector(setTouchBar:)]);
[window_ performSelector:@selector(setTouchBar:) withObject:nil]; [window_ performSelector:@selector(setTouchBar:) withObject:nil];
...@@ -118,15 +63,6 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge ...@@ -118,15 +63,6 @@ class API_AVAILABLE(macos(10.12.2)) WebContentsNotificationBridge
return [defaultTouchBar_ makeTouchBar]; return [defaultTouchBar_ makeTouchBar];
} }
- (void)updateWebContents:(content::WebContents*)contents {
[defaultTouchBar_ updateWebContents:contents];
[self invalidateTouchBar];
}
- (content::WebContents*)webContents {
return notificationBridge_->web_contents();
}
@end @end
@implementation BrowserWindowTouchBarController (ExposedForTesting) @implementation BrowserWindowTouchBarController (ExposedForTesting)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment