Show a confirmation bubble after one-click sign in

BUG=117899
TEST=See bubble appear below wrench menu after accepting one-click signin and
pressing Start in dialog box.


Review URL: http://codereview.chromium.org/9814022

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@129024 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 52d7c200..1f69c6a 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -216,6 +216,11 @@
   // Shows the Chrome To Mobile bubble.
   virtual void ShowChromeToMobileBubble() = 0;
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  // Shows the one-click sign in bubble.
+  virtual void ShowOneClickSigninBubble() = 0;
+#endif
+
   // Whether or not the shelf view is visible.
   virtual bool IsDownloadShelfVisible() const = 0;
 
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index 8a813b5d..0323151 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -94,6 +94,9 @@
   virtual void ShowBookmarkBubble(const GURL& url,
                                   bool already_bookmarked) OVERRIDE;
   virtual void ShowChromeToMobileBubble() OVERRIDE;
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  virtual void ShowOneClickSigninBubble() OVERRIDE;
+#endif
   virtual bool IsDownloadShelfVisible() const OVERRIDE;
   virtual DownloadShelf* GetDownloadShelf() OVERRIDE;
   virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE;
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index ac724a0..0f15647 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -449,6 +449,12 @@
   NOTIMPLEMENTED();
 }
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+void BrowserWindowCocoa::ShowOneClickSigninBubble() {
+  NOTIMPLEMENTED();
+}
+#endif
+
 bool BrowserWindowCocoa::IsDownloadShelfVisible() const {
   return [controller_ isDownloadShelfVisible] != NO;
 }
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc
index 66d1b818..028921f 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.cc
+++ b/chrome/browser/ui/gtk/browser_window_gtk.cc
@@ -1078,6 +1078,14 @@
   toolbar_->GetLocationBarView()->ShowChromeToMobileBubble();
 }
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+void BrowserWindowGtk::ShowOneClickSigninBubble() {
+  // TODO(rogerta): will be implemented when the one-click feature is done on
+  // linux.
+  NOTIMPLEMENTED();
+}
+#endif
+
 bool BrowserWindowGtk::IsDownloadShelfVisible() const {
   return download_shelf_.get() && download_shelf_->IsShowing();
 }
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.h b/chrome/browser/ui/gtk/browser_window_gtk.h
index c8b2638..f9f2534 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.h
+++ b/chrome/browser/ui/gtk/browser_window_gtk.h
@@ -136,6 +136,9 @@
   virtual void ShowBookmarkBubble(const GURL& url,
                                   bool already_bookmarked) OVERRIDE;
   virtual void ShowChromeToMobileBubble() OVERRIDE;
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  virtual void ShowOneClickSigninBubble() OVERRIDE;
+#endif
   virtual bool IsDownloadShelfVisible() const OVERRIDE;
   virtual DownloadShelf* GetDownloadShelf() OVERRIDE;
   virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE;
diff --git a/chrome/browser/ui/panels/panel.cc b/chrome/browser/ui/panels/panel.cc
index 05a6776e..a2f828f 100644
--- a/chrome/browser/ui/panels/panel.cc
+++ b/chrome/browser/ui/panels/panel.cc
@@ -524,6 +524,12 @@
   NOTIMPLEMENTED();
 }
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+void Panel::ShowOneClickSigninBubble() {
+  NOTIMPLEMENTED();
+}
+#endif
+
 bool Panel::IsDownloadShelfVisible() const {
   return false;
 }
diff --git a/chrome/browser/ui/panels/panel.h b/chrome/browser/ui/panels/panel.h
index 3c3b2121..cec68bb 100644
--- a/chrome/browser/ui/panels/panel.h
+++ b/chrome/browser/ui/panels/panel.h
@@ -150,6 +150,9 @@
   virtual void ShowBookmarkBubble(
       const GURL& url, bool already_bookmarked) OVERRIDE;
   virtual void ShowChromeToMobileBubble() OVERRIDE;
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  virtual void ShowOneClickSigninBubble() OVERRIDE;
+#endif
   virtual bool IsDownloadShelfVisible() const OVERRIDE;
   virtual DownloadShelf* GetDownloadShelf() OVERRIDE;
   virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE;
diff --git a/chrome/browser/ui/sync/one_click_signin_helper.cc b/chrome/browser/ui/sync/one_click_signin_helper.cc
index 0a401cc3..b5329ba8 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper.cc
+++ b/chrome/browser/ui/sync/one_click_signin_helper.cc
@@ -17,6 +17,8 @@
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
 #include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/sync/one_click_signin_dialog.h"
 #include "chrome/browser/ui/sync/one_click_signin_histogram.h"
 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
@@ -65,8 +67,6 @@
   // Record the specified action in the histogram for one-click sign in.
   void RecordHistogramAction(int action);
 
-  Profile* profile_;
-
   // Information about the account that has just logged in.
   std::string session_index_;
   std::string email_;
@@ -84,13 +84,10 @@
     const std::string& email,
     const std::string& password)
     : ConfirmInfoBarDelegate(owner),
-      profile_(Profile::FromBrowserContext(
-          owner->web_contents()->GetBrowserContext())),
       session_index_(session_index),
       email_(email),
       password_(password),
       button_pressed_(false) {
-  DCHECK(profile_);
   RecordHistogramAction(one_click_signin::HISTOGRAM_SHOWN);
 }
 
@@ -127,15 +124,20 @@
 namespace {
 
 // Start syncing with the given user information.
-void StartSync(Profile* profile,
+void StartSync(content::WebContents* web_contents,
                const std::string& session_index,
                const std::string& email,
                const std::string& password,
                bool use_default_settings) {
   // The starter deletes itself once its done.
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
   ignore_result(
       new OneClickSigninSyncStarter(
           profile, session_index, email, password, use_default_settings));
+
+  Browser* browser = BrowserList::FindBrowserWithWebContents(web_contents);
+  browser->window()->ShowOneClickSigninBubble();
 }
 
 }  // namespace
@@ -145,7 +147,8 @@
   RecordHistogramAction(one_click_signin::HISTOGRAM_ACCEPTED);
   ShowOneClickSigninDialog(
       owner()->web_contents()->GetView()->GetTopLevelNativeWindow(),
-      base::Bind(&StartSync, profile_, session_index_, email_, password_));
+      base::Bind(&StartSync, owner()->web_contents(), session_index_, email_,
+                 password_));
   button_pressed_ = true;
   return true;
 }
@@ -269,14 +272,12 @@
 
   // TODO(rogerta): remove this #if once the dialog is fully implemented for
   // mac and linux.
-#if defined(ENABLE_ONE_CLICK_SIGNIN)
   // Save the email in the one-click signin manager.  The manager may
   // not exist if the contents is incognito or if the profile is already
   // connected to a Google account.
   OneClickSigninHelper* helper = wrapper->one_click_signin_helper();
   if (helper)
     helper->SaveSessionIndexAndEmail(session_index, email);
