[Mac] Created Suggested Text Touch Bar

When text is selected in a textfield, the touch bar displays options
to replace the selected text. Clicking on one of the options will
replace the selected text with the selected option.

Currently, there are no functional unit tests for the suggested text
touch bar. As functionality is fleshed out, unit tests will be
created.

A new folder (chrome/browser/ui/cocoa/touchbar) was created to house all
files related to the Mac touch bar. The
CreditCardAutofillTouchBarController files and the
WebTextfieldTouchBarController files were moved to this new folder.
Dependencies and header guards were updated to reflect this change.

New files:
  - suggested_text_touch_bar_controller.h
  - suggested_text_touch_bar_controller.mm
  - suggested_text_touch_bar_controller_browsertest.mm
Moved files:
  - credit_card_autofill_touch_bar_controller.h
  - credit_card_autofill_touch_bar_controller.mm
  - credit_card_autofill_touch_bar_controller_unittest.mm
  - web_textfield_touch_bar_controller.h
  - web_textfield_touch_bar_controller.mm

BUILD files were updated to accomodate the change in location for the
moved files and to include the new files.

WebContentsImpl, WebContentsDelegate, and WebContentsObserver were
modified so that the SuggestedTextTouchBarController can observe
TextInputManager for a change in text selection.

A feature flag SuggestedTextTouchBar was created and added to
chrome://about so that the new touch bar is disabled by default and
can be enabled via command line or chrome://flags with the flag
#enable-suggested-text-touch-bar.

This CL includes the following browser tests for
SuggestedTextTouchBarController:
  - SetTextTest
  - TouchBarTest
  - TextSelectionChangedTest

Bug: 717553
Change-Id: Icdd796c5d7ff6f67598b58c6c6c8c17a6a85d004
Reviewed-on: https://chromium-review.googlesource.com/1069495
Commit-Queue: Tessa Nijssen <tnijssen@google.com>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Sarah Chan <spqchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565799}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8237a96..b9e378f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3898,6 +3898,13 @@
      flag_descriptions::kEnableBlinkHeapIncrementalMarkingDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kBlinkHeapIncrementalMarking)},
 
+#if defined(OS_MACOSX)
+    {"enable-suggested-text-touch-bar",
+     flag_descriptions::kSuggestedTextTouchBarName,
+     flag_descriptions::kSuggestedTextTouchBarDescription, kOsMac,
+     FEATURE_VALUE_TYPE(features::kSuggestedTextTouchBar)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8f1f86e..ea155bf 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2664,6 +2664,10 @@
 const char kMacViewsTaskManagerDescription[] =
     "Controls whether to use the Toolkit-Views based Task Manager.";
 
+const char kSuggestedTextTouchBarName[] = "Suggested Text Touch Bar";
+const char kSuggestedTextTouchBarDescription[] =
+    "Enable suggested text touch bar for textfields.";
+
 const char kTabDetachingInFullscreenName[] =
     "Allow tab detaching in fullscreen";
 const char kTabDetachingInFullscreenDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1791ba6..1a673e6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1627,6 +1627,9 @@
 extern const char kMacViewsTaskManagerName[];
 extern const char kMacViewsTaskManagerDescription[];
 
+extern const char kSuggestedTextTouchBarName[];
+extern const char kSuggestedTextTouchBarDescription[];
+
 extern const char kTabDetachingInFullscreenName[];
 extern const char kTabDetachingInFullscreenDescription[];
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6ee6204..a9da651 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -73,8 +73,6 @@
       "cocoa/autofill/autofill_popup_view_cocoa.mm",
       "cocoa/autofill/autofill_tooltip_controller.h",
       "cocoa/autofill/autofill_tooltip_controller.mm",
-      "cocoa/autofill/credit_card_autofill_touch_bar_controller.h",
-      "cocoa/autofill/credit_card_autofill_touch_bar_controller.mm",
       "cocoa/autofill/save_card_bubble_view_views.h",
       "cocoa/autofill/save_card_bubble_view_views.mm",
       "cocoa/background_gradient_view.h",
@@ -537,6 +535,12 @@
       "cocoa/toolbar/toolbar_controller.mm",
       "cocoa/toolbar/toolbar_view_cocoa.h",
       "cocoa/toolbar/toolbar_view_cocoa.mm",
+      "cocoa/touchbar/credit_card_autofill_touch_bar_controller.h",
+      "cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm",
+      "cocoa/touchbar/suggested_text_touch_bar_controller.h",
+      "cocoa/touchbar/suggested_text_touch_bar_controller.mm",
+      "cocoa/touchbar/web_textfield_touch_bar_controller.h",
+      "cocoa/touchbar/web_textfield_touch_bar_controller.mm",
       "cocoa/translate/translate_bubble_bridge_views.h",
       "cocoa/translate/translate_bubble_bridge_views.mm",
       "cocoa/translate/translate_bubble_controller.h",
@@ -550,8 +554,6 @@
       "cocoa/view_resizer.h",
       "cocoa/web_contents_modal_dialog_manager_views_mac.h",
       "cocoa/web_contents_modal_dialog_manager_views_mac.mm",
-      "cocoa/web_textfield_touch_bar_controller.h",
-      "cocoa/web_textfield_touch_bar_controller.mm",
 
       # TODO(estade): this class should be deleted in favor of a combobox model.
       # See crbug.com/590850
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
index f493488..3720ffb 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
@@ -7,7 +7,7 @@
 #include "base/mac/availability.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
-#import "chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
 #include "components/autofill/core/browser/autofill_popup_delegate.h"
 #include "components/autofill/core/browser/popup_item_ids.h"
 
diff --git a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
index e04c167..2067c7a 100644
--- a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
@@ -18,7 +18,7 @@
 #include "chrome/browser/ui/cocoa/fullscreen_placeholder_view.h"
 #include "chrome/browser/ui/cocoa/separate_fullscreen_window.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
-#import "chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h
similarity index 83%
rename from chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h
rename to chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h
index df106a2..fd43b29 100644
--- a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_COCOA_AUTOFILL_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_AUTOFILL_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_COCOA_TOUCHBAR_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
+#define CHROME_BROWSER_UI_COCOA_TOUCHBAR_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -35,4 +35,4 @@
 
 @end
 
-#endif  // CHROME_BROWSER_UI_COCOA_AUTOFILL_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
\ No newline at end of file
+#endif  // CHROME_BROWSER_UI_COCOA_TOUCHBAR_CREDIT_CARD_AUTOFILL_TOUCH_BAR_CONTROLLER_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.mm b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
similarity index 98%
rename from chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.mm
rename to chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
index 92bd35c..be7f8fe 100644
--- a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.mm
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h"
 
 #import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller_unittest.mm b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
similarity index 98%
rename from chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller_unittest.mm
rename to chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
index 7335f1b..343297e 100644
--- a/chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
@@ -11,8 +11,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
 #include "chrome/browser/ui/autofill/autofill_popup_layout_model.h"
-#import "chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h"
 #import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h"
 #include "components/autofill/core/browser/suggestion.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h
new file mode 100644
index 0000000..b1bb03b
--- /dev/null
+++ b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h
@@ -0,0 +1,65 @@
+// Copyright 2018 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 CHROME_BROWSER_UI_COCOA_TOUCHBAR_SUGGESTED_TEXT_TOUCH_BAR_CONTROLLER_H_
+#define CHROME_BROWSER_UI_COCOA_TOUCHBAR_SUGGESTED_TEXT_TOUCH_BAR_CONTROLLER_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include <memory>
+
+#import "base/mac/scoped_nsobject.h"
+#include "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
+#import "ui/base/cocoa/touch_bar_forward_declarations.h"
+
+@class WebTextfieldTouchBarController;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+@interface SuggestedTextTouchBarController
+    : NSObject<NSTouchBarDelegate, NSCandidateListTouchBarItemDelegate>
+
+- (instancetype)initWithWebContents:(content::WebContents*)webContents
+                         controller:(WebTextfieldTouchBarController*)controller;
+
+- (void)initObserver;
+
+- (NSTouchBar*)makeTouchBar API_AVAILABLE(macos(10.12.2));
+
+- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
+      makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
+    API_AVAILABLE(macos(10.12.2));
+
+// Creates a NSCandidateListTouchBarItem that contains text suggestions
+// based on the current text selection.
+- (NSCandidateListTouchBarItem*)createCandidateListItem
+    API_AVAILABLE(macos(10.12.2));
+
+- (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem*)anItem
+     endSelectingCandidateAtIndex:(NSInteger)index
+    API_AVAILABLE(macos(10.12.2));
+
+- (void)webContentsTextSelectionChanged:(NSString*)text;
+
+- (void)requestSuggestionsForText:(NSString*)text;
+
+- (void)replaceSelectedText:(NSString*)text;
+
+@end
+
+@interface SuggestedTextTouchBarController (ExposedForTesting)
+
+- (void)setText:(NSString*)text;
+- (NSString*)text;
+- (void)setSuggestions:(NSArray*)suggestions;
+- (NSArray*)suggestions;
+- (WebTextfieldTouchBarController*)controller;
+- (void)setWebContents:(content::WebContents*)webContents;
+- (content::WebContents*)webContents;
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_TOUCHBAR_SUGGESTED_TEXT_TOUCH_BAR_CONTROLLER_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.mm b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.mm
new file mode 100644
index 0000000..bd3cf65
--- /dev/null
+++ b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.mm
@@ -0,0 +1,179 @@
+// Copyright 2018 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 "chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#import "ui/base/cocoa/touch_bar_util.h"
+
+namespace {
+// Touch bar identifier.
+NSString* const kSuggestedTextTouchBarId = @"suggested-text";
+
+// Touch bar item identifiers.
+NSString* const kSuggestedTextItemsTouchId = @"SUGGESTED-TEXT-ITEMS";
+}  // namespace
+
+namespace text_observer {
+class WebContentsTextObserver : public content::WebContentsObserver {
+ public:
+  WebContentsTextObserver(content::WebContents* web_contents,
+                          SuggestedTextTouchBarController* owner)
+      : WebContentsObserver(web_contents), owner_(owner) {}
+
+  void DidChangeTextSelection(base::string16 new_selected_text) override {
+    [owner_ webContentsTextSelectionChanged:base::SysUTF16ToNSString(
+                                                new_selected_text)];
+  }
+
+ private:
+  SuggestedTextTouchBarController* owner_;  // weak
+};
+}  // namespace text_observer
+
+@interface SuggestedTextTouchBarController () {
+  // An observer for text selection changes.
+  std::unique_ptr<text_observer::WebContentsTextObserver> observer_;
+
+  // The WebContents in which text is being selected and replaced.
+  content::WebContents* webContents_;  // weak
+
+  // The WebTextfieldTouchBarController that invalidates the touch bar.
+  WebTextfieldTouchBarController* controller_;  // weak
+
+  // The text on which suggestions are based.
+  base::scoped_nsobject<NSString> text_;
+
+  // A list of NSTextCheckingResults containing text suggestions for |text_|.
+  base::scoped_nsobject<NSArray> suggestions_;
+}
+@end
+
+@implementation SuggestedTextTouchBarController
+
+- (instancetype)initWithWebContents:(content::WebContents*)webContents
+                         controller:
+                             (WebTextfieldTouchBarController*)controller {
+  if ((self = [super init])) {
+    webContents_ = webContents;
+    controller_ = controller;
+  }
+
+  return self;
+}
+
+- (void)initObserver {
+  observer_.reset(
+      new text_observer::WebContentsTextObserver(webContents_, self));
+}
+
+- (NSTouchBar*)makeTouchBar {
+  if (!webContents_->IsFocusedElementEditable() || ![text_ length])
+    return nil;
+
+  base::scoped_nsobject<NSTouchBar> touchBar([[ui::NSTouchBar() alloc] init]);
+  [touchBar
+      setCustomizationIdentifier:ui::GetTouchBarId(kSuggestedTextTouchBarId)];
+  [touchBar setDelegate:self];
+
+  [touchBar setDefaultItemIdentifiers:@[ kSuggestedTextItemsTouchId ]];
+  return touchBar.autorelease();
+}
+
+- (NSTouchBarItem*)touchBar:(NSTouchBar*)touchBar
+      makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
+    API_AVAILABLE(macos(10.12.2)) {
+  if (![identifier hasSuffix:kSuggestedTextItemsTouchId])
+    return nil;
+
+  return [self createCandidateListItem];
+}
+
+- (NSCandidateListTouchBarItem*)createCandidateListItem
+    API_AVAILABLE(macos(10.12.2)) {
+  NSCandidateListTouchBarItem* candidateListItem =
+      [[NSCandidateListTouchBarItem alloc]
+          initWithIdentifier:kSuggestedTextItemsTouchId];
+  candidateListItem.delegate = self;
+  [candidateListItem setCandidates:suggestions_
+                  forSelectedRange:NSMakeRange(0, [text_ length])
+                          inString:text_];
+  return candidateListItem;
+}
+
+- (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem*)anItem
+     endSelectingCandidateAtIndex:(NSInteger)index
+    API_AVAILABLE(macos(10.12.2)) {
+  if (index == NSNotFound)
+    return;
+
+  NSTextCheckingResult* selectedResult = anItem.candidates[index];
+  [self replaceSelectedText:[selectedResult replacementString]];
+}
+
+- (void)webContentsTextSelectionChanged:(NSString*)text {
+  if (webContents_->IsFocusedElementEditable()) {
+    [self setText:text];
+    [self requestSuggestionsForText:text];
+  } else {
+    [controller_ invalidateTouchBar];
+  }
+}
+
+- (void)requestSuggestionsForText:(NSString*)text
+    API_AVAILABLE(macos(10.12.2)) {
+  NSSpellChecker* spell_checker = [NSSpellChecker sharedSpellChecker];
+  [spell_checker
+      requestCandidatesForSelectedRange:NSMakeRange(0, [text length])
+                               inString:text
+                                  types:NSTextCheckingAllSystemTypes
+                                options:nil
+                 inSpellDocumentWithTag:0
+                      completionHandler:^(
+                          NSInteger sequenceNumber,
+                          NSArray<NSTextCheckingResult*>* candidates) {
+                        suggestions_.reset([candidates copy]);
+                        [controller_ invalidateTouchBar];
+                      }];
+}
+
+- (void)replaceSelectedText:(NSString*)text {
+  // Strip the string of leading and trailing whitespace for replacement.
+  text = [text
+      stringByTrimmingCharactersInSet:[NSCharacterSet
+                                          whitespaceAndNewlineCharacterSet]];
+  webContents_->Replace(base::SysNSStringToUTF16(text));
+}
+
+- (void)setText:(NSString*)text {
+  text_.reset([text copy]);
+}
+
+- (NSString*)text {
+  return text_;
+}
+
+- (void)setSuggestions:(NSArray*)suggestions {
+  suggestions_.reset([suggestions copy]);
+}
+
+- (NSArray*)suggestions {
+  return suggestions_;
+}
+
+- (WebTextfieldTouchBarController*)controller {
+  return controller_;
+}
+
+- (void)setWebContents:(content::WebContents*)webContents {
+  webContents_ = webContents;
+}
+
+- (content::WebContents*)webContents {
+  return webContents_;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller_browsertest.mm b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller_browsertest.mm
new file mode 100644
index 0000000..1cfdeeb
--- /dev/null
+++ b/chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller_browsertest.mm
@@ -0,0 +1,193 @@
+// Copyright 2018 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 <Cocoa/Cocoa.h>
+
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/browser.h"
+#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h"
+#include "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "ui/base/cocoa/touch_bar_util.h"
+
+@interface MockWebTextfieldTouchBarController : WebTextfieldTouchBarController {
+  // Counter for the number of times invalidateTouchBar is called.
+  int numInvalidations_;
+}
+- (int)numInvalidations;
+
+- (void)resetNumInvalidations;
+
+@end
+
+@implementation MockWebTextfieldTouchBarController
+
+- (void)invalidateTouchBar {
+  numInvalidations_++;
+}
+
+- (int)numInvalidations {
+  return numInvalidations_;
+}
+
+- (void)resetNumInvalidations {
+  numInvalidations_ = 0;
+}
+
+@end
+
+@interface MockSuggestedTextTouchBarController : SuggestedTextTouchBarController
+- (NSString*)firstSuggestion;
+@end
+
+@implementation MockSuggestedTextTouchBarController
+
+- (void)requestSuggestionsForText:(NSString*)text {
+  [self setSuggestions:@[ text ]];
+  [[self controller] invalidateTouchBar];
+}
+
+- (NSString*)firstSuggestion {
+  return [self suggestions][0];
+}
+
+@end
+
+namespace {
+
+NSString* const kSuggestedTextTouchBarId = @"suggested-text";
+NSString* const kText = @"text";
+NSString* const kEmptyText = @"";
+
+class SuggestedTextTouchBarControllerBrowserTest : public InProcessBrowserTest {
+ public:
+  void SetUp() override {
+    InProcessBrowserTest::SetUp();
+    feature_list.InitAndEnableFeature(features::kSuggestedTextTouchBar);
+  }
+
+  void SetUpOnMainThread() override {
+    web_textfield_controller_.reset(
+        [[MockWebTextfieldTouchBarController alloc] init]);
+    [web_textfield_controller_ resetNumInvalidations];
+  }
+
+  MockSuggestedTextTouchBarController* focused_touch_bar_controller() const {
+    ui_test_utils::NavigateToURL(
+        browser(),
+        GURL("data:text/html;charset=utf-8,<input type=\"text\" autofocus>"));
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return [[MockSuggestedTextTouchBarController alloc]
+        initWithWebContents:web_contents
+                 controller:web_textfield_controller_];
+  }
+
+  MockSuggestedTextTouchBarController* unfocused_touch_bar_controller() const {
+    ui_test_utils::NavigateToURL(
+        browser(), GURL("data:text/html;charset=utf-8,<input type=\"text\">"));
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return [[MockSuggestedTextTouchBarController alloc]
+        initWithWebContents:web_contents
+                 controller:web_textfield_controller_];
+  }
+
+  MockWebTextfieldTouchBarController* web_textfield_controller() const {
+    return web_textfield_controller_;
+  }
+
+ private:
+  base::scoped_nsobject<MockWebTextfieldTouchBarController>
+      web_textfield_controller_;
+  base::test::ScopedFeatureList feature_list;
+};
+
+// Tests that text is saved properly, regardless of whether a textfield is
+// focused.
+IN_PROC_BROWSER_TEST_F(SuggestedTextTouchBarControllerBrowserTest,
+                       SetTextTest) {
+  SuggestedTextTouchBarController* touch_bar_controller;
+
+  touch_bar_controller = unfocused_touch_bar_controller();
+  ASSERT_FALSE([touch_bar_controller webContents]->IsFocusedElementEditable());
+  [touch_bar_controller setText:kEmptyText];
+  EXPECT_EQ(kEmptyText, [touch_bar_controller text]);
+
+  touch_bar_controller = focused_touch_bar_controller();
+  ASSERT_TRUE([touch_bar_controller webContents]->IsFocusedElementEditable());
+  [touch_bar_controller setText:kText];
+  EXPECT_EQ(kText, [touch_bar_controller text]);
+}
+
+// Tests to check if the touch bar shows up properly.
+IN_PROC_BROWSER_TEST_F(SuggestedTextTouchBarControllerBrowserTest,
+                       TouchBarTest) {
+  if (@available(macOS 10.12.2, *)) {
+    SuggestedTextTouchBarController* touch_bar_controller;
+
+    // Touch bar shouldn't appear if not in textfield.
+    touch_bar_controller = unfocused_touch_bar_controller();
+    ASSERT_FALSE(
+        [touch_bar_controller webContents]->IsFocusedElementEditable());
+    EXPECT_FALSE([touch_bar_controller makeTouchBar]);
+
+    // Touch bar shouldn't appear if there is no text to make suggestions.
+    touch_bar_controller = focused_touch_bar_controller();
+    [touch_bar_controller setText:kEmptyText];
+    ASSERT_TRUE([touch_bar_controller webContents]->IsFocusedElementEditable());
+    ASSERT_EQ(kEmptyText, [touch_bar_controller text]);
+    EXPECT_FALSE([touch_bar_controller makeTouchBar]);
+
+    // Touch bar should appear if in textfield and text exists for suggestions.
+    [touch_bar_controller setText:kText];
+    ASSERT_TRUE([touch_bar_controller webContents]->IsFocusedElementEditable());
+    ASSERT_EQ(kText, [touch_bar_controller text]);
+    NSTouchBar* touch_bar = [touch_bar_controller makeTouchBar];
+    EXPECT_TRUE(touch_bar);
+    EXPECT_TRUE([[touch_bar customizationIdentifier]
+        isEqual:ui::GetTouchBarId(kSuggestedTextTouchBarId)]);
+  }
+}
+
+// Tests that a change in text selection is handled properly.
+IN_PROC_BROWSER_TEST_F(SuggestedTextTouchBarControllerBrowserTest,
+                       TextSelectionChangedTest) {
+  MockSuggestedTextTouchBarController* touch_bar_controller;
+
+  // If not in a textfield,
+  //  1. New text selection should not be saved.
+  //  2. Touch bar should be invalidated.
+  touch_bar_controller = unfocused_touch_bar_controller();
+  [touch_bar_controller setText:kEmptyText];
+  ASSERT_FALSE([touch_bar_controller webContents]->IsFocusedElementEditable());
+  ASSERT_EQ(kEmptyText, [touch_bar_controller text]);
+  [touch_bar_controller webContentsTextSelectionChanged:kText];
+  EXPECT_EQ(kEmptyText, [touch_bar_controller text]);
+  EXPECT_EQ(1, [web_textfield_controller() numInvalidations]);
+
+  // If in a textfield,
+  //   1. New text selection should be saved.
+  //   2. Suggestions should be generated based on text selection.
+  //   3. Touch bar should be invalidated.
+  touch_bar_controller = focused_touch_bar_controller();
+  [touch_bar_controller setText:kEmptyText];
+  ASSERT_TRUE([touch_bar_controller webContents]->IsFocusedElementEditable());
+  ASSERT_EQ(kEmptyText, [touch_bar_controller text]);
+  [touch_bar_controller webContentsTextSelectionChanged:kText];
+  EXPECT_EQ(kText, [touch_bar_controller text]);
+  EXPECT_EQ(kText, [touch_bar_controller firstSuggestion]);
+  EXPECT_EQ(2, [web_textfield_controller() numInvalidations]);
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h
similarity index 70%
rename from chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h
rename to chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h
index 8ecccdb..5237569 100644
--- a/chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h
+++ b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_COCOA_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
-#define CHROME_BROWSER_UI_COCOA_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_COCOA_TOUCHBAR_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
+#define CHROME_BROWSER_UI_COCOA_TOUCHBAR_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
 
 #import <Cocoa/Cocoa.h>
 
@@ -13,6 +13,7 @@
 #import "ui/base/cocoa/touch_bar_forward_declarations.h"
 
 @class CreditCardAutofillTouchBarController;
+@class SuggestedTextTouchBarController;
 @class TabContentsController;
 
 namespace autofill {
@@ -22,9 +23,11 @@
 // Provides a touch bar for the textfields in the WebContents. This class
 // implements the NSTouchBarDelegate and handles the items in the touch bar.
 @interface WebTextfieldTouchBarController : NSObject<NSTouchBarDelegate> {
-  TabContentsController* owner_;       // weak.
+  TabContentsController* owner_;  // weak.
   base::scoped_nsobject<CreditCardAutofillTouchBarController>
-      autofillTouchBarController_;  // weak.
+      autofillTouchBarController_;
+  base::scoped_nsobject<SuggestedTextTouchBarController>
+      suggestedTextTouchBarController_;
 }
 
 // Designated initializer.
@@ -42,4 +45,4 @@
 
 @end
 
-#endif  // CHROME_BROWSER_UI_COCOA_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_COCOA_TOUCHBAR_WEB_TEXTFIELD_TOUCH_BAR_CONTROLLER_H_
diff --git a/chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.mm b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
similarity index 62%
rename from chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.mm
rename to chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
index aeb86e2..b421dcb 100644
--- a/chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.mm
+++ b/chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.mm
@@ -2,20 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/web_textfield_touch_bar_controller.h"
 
+#include "base/debug/stack_trace.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/mac/sdk_forward_declarations.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
-#import "chrome/browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller.h"
 #import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.h"
+#import "chrome/browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller.h"
+#include "chrome/common/chrome_features.h"
+#include "content/public/browser/web_contents.h"
 #import "ui/base/cocoa/touch_bar_util.h"
 
 @implementation WebTextfieldTouchBarController
 
 - (instancetype)initWithTabContentsController:(TabContentsController*)owner {
-  if ((self = [self init])) {
+  if ((self = [super init])) {
     owner_ = owner;
+
+    suggestedTextTouchBarController_.reset(
+        [[SuggestedTextTouchBarController alloc]
+            initWithWebContents:[owner_ webContents]
+                     controller:self]);
+    [suggestedTextTouchBarController_ initObserver];
   }
 
   return self;
@@ -39,6 +49,10 @@
   [self invalidateTouchBar];
 }
 
+bool IsSuggestedTextTouchBarEnabled() {
+  return base::FeatureList::IsEnabled(features::kSuggestedTextTouchBar);
+}
+
 - (void)invalidateTouchBar {
   if ([owner_ respondsToSelector:@selector(setTouchBar:)])
     [owner_ performSelector:@selector(setTouchBar:) withObject:nil];
@@ -48,6 +62,9 @@
   if (autofillTouchBarController_)
     return [autofillTouchBarController_ makeTouchBar];
 
+  if (IsSuggestedTextTouchBarEnabled())
+    return [suggestedTextTouchBarController_ makeTouchBar];
+
   return nil;
 }
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 6eefcf7..8fcfbb81 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -40,6 +40,10 @@
 // Use the Toolkit-Views Task Manager window.
 const base::Feature kViewsTaskManager{"ViewsTaskManager",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables the suggested text touch bar for autocomplete in textfields.
+const base::Feature kSuggestedTextTouchBar{"SuggestedTextTouchBar",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_MACOSX)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 890050c..b64a4a2 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -293,6 +293,10 @@
 
 extern const base::Feature kSoundContentSetting;
 
+#if defined(OS_MACOSX)
+extern const base::Feature kSuggestedTextTouchBar;
+#endif
+
 extern const base::Feature kSupervisedUserCommittedInterstitials;
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 80a86d4..79d6d75 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1906,6 +1906,7 @@
         "../browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm",
         "../browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa_browsertest.mm",
         "../browser/ui/cocoa/ssl_client_certificate_selector_cocoa_browsertest.mm",
+        "../browser/ui/cocoa/touchbar/suggested_text_touch_bar_controller_browsertest.mm",
         "../browser/ui/cocoa/view_id_util_browsertest.mm",
         "../browser/ui/cocoa/web_contents_modal_dialog_manager_views_mac_browsertest.mm",
 
@@ -4009,7 +4010,6 @@
         "../browser/ui/cocoa/app_menu/menu_tracked_root_view_unittest.mm",
         "../browser/ui/cocoa/autofill/autofill_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/autofill/autofill_tooltip_controller_unittest.mm",
-        "../browser/ui/cocoa/autofill/credit_card_autofill_touch_bar_controller_unittest.mm",
         "../browser/ui/cocoa/background_gradient_view_unittest.mm",
         "../browser/ui/cocoa/base_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/bookmarks/bookmark_all_tabs_controller_unittest.mm",
@@ -4154,6 +4154,7 @@
         "../browser/ui/cocoa/toolbar/toolbar_button_unittest.mm",
         "../browser/ui/cocoa/toolbar/toolbar_controller_unittest.mm",
         "../browser/ui/cocoa/toolbar/toolbar_view_unittest.mm",
+        "../browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm",
         "../browser/ui/cocoa/translate/translate_bubble_controller_unittest.mm",
         "../browser/ui/cocoa/url_drop_target_unittest.mm",
         "../browser/ui/cocoa/vertical_gradient_view_unittest.mm",
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 59f3a50..e4c5b88 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -639,6 +639,9 @@
       GetOuterWebContents()->OnAudioStateChanged();
   }
 
+  if (GetTextInputManager())
+    GetTextInputManager()->RemoveObserver(this);
+
   for (auto& observer : observers_)
     observer.FrameDeleted(root->current_frame_host());
 
@@ -1107,6 +1110,18 @@
   }
 }
 
+void WebContentsImpl::OnTextSelectionChanged(
+    TextInputManager* text_input_manager,
+    RenderWidgetHostViewBase* updated_view) {
+  if (delegate_) {
+    base::string16 text;
+    if (text_input_manager->GetTextSelection())
+      text = text_input_manager->GetTextSelection()->selected_text();
+    for (auto& observer : observers_)
+      observer.DidChangeTextSelection(text);
+  }
+}
+
 #if !defined(OS_ANDROID)
 void WebContentsImpl::SetTemporaryZoomLevel(double level,
                                             bool temporary_zoom_enabled) {
@@ -1931,6 +1946,10 @@
   // happens after RenderFrameHostManager::Init.
   NotifySwappedFromRenderManager(
       nullptr, GetRenderManager()->current_frame_host(), true);
+
+  // Start observing the text input manager for text selection changes.
+  if (GetTextInputManager())
+    GetTextInputManager()->AddObserver(this);
 }
 
 void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 944039f..c8ee64b 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -38,6 +38,7 @@
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/wake_lock/wake_lock_context_host.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/color_chooser.h"
@@ -133,7 +134,8 @@
                                        public blink::mojom::ColorChooserFactory,
                                        public NotificationObserver,
                                        public NavigationControllerDelegate,
-                                       public NavigatorDelegate {
+                                       public NavigatorDelegate,
+                                       public TextInputManager::Observer {
  public:
   class FriendWrapper;
 
@@ -1368,6 +1370,10 @@
                                              AXTreeSnapshotCombiner* combiner,
                                              ui::AXMode ax_mode);
 
+  // TextInputManager::Observer implementation.
+  void OnTextSelectionChanged(TextInputManager* text_input_manager,
+                              RenderWidgetHostViewBase* updated_view) override;
+
   // Data for core operation ---------------------------------------------------
 
   // Delegate for notifying our owner about stuff. Not owned by us.
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index af9b116..5eb25cd 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -429,6 +429,9 @@
   // Invoked when theme color is changed to |theme_color|.
   virtual void DidChangeThemeColor(SkColor theme_color) {}
 
+  // Invoked when text selection is changed.
+  virtual void DidChangeTextSelection(base::string16 new_selected_text) {}
+
   // Invoked when media is playing or paused.  |id| is unique per player and per
   // RenderFrameHost.  There may be multiple players within a RenderFrameHost
   // and subsequently within a WebContents.  MediaStartedPlaying() will always
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c0ceff7..831806b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27274,6 +27274,7 @@
   <int value="-1039555838" label="GamepadExtensions:enabled"/>
   <int value="-1034344165" label="V8NoTurbo:disabled"/>
   <int value="-1033738911" label="enable-mac-views-dialogs"/>
+  <int value="-1032031735" label="SuggestedTextTouchBar:enabled"/>
   <int value="-1031350684" label="PdfIsolation:disabled"/>
   <int value="-1029920490" label="IdleTimeSpellChecking:enabled"/>
   <int value="-1028733699" label="MacViewsWebUIDialogs:disabled"/>
@@ -28064,6 +28065,7 @@
       label="enable-fullscreen-handwriting-virtual-keyboard:enabled"/>
   <int value="773743944" label="show-android-files-in-files-app"/>
   <int value="773919225" label="disable-office-editing-component-extension"/>
+  <int value="774079453" label="SuggestedTextTouchBar:disabled"/>
   <int value="777667507" label="DesktopPWAsLinkCapturing:enabled"/>
   <int value="779086132" label="enable-data-reduction-proxy-alt"/>
   <int value="779849093" label="OfflinePagesCTSuppressNotifications:disabled"/>