MacViews: TouchBar integration for toolkit-views dialogs.

Native Mac NSAlert dialogs show the dialog buttons on the TouchBar, we
want similar behaviour for Chrome dialogs.

If a Widget's delegate implements views::DialogDelegate, add buttons to
the touchbar for the "OK" and "Cancel" buttons. Note the labels might
not necessarily be labeled "OK" and "Cancel".

Nearly all dialogs and bubbles work correctly with the TouchBar this
way. One exception is the Add Bookmark bubble -
LocationBarBubbleDelegateView::GetDialogButtons() returns
ui::DIALOG_BUTTON_NONE. That makes sense for the ZoomBubble.
SaveCardBubbleViews overrides again to return both buttons and works.
The translate bubble has "TODO: This should be using GetDialogButtons."

Screenshot: http://crbug.com/660126#c13

BUG=661581

Review-Url: https://codereview.chromium.org/2469213002
Cr-Commit-Position: refs/heads/master@{#430209}
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index f77a425..5e1e285 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -289,6 +289,20 @@
 
 #endif  // MAC_OS_X_VERSION_10_12
 
+// Once Chrome no longer supports OSX 10.12.0, everything within this
+// preprocessor block can be removed.
+#if !defined(MAC_OS_X_VERSION_10_12_1) || \
+    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
+
+@interface NSButton (SierraPointOneSDK)
+@property(copy) NSColor* bezelColor;
++ (instancetype)buttonWithTitle:(NSString*)title
+                         target:(id)target
+                         action:(SEL)action;
+@end
+
+#endif  // MAC_OS_X_VERSION_10_12_1
+
 // ----------------------------------------------------------------------------
 // The symbol for kCWSSIDDidChangeNotification is available in the
 // CoreWLAN.framework for OSX versions 10.6 through 10.10. The symbol is not
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 1cc0c01..e50e4029 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -130,6 +130,7 @@
     "cocoa/three_part_image.mm",
     "cocoa/tool_tip_base_view.h",
     "cocoa/tool_tip_base_view.mm",
+    "cocoa/touch_bar_forward_declarations.h",
     "cocoa/tracking_area.h",
     "cocoa/tracking_area.mm",
     "cocoa/underlay_opengl_hosting_window.h",
diff --git a/ui/base/cocoa/touch_bar_forward_declarations.h b/ui/base/cocoa/touch_bar_forward_declarations.h
new file mode 100644
index 0000000..54c68ae5
--- /dev/null
+++ b/ui/base/cocoa/touch_bar_forward_declarations.h
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_
+#define UI_BASE_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_
+
+// Once Chrome no longer supports OSX 10.12.0, this file can be deleted.
+
+#import <Foundation/Foundation.h>
+
+#if !defined(MAC_OS_X_VERSION_10_12_1)
+
+// The TouchBar classes do not exist at all without the 10.12.1 SDK. When
+// compiling with older SDKs, pretend they are NSObject and add categories to
+// NSObject to expose the methods.
+// To alloc one of these classes, use -[NSClassFromString(@"..") alloc].
+
+// Incomplete. Add more as necessary.
+
+typedef NSObject NSCustomTouchBarItem;
+typedef NSObject NSGroupTouchBarItem;
+typedef NSObject NSTouchBar;
+typedef NSObject NSTouchBarItem;
+typedef NSString* NSTouchBarItemIdentifier;
+
+@protocol NSTouchBarDelegate<NSObject>
+@end
+
+@interface NSObject (FakeNSCustomTouchBarItem)
+@property(readwrite, strong) NSView* view;
+@end
+
+@interface NSObject (FakeNSGroupTouchBarItem)
++ (NSGroupTouchBarItem*)groupItemWithIdentifier:
+                            (NSTouchBarItemIdentifier)identifier
+                                          items:(NSArray*)items;
+@end
+
+@interface NSObject (FakeNSTouchBar)
+@property(copy) NSArray* defaultItemIdentifiers;
+@property(copy) NSTouchBarItemIdentifier principalItemIdentifier;
+@property(weak) id<NSTouchBarDelegate> delegate;
+@end
+
+#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1
+
+// When compiling against the 10.12.1 SDK or later, just provide forward
+// declarations to suppress the partial availability warnings.
+
+@class NSCustomTouchBarItem;
+@class NSGroupTouchBarItem;
+@class NSTouchBar;
+@protocol NSTouchBarDelegate;
+@class NSTouchBarItem;
+
+#endif  // MAC_OS_X_VERSION_10_12_1
+
+#endif  // UI_BASE_COCOA_TOUCH_BAR_FORWARD_DECLARATIONS_H_
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index e0a734f7..0f989ea 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -62,6 +62,7 @@
     "button_drag_utils.h",
     "cocoa/bridged_content_view.h",
     "cocoa/bridged_content_view.mm",
+    "cocoa/bridged_content_view_touch_bar.mm",
     "cocoa/bridged_native_widget.h",
     "cocoa/bridged_native_widget.mm",
     "cocoa/bridged_native_widget_owner.h",
diff --git a/ui/views/cocoa/bridged_content_view_touch_bar.mm b/ui/views/cocoa/bridged_content_view_touch_bar.mm
new file mode 100644
index 0000000..109f4cda
--- /dev/null
+++ b/ui/views/cocoa/bridged_content_view_touch_bar.mm
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/mac/scoped_nsobject.h"
+#import "base/mac/sdk_forward_declarations.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ui/base/cocoa/touch_bar_forward_declarations.h"
+#include "ui/base/models/dialog_model.h"
+#import "ui/views/cocoa/bridged_content_view.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/dialog_client_view.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace {
+
+NSString* const kTouchBarDialogButtonsGroupId =
+    @"com.google.chrome-DIALOG-BUTTONS-GROUP";
+NSString* const kTouchBarOKId = @"com.google.chrome-OK";
+NSString* const kTouchBarCancelId = @"com.google.chrome-CANCEL";
+
+}  // namespace
+
+@interface BridgedContentView (TouchBarAdditions)<NSTouchBarDelegate>
+- (void)touchBarButtonAction:(id)sender;
+@end
+
+@implementation BridgedContentView (TouchBarAdditions)
+
+- (void)touchBarButtonAction:(id)sender {
+  if (!hostedView_)
+    return;
+
+  views::DialogDelegate* dialog =
+      hostedView_->GetWidget()->widget_delegate()->AsDialogDelegate();
+  DCHECK(dialog);
+  views::DialogClientView* client = dialog->GetDialogClientView();
+
+  if ([sender tag] == ui::DIALOG_BUTTON_OK) {
+    client->AcceptWindow();
+    return;
+  }
+
+  DCHECK_EQ([sender tag], ui::DIALOG_BUTTON_CANCEL);
+  client->CancelWindow();
+}
+
+// NSTouchBarDelegate protocol implementation.
+
+- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
+      makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
+  if (!hostedView_)
+    return nil;
+
+  if ([identifier isEqualToString:kTouchBarDialogButtonsGroupId]) {
+    NSMutableArray* items = [NSMutableArray arrayWithCapacity:2];
+    for (NSTouchBarItemIdentifier i in @[ kTouchBarCancelId, kTouchBarOKId ]) {
+      NSTouchBarItem* item = [self touchBar:touchBar makeItemForIdentifier:i];
+      if (item)
+        [items addObject:item];
+    }
+    if ([items count] == 0)
+      return nil;
+    return [NSGroupTouchBarItem groupItemWithIdentifier:identifier items:items];
+  }
+
+  ui::DialogButton type = ui::DIALOG_BUTTON_NONE;
+  if ([identifier isEqualToString:kTouchBarOKId])
+    type = ui::DIALOG_BUTTON_OK;
+  else if ([identifier isEqualToString:kTouchBarCancelId])
+    type = ui::DIALOG_BUTTON_CANCEL;
+  else
+    return nil;
+
+  ui::DialogModel* model =
+      hostedView_->GetWidget()->widget_delegate()->AsDialogDelegate();
+  if (!model || !(model->GetDialogButtons() & type))
+    return nil;
+
+  base::scoped_nsobject<NSCustomTouchBarItem> item([[NSClassFromString(
+      @"NSCustomTouchBarItem") alloc] initWithIdentifier:identifier]);
+  NSString* title = base::SysUTF16ToNSString(model->GetDialogButtonLabel(type));
+  NSButton* button =
+      [NSButton buttonWithTitle:title
+                         target:self
+                         action:@selector(touchBarButtonAction:)];
+  if (type == model->GetDefaultDialogButton()) {
+    // NSAlert uses a private NSButton subclass (_NSTouchBarGroupButton) with
+    // more bells and whistles. It doesn't use -setBezelColor: directly, but
+    // this gives an appearance matching the default _NSTouchBarGroupButton.
+    [button setBezelColor:[NSColor colorWithSRGBRed:0.168
+                                              green:0.51
+                                               blue:0.843
+                                              alpha:1.0]];
+  }
+  [button setEnabled:model->IsDialogButtonEnabled(type)];
+  [button setTag:type];
+  [item setView:button];
+  return item.autorelease();
+}
+
+// NSTouchBarProvider protocol implementation (via NSResponder category).
+
+- (NSTouchBar*)makeTouchBar {
+  if (!hostedView_)
+    return nil;
+
+  ui::DialogModel* model =
+      hostedView_->GetWidget()->widget_delegate()->AsDialogDelegate();
+  if (!model || !model->GetDialogButtons())
+    return nil;
+
+  base::scoped_nsobject<NSTouchBar> bar(
+      [[NSClassFromString(@"NSTouchBar") alloc] init]);
+  [bar setDelegate:self];
+
+  // Use a group rather than individual items so they can be centered together.
+  [bar setDefaultItemIdentifiers:@[ kTouchBarDialogButtonsGroupId ]];
+
+  // Setting the group as principal will center it in the TouchBar.
+  [bar setPrincipalItemIdentifier:kTouchBarDialogButtonsGroupId];
+  return bar.autorelease();
+}
+
+@end