-#endif
 }
 
 void OneClickSigninHelper::DidNavigateAnyFrame(
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 1b31623..ca83140 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -121,6 +121,10 @@
 #include "chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h"
 #endif
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+#include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
+#endif
+
 #if !defined(OS_CHROMEOS) || defined(USE_AURA)
 #include "chrome/browser/ui/views/download/download_shelf_view.h"
 #endif
@@ -1114,6 +1118,12 @@
   GetLocationBarView()->ShowChromeToMobileBubble();
 }
 
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+void BrowserView::ShowOneClickSigninBubble() {
+  OneClickSigninBubbleView::ShowBubble(toolbar_->app_menu(), browser_.get());
+}
+#endif
+
 void BrowserView::SetDownloadShelfVisible(bool visible) {
   // This can be called from the superclass destructor, when it destroys our
   // child views. At that point, browser_ is already gone.
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 10104d4..aa744184 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -282,6 +282,9 @@
   virtual void ShowBookmarkBubble(const GURL& url,
                                   bool already_bookmarked) OVERRIDE;
   virtual void ShowChromeToMobileBubble() OVERRIDE;
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  virtual void ShowOneClickSigninBubble() OVERRIDE;
+#endif
   // TODO(beng): Not an override, move somewhere else.
   void SetDownloadShelfVisible(bool visible);
   virtual bool IsDownloadShelfVisible() const OVERRIDE;
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
new file mode 100644
index 0000000..9d358078
--- /dev/null
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2012 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.
+
+#include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/google/google_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/window.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/common/page_transition_types.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/controls/button/text_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/events/event.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+
+// Minimum width for the fields - they will push out the size of the bubble if
+// necessary. This should be big enough so that the field pushes the right side
+// of the bubble far enough so that the edit button's left edge is to the right
+// of the field's left edge.
+const int kMinimumFieldSize = 240;
+
+// BookmarkBubbleView ---------------------------------------------------------
+
+// static
+OneClickSigninBubbleView* OneClickSigninBubbleView::bubble_view_ = NULL;
+
+// static
+void OneClickSigninBubbleView::ShowBubble(views::View* anchor_view,
+                                          Browser* browser) {
+  if (IsShowing())
+    return;
+
+  bubble_view_ = new OneClickSigninBubbleView(anchor_view, browser);
+  browser::CreateViewsBubble(bubble_view_);
+  bubble_view_->Show();
+}
+
+// static
+bool OneClickSigninBubbleView::IsShowing() {
+  return bubble_view_ != NULL;
+}
+
+// static
+void OneClickSigninBubbleView::Hide() {
+  if (IsShowing())
+    bubble_view_->GetWidget()->Close();
+}
+
+OneClickSigninBubbleView::OneClickSigninBubbleView(views::View* anchor_view,
+                                                   Browser* browser)
+    : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
+      learn_more_link_(NULL),
+      advanced_link_(NULL),
+      close_button_(NULL),
+      browser_(browser),
+      message_loop_for_testing_(NULL) {
+  DCHECK(browser_);
+}
+
+OneClickSigninBubbleView::~OneClickSigninBubbleView() {
+}
+
+void OneClickSigninBubbleView::AnimationEnded(const ui::Animation* animation) {
+  views::BubbleDelegateView::AnimationEnded(animation);
+  if (message_loop_for_testing_)
+    message_loop_for_testing_->Quit();
+}
+
+void OneClickSigninBubbleView::Init() {
+  views::GridLayout* layout = new views::GridLayout(this);
+  SetLayoutManager(layout);
+  set_border(views::Border::CreateEmptyBorder(8, 8, 8, 8));
+
+  enum {
+    kColumnSetFillAlign,
+    kColumnSetControls
+  };
+
+  // Column set for descriptive text and link.
+  views::ColumnSet* cs = layout->AddColumnSet(kColumnSetFillAlign);
+  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 0,
+                views::GridLayout::USE_PREF, 0, kMinimumFieldSize);
+
+  cs = layout->AddColumnSet(kColumnSetControls);
+  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
+                views::GridLayout::USE_PREF, 0, 0);
+  cs->AddPaddingColumn(1, 0);
+  cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
+                views::GridLayout::USE_PREF, 0, 0);
+
+  // Add main text description.
+  views::Label* label = new views::Label(
+      l10n_util::GetStringFUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_MESSAGE,
+          l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)));
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+  label->SizeToFit(kMinimumFieldSize);
+
+  layout->StartRow(0, kColumnSetFillAlign);
+  layout->AddView(label);
+
+  // Add link for user to learn more about sync.
+  learn_more_link_= new views::Link(
+      l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_LEARN_MORE));
+  learn_more_link_->set_listener(this);
+  learn_more_link_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+
+  layout->StartRow(0, kColumnSetFillAlign);
+  layout->AddView(learn_more_link_);
+
+  layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
+
+  // Add link for user to do advanced config of sync.
+  advanced_link_= new views::Link(
+      l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_ADVANCED));
+  advanced_link_->set_listener(this);
+  advanced_link_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+
+  // Add controls at the bottom.
+  close_button_ = new views::NativeTextButton(
+      this, l10n_util::GetStringUTF16(IDS_OK));
+  close_button_->SetIsDefault(true);
+
+  layout->StartRow(0, kColumnSetControls);
+  layout->AddView(advanced_link_);
+  layout->AddView(close_button_);
+
+  AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
+}
+
+void OneClickSigninBubbleView::WindowClosing() {
+  // We have to reset |bubble_view_| here, not in our destructor, because
+  // we'll be destroyed asynchronously and the shown state will be checked
+  // before then.
+  DCHECK(bubble_view_ == this);
+  bubble_view_ = NULL;
+ }
+
+bool OneClickSigninBubbleView::AcceleratorPressed(
+    const ui::Accelerator& accelerator) {
+  if (accelerator.key_code() == ui::VKEY_RETURN ||
+      accelerator.key_code() == ui::VKEY_ESCAPE) {
+    StartFade(false);
+    return true;
+  }
+
+  return BubbleDelegateView::AcceleratorPressed(accelerator);
+}
+
+void OneClickSigninBubbleView::LinkClicked(views::Link* source,
+                                           int event_flags) {
+  StartFade(false);
+
+  GURL url;
+  if (source == learn_more_link_) {
+    url = GURL(chrome::kSyncLearnMoreURL);
+  } else {
+    url = GURL(std::string(chrome::kChromeUISettingsURL) +
+                           chrome::kSyncSetupSubPage);
+  }
+  browser_->AddSelectedTabWithURL(url, content::PAGE_TRANSITION_AUTO_BOOKMARK);
+}
+
+void OneClickSigninBubbleView::ButtonPressed(
+    views::Button* sender, const views::Event& event) {
+  DCHECK_EQ(sender, close_button_);
+  StartFade(false);
+}
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h
new file mode 100644
index 0000000..fe01158e
--- /dev/null
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 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_VIEWS_SYNC_ONE_CLICK_SIGNIN_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_SYNC_ONE_CLICK_SIGNIN_BUBBLE_VIEW_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/link_listener.h"
+
+class Browser;
+class MessageLoop;
+
+namespace views {
+class TextButton;
+}
+
+// OneClickSigninBubbleView is a view intended to be used as the content of an
+// Bubble. It provides simple and concise feedback to the user that sync'ing
+// has started after using the one-click singin infobar.
+class OneClickSigninBubbleView : public views::BubbleDelegateView,
+                                 public views::LinkListener,
+                                 public views::ButtonListener {
+ public:
+  // Show the one-click signin bubble if not already showing.  The bubble
+  // will be placed visually beneath |anchor_view|.  The |browser| is used
+  // to open links.
+  static void ShowBubble(views::View* anchor_view, Browser* browser);
+
+  static bool IsShowing();
+
+  static void Hide();
+
+  // Gets the global bubble view.  If its not showing returns NULL.  This
+  // method is meant to be called only from tests.
+  static OneClickSigninBubbleView* view_for_testing() { return bubble_view_; }
+
+  // The following accessor message should only be used for testing.
+  views::Link* learn_more_link_for_testing() const { return learn_more_link_; }
+  views::Link* advanced_link_for_testing() const { return advanced_link_; }
+  views::TextButton* close_button_for_testing() const { return close_button_; }
+  void set_message_loop_for_testing(MessageLoop* loop) {
+    message_loop_for_testing_ = loop;
+  }
+
+ private:
+  // Creates a BookmarkBubbleView.
+  OneClickSigninBubbleView(views::View* anchor_view, Browser* browser);
+
+  virtual ~OneClickSigninBubbleView();
+
+  // views::BubbleDelegateView methods:
+  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
+  virtual void Init() OVERRIDE;
+
+  // views::WidgetDelegate method:
+  virtual void WindowClosing() OVERRIDE;
+
+  // views::View method:
+  virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
+
+  // Overridden from views::LinkListener:
+  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
+
+  // Overridden from views::ButtonListener:
+  virtual void ButtonPressed(views::Button* sender,
+                             const views::Event& event) OVERRIDE;
+
+  // The bubble, if we're showing one.
+  static OneClickSigninBubbleView* bubble_view_;
+
+  // Link to web page to learn more about sync.
+  views::Link* learn_more_link_;
+
+  // Link to sync setup advanced page.
+  views::Link* advanced_link_;
+
+  // Button to close the window.
+  views::TextButton* close_button_;
+
+  // The browser that contains this bubble.
+  Browser* browser_;
+
+  // A message loop used only with unit tests.
+  MessageLoop* message_loop_for_testing_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_SYNC_ONE_CLICK_SIGNIN_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc
new file mode 100644
index 0000000..b391fc5
--- /dev/null
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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.
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "ui/views/controls/button/text_button.h"
+#include "ui/views/events/event.h"
+
+class OneClickSigninBubbleViewBrowserTest : public InProcessBrowserTest {
+ public:
+  OneClickSigninBubbleViewBrowserTest() : InProcessBrowserTest() { }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OneClickSigninBubbleViewBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, Show) {
+  browser()->window()->ShowOneClickSigninBubble();
+  ui_test_utils::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
+
+  OneClickSigninBubbleView::Hide();
+  ui_test_utils::RunAllPendingInMessageLoop();
+  EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
+}
+
+IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, CloseButton) {
+  int initial_tab_count = browser()->tab_count();
+
+  browser()->window()->ShowOneClickSigninBubble();
+  ui_test_utils::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
+
+  OneClickSigninBubbleView* view = OneClickSigninBubbleView::view_for_testing();
+  EXPECT_TRUE(view != NULL);
+  EXPECT_EQ(initial_tab_count, browser()->tab_count());
+
+  // Simulate pressing the OK button.  Set the message loop in the bubble
+  // view so that it can be quit once the bubble is hidden.
+  view->set_message_loop_for_testing(MessageLoop::current());
+  views::ButtonListener* listener = view;
+  views::MouseEvent event(ui::ET_MOUSE_PRESSED, 0, 0, 0);
+  listener->ButtonPressed(view->close_button_for_testing(), event);
+
+  // View should no longer be showing.  The message loop will exit once the
+  // fade animation of the bubble is done.
+  ui_test_utils::RunMessageLoop();
+  EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
+  EXPECT_EQ(initial_tab_count, browser()->tab_count());
+}
+
+IN_PROC_BROWSER_TEST_F(OneClickSigninBubbleViewBrowserTest, ViewLink) {
+  int initial_tab_count = browser()->tab_count();
+
+  browser()->window()->ShowOneClickSigninBubble();
+  ui_test_utils::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(OneClickSigninBubbleView::IsShowing());
+
+  OneClickSigninBubbleView* view = OneClickSigninBubbleView::view_for_testing();
+  EXPECT_TRUE(view != NULL);
+  EXPECT_EQ(initial_tab_count, browser()->tab_count());
+
+  // Simulate pressing a link in the bubble.  This should open a new tab.
+  views::LinkListener* listener = view;
+  listener->LinkClicked(view->learn_more_link_for_testing(), 0);
+
+  // View should no longer be showing and a new tab should be opened.
+  ui_test_utils::RunAllPendingInMessageLoop();
+  EXPECT_FALSE(OneClickSigninBubbleView::IsShowing());
+  EXPECT_EQ(initial_tab_count + 1, browser()->tab_count());
+}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index a8de353..2589374 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3522,6 +3522,8 @@
         'browser/ui/views/status_icons/status_tray_win.cc',
         'browser/ui/views/status_icons/status_tray_win.h',
         'browser/ui/views/stubs_aura.cc',
+        'browser/ui/views/sync/one_click_signin_bubble_view.cc',
+        'browser/ui/views/sync/one_click_signin_bubble_view.h',
         'browser/ui/views/tab_contents/native_tab_contents_container.h',
         'browser/ui/views/tab_contents/native_tab_contents_container_aura.cc',
         'browser/ui/views/tab_contents/native_tab_contents_container_aura.h',
@@ -3983,6 +3985,22 @@
         '<(SHARED_INTERMEDIATE_DIR)/autofill_regex_constants.cc',
       ],
       'conditions': [
+        ['enable_one_click_signin==0', {
+          'sources!': [
+            'browser/ui/cocoa/one_click_signin_dialog_controller.h',
+            'browser/ui/cocoa/one_click_signin_dialog_controller.mm',
+            'browser/ui/gtk/one_click_signin_dialog_gtk.cc',
+            'browser/ui/sync/one_click_signin_dialog.h',
+            'browser/ui/sync/one_click_signin_helper.cc',
+            'browser/ui/sync/one_click_signin_helper.h',
+            'browser/ui/sync/one_click_signin_histogram.h',
+            'browser/ui/sync/one_click_signin_sync_starter.cc',
+            'browser/ui/sync/one_click_signin_sync_starter.h',
+            'browser/ui/views/one_click_signin_dialog_views.cc',
+            'browser/ui/views/sync/one_click_signin_bubble_view.cc',
+            'browser/ui/views/sync/one_click_signin_bubble_view.h',
+          ]
+        }],
         ['disable_nacl==0', {
           'sources': [
             'browser/nacl_host/nacl_broker_host_win.cc',
@@ -4140,13 +4158,6 @@
             'browser/password_manager/native_backend_kwallet_x.h',
             'browser/platform_util_linux.cc',
             'browser/speech/extension_api/tts_extension_api_linux.cc',
-            'browser/ui/sync/one_click_signin_dialog.h',
-            'browser/ui/sync/one_click_signin_helper.cc',
-            'browser/ui/sync/one_click_signin_helper.h',
-            'browser/ui/sync/one_click_signin_histogram.h',
-            'browser/ui/sync/one_click_signin_sync_starter.cc',
-            'browser/ui/sync/one_click_signin_sync_starter.h',
-            'browser/ui/views/one_click_signin_dialog_views.cc',
             'browser/ui/webui/help/version_updater_unimplemented.cc',
             'browser/ui/webui/help/version_updater_unimplemented.h',
             'browser/upgrade_detector_impl.cc',
@@ -4416,12 +4427,6 @@
             'browser/bookmarks/bookmark_html_writer.cc',
             'browser/jankometer.cc',
             'browser/ui/sad_tab_helper.cc',
-            'browser/ui/sync/one_click_signin_dialog.h',
-            'browser/ui/sync/one_click_signin_helper.cc',
-            'browser/ui/sync/one_click_signin_helper.h',
-            'browser/ui/sync/one_click_signin_histogram.h',
-            'browser/ui/sync/one_click_signin_sync_starter.cc',
-            'browser/ui/sync/one_click_signin_sync_starter.h',
             'browser/ui/webui/certificate_viewer_webui.cc',
             'browser/ui/window_sizer.cc',
             'browser/ui/window_sizer.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 262132b..68ba37d 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2142,6 +2142,11 @@
         '../webkit/quota/mock_storage_client.h',
       ],
       'conditions': [
+        ['enable_one_click_signin==0', {
+          'sources!': [
+            'browser/ui/cocoa/one_click_signin_dialog_controller_unittest.mm',
+          ]
+        }],
         ['disable_nacl==1', {
           'sources!':[
             'nacl/nacl_validation_query_unittest.cc',
@@ -2814,6 +2819,7 @@
         'browser/ui/views/dom_view_browsertest.cc',
         'browser/ui/views/html_dialog_view_browsertest.cc',
         'browser/ui/views/select_file_dialog_extension_browsertest.cc',
+        'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
         'browser/ui/webui/chrome_url_data_manager_browsertest.cc',
         'browser/ui/webui/ntp/most_visited_browsertest.cc',
         'browser/ui/webui/test_chrome_web_ui_controller_factory_browsertest.cc',
@@ -2957,6 +2963,11 @@
         },
       ],
       'conditions': [
+        ['enable_one_click_signin==0', {
+          'sources!': [
+            'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
+          ]
+        }],
         ['disable_nacl==0', {
           'sources':[
             'browser/extensions/extension_nacl_browsertest.cc',
@@ -3207,6 +3218,7 @@
             'browser/ui/views/crypto_module_password_dialog_view_unittest.cc',
             'browser/ui/views/dom_view_browsertest.cc',
             'browser/ui/views/html_dialog_view_browsertest.cc',
+            'browser/ui/views/sync/one_click_signin_bubble_view_browsertest.cc',
           ],
         }],
         ['target_arch!="arm"', {
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 2c5303f..2471e32 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -93,6 +93,9 @@
   virtual void ShowBookmarkBubble(const GURL& url,
                                   bool already_bookmarked) OVERRIDE {}
   virtual void ShowChromeToMobileBubble() OVERRIDE {}
+#if defined(ENABLE_ONE_CLICK_SIGNIN)
+  virtual void ShowOneClickSigninBubble() OVERRIDE {}
+#endif
   virtual bool IsDownloadShelfVisible() const OVERRIDE;
   virtual DownloadShelf* GetDownloadShelf() OVERRIDE;
   virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE {}