| Index: chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc
 | 
| diff --git a/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc b/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..25f604287a13b31ddfb3da506ce54ff9bd5c32f8
 | 
| --- /dev/null
 | 
| +++ b/chrome/browser/ui/views/website_settings/chooser_bubble_ui.cc
 | 
| @@ -0,0 +1,346 @@
 | 
| +// Copyright 2015 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/website_settings/chooser_bubble_ui.h"
 | 
| +
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/prefs/pref_service.h"
 | 
| +#include "base/strings/string16.h"
 | 
| +#include "base/strings/utf_string_conversions.h"
 | 
| +#include "chrome/browser/profiles/profile.h"
 | 
| +#include "chrome/browser/ui/browser.h"
 | 
| +#include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
 | 
| +#include "chrome/browser/ui/views/frame/browser_view.h"
 | 
| +#include "chrome/browser/ui/views/frame/top_container_view.h"
 | 
| +#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 | 
| +#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
 | 
| +#include "chrome/browser/usb/web_usb_permission_bubble_request.h"
 | 
| +#include "chrome/common/pref_names.h"
 | 
| +#include "chrome/grit/generated_resources.h"
 | 
| +#include "ui/accessibility/ax_view_state.h"
 | 
| +#include "ui/base/l10n/l10n_util.h"
 | 
| +#include "ui/base/resource/resource_bundle.h"
 | 
| +#include "ui/gfx/paint_vector_icon.h"
 | 
| +#include "ui/gfx/text_constants.h"
 | 
| +#include "ui/gfx/vector_icons_public.h"
 | 
| +#include "ui/views/bubble/bubble_delegate.h"
 | 
| +#include "ui/views/bubble/bubble_frame_view.h"
 | 
| +#include "ui/views/controls/button/label_button.h"
 | 
| +#include "ui/views/controls/button/label_button_border.h"
 | 
| +#include "ui/views/controls/label.h"
 | 
| +#include "ui/views/controls/table/table_view.h"
 | 
| +#include "ui/views/controls/table/table_view_observer.h"
 | 
| +#include "ui/views/layout/box_layout.h"
 | 
| +#include "ui/views/layout/grid_layout.h"
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// chooser permission bubble width
 | 
| +const int kChooserPermissionBubbleWidth = 300;
 | 
| +
 | 
| +// chooser permission bubble height
 | 
| +const int kChooserPermissionBubbleHeight = 200;
 | 
| +
 | 
| +// text label height when no devices found
 | 
| +const int kLabelHeight = 30;
 | 
| +
 | 
| +// Spacing constant for outer margin. This is added to the
 | 
| +// bubble margin itself to equalize the margins at 13px.
 | 
| +const int kBubbleOuterMargin = 5;
 | 
| +
 | 
| +// Spacing between major items should be 9px.
 | 
| +const int kItemMajorSpacing = 9;
 | 
| +
 | 
| +// Button border size, draws inside the spacing distance.
 | 
| +const int kButtonBorderSize = 2;
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +// View implementation for the chooser bubble.
 | 
| +class ChooserBubbleUiDelegate : public views::BubbleDelegateView,
 | 
| +                                public views::ButtonListener,
 | 
| +                                public views::TableViewObserver {
 | 
| + public:
 | 
| +  ChooserBubbleUiDelegate(views::View* anchor_view,
 | 
| +                          views::BubbleBorder::Arrow anchor_arrow,
 | 
| +                          ChooserBubbleUi* owner,
 | 
| +                          const WebUsbPermissionBubbleRequest* request);
 | 
| +  ~ChooserBubbleUiDelegate() override;
 | 
| +
 | 
| +  void Close();
 | 
| +
 | 
| +  // BubbleDelegateView:
 | 
| +  bool ShouldShowCloseButton() const override;
 | 
| +  bool ShouldShowWindowTitle() const override;
 | 
| +  base::string16 GetWindowTitle() const override;
 | 
| +  void OnWidgetDestroying(views::Widget* widget) override;
 | 
| +
 | 
| +  // ButtonListener:
 | 
| +  void ButtonPressed(views::Button* button, const ui::Event& event) override;
 | 
| +
 | 
| +  // views::TableViewObserver:
 | 
| +  void OnSelectionChanged() override;
 | 
| +
 | 
| +  // Updates the anchor's arrow and view. Also repositions the bubble so it's
 | 
| +  // displayed in the correct location.
 | 
| +  void UpdateAnchor(views::View* anchor_view,
 | 
| +                    views::BubbleBorder::Arrow anchor_arrow);
 | 
| +
 | 
| + private:
 | 
| +  ChooserBubbleUi* owner_;
 | 
| +  views::Button* connect_;
 | 
| +  views::Button* cancel_;
 | 
| +  views::TableView* table_view_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(ChooserBubbleUiDelegate);
 | 
| +};
 | 
| +
 | 
| +ui::TableColumn ChooserTableColumn(int id, const std::string& title) {
 | 
| +  ui::TableColumn column;
 | 
| +  column.id = id;
 | 
| +  column.title = base::ASCIIToUTF16(title.c_str());
 | 
| +  return column;
 | 
| +}
 | 
| +
 | 
| +class ChooserTableModel : public ui::TableModel {
 | 
| + public:
 | 
| +  explicit ChooserTableModel(const std::vector<std::string>& device_names)
 | 
| +      : observer_(nullptr) {
 | 
| +    device_names_ = device_names;
 | 
| +    row_count_ = static_cast<int>(device_names_.size());
 | 
| +  }
 | 
| +
 | 
| +  // ui::TableModel:
 | 
| +  int RowCount() override { return row_count_; }
 | 
| +
 | 
| +  base::string16 GetText(int row, int column_id) override {
 | 
| +    if (row >= 0 && row < row_count_) {
 | 
| +      return base::ASCIIToUTF16(device_names_[row]);
 | 
| +    } else {
 | 
| +      return base::string16();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  void SetObserver(ui::TableModelObserver* observer) override {
 | 
| +    observer_ = observer;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  ui::TableModelObserver* observer_;
 | 
| +  std::vector<std::string> device_names_;
 | 
| +  int row_count_;
 | 
| +};
 | 
| +
 | 
| +ChooserBubbleUiDelegate::ChooserBubbleUiDelegate(
 | 
| +    views::View* anchor_view,
 | 
| +    views::BubbleBorder::Arrow anchor_arrow,
 | 
| +    ChooserBubbleUi* owner,
 | 
| +    const WebUsbPermissionBubbleRequest* request)
 | 
| +    : views::BubbleDelegateView(anchor_view, anchor_arrow),
 | 
| +      owner_(owner),
 | 
| +      connect_(nullptr),
 | 
| +      cancel_(nullptr) {
 | 
| +  views::GridLayout* layout = new views::GridLayout(this);
 | 
| +  SetLayoutManager(layout);
 | 
| +
 | 
| +  views::ColumnSet* column_set = layout->AddColumnSet(0);
 | 
| +  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
 | 
| +                        views::GridLayout::USE_PREF, 0, 0);
 | 
| +
 | 
| +  layout->StartRow(1, 0);
 | 
| +
 | 
| +  const std::vector<std::string>& device_names = request->device_names();
 | 
| +  if (!device_names.empty()) {
 | 
| +    // create a table view and list the device names into it.
 | 
| +    std::vector<ui::TableColumn> table_columns;
 | 
| +    table_columns.push_back(ChooserTableColumn(
 | 
| +        0, "" /* empty string makes the column title invisible */));
 | 
| +    table_view_ = new views::TableView(new ChooserTableModel(device_names),
 | 
| +                                       table_columns, views::TEXT_ONLY, true);
 | 
| +    table_view_->SetObserver(this);
 | 
| +    layout->AddView(table_view_->CreateParentIfNecessary(), 1, 1,
 | 
| +                    views::GridLayout::FILL, views::GridLayout::FILL,
 | 
| +                    kChooserPermissionBubbleWidth,
 | 
| +                    kChooserPermissionBubbleHeight);
 | 
| +  } else {
 | 
| +    // show a text label saying no devices found.
 | 
| +    views::Label* label = new views::Label(l10n_util::GetStringUTF16(
 | 
| +        IDS_WEBUSB_PERMISSIONS_BUBBLE_NO_DEVICES_FOUND_PROMPT));
 | 
| +    label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
 | 
| +    layout->AddView(label, 1, 1, views::GridLayout::FILL,
 | 
| +                    views::GridLayout::FILL, kChooserPermissionBubbleWidth,
 | 
| +                    kLabelHeight);
 | 
| +  }
 | 
| +
 | 
| +  layout->AddPaddingRow(0, kItemMajorSpacing);
 | 
| +
 | 
| +  views::View* button_row = new views::View();
 | 
| +  views::GridLayout* button_layout = new views::GridLayout(button_row);
 | 
| +  views::ColumnSet* button_columns = button_layout->AddColumnSet(0);
 | 
| +  button_row->SetLayoutManager(button_layout);
 | 
| +  layout->StartRow(1, 0);
 | 
| +  layout->AddView(button_row);
 | 
| +
 | 
| +  // lay out the Connect/Cancel buttons.
 | 
| +  button_columns->AddColumn(views::GridLayout::TRAILING,
 | 
| +                            views::GridLayout::FILL, 100,
 | 
| +                            views::GridLayout::USE_PREF, 0, 0);
 | 
| +  button_columns->AddPaddingColumn(0,
 | 
| +                                   kItemMajorSpacing - (2 * kButtonBorderSize));
 | 
| +  button_columns->AddColumn(views::GridLayout::TRAILING,
 | 
| +                            views::GridLayout::FILL, 0,
 | 
| +                            views::GridLayout::USE_PREF, 0, 0);
 | 
| +  button_layout->StartRow(0, 0);
 | 
| +
 | 
| +  base::string16 connect_text = base::ASCIIToUTF16("Connect");
 | 
| +  views::LabelButton* connect_button =
 | 
| +      new views::LabelButton(this, connect_text);
 | 
| +  connect_button->SetStyle(views::Button::STYLE_BUTTON);
 | 
| +  // disable the connect button at the beginning since no device selected yet.
 | 
| +  connect_button->SetEnabled(false);
 | 
| +  button_layout->AddView(connect_button);
 | 
| +  connect_ = connect_button;
 | 
| +
 | 
| +  base::string16 cancel_text = base::ASCIIToUTF16("Cancel");
 | 
| +  views::LabelButton* cancel_button = new views::LabelButton(this, cancel_text);
 | 
| +  cancel_button->SetStyle(views::Button::STYLE_BUTTON);
 | 
| +  button_layout->AddView(cancel_button);
 | 
| +  cancel_ = cancel_button;
 | 
| +
 | 
| +  button_layout->AddPaddingRow(0, kBubbleOuterMargin);
 | 
| +}
 | 
| +
 | 
| +ChooserBubbleUiDelegate::~ChooserBubbleUiDelegate() {
 | 
| +  RemoveAllChildViews(true);
 | 
| +  if (owner_)
 | 
| +    owner_->Close();
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUiDelegate::Close() {
 | 
| +  owner_ = nullptr;
 | 
| +  GetWidget()->Close();
 | 
| +}
 | 
| +
 | 
| +bool ChooserBubbleUiDelegate::ShouldShowCloseButton() const {
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ChooserBubbleUiDelegate::ShouldShowWindowTitle() const {
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +base::string16 ChooserBubbleUiDelegate::GetWindowTitle() const {
 | 
| +  return l10n_util::GetStringUTF16(IDS_WEBUSB_PERMISSIONS_BUBBLE_PROMPT);
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUiDelegate::OnWidgetDestroying(views::Widget* widget) {
 | 
| +  views::BubbleDelegateView::OnWidgetDestroying(widget);
 | 
| +  if (owner_) {
 | 
| +    owner_->Close();
 | 
| +    owner_ = nullptr;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUiDelegate::ButtonPressed(views::Button* button,
 | 
| +                                            const ui::Event& event) {
 | 
| +  if (!owner_)
 | 
| +    return;
 | 
| +
 | 
| +  if (button == connect_)
 | 
| +    owner_->Connect(table_view_->selection_model().active());
 | 
| +  else if (button == cancel_)
 | 
| +    owner_->Cancel();
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUiDelegate::OnSelectionChanged() {
 | 
| +  // enable the connect button since user has selected an item.
 | 
| +  connect_->SetEnabled(true);
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUiDelegate::UpdateAnchor(
 | 
| +    views::View* anchor_view,
 | 
| +    views::BubbleBorder::Arrow anchor_arrow) {
 | 
| +  if (GetAnchorView() == anchor_view && arrow() == anchor_arrow)
 | 
| +    return;
 | 
| +
 | 
| +  set_arrow(anchor_arrow);
 | 
| +
 | 
| +  // Update the border in the bubble: will either add or remove the arrow.
 | 
| +  views::BubbleFrameView* frame =
 | 
| +      views::BubbleDelegateView::GetBubbleFrameView();
 | 
| +  views::BubbleBorder::Arrow adjusted_arrow = anchor_arrow;
 | 
| +  if (base::i18n::IsRTL())
 | 
| +    adjusted_arrow = views::BubbleBorder::horizontal_mirror(adjusted_arrow);
 | 
| +  frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
 | 
| +      new views::BubbleBorder(adjusted_arrow, shadow(), color())));
 | 
| +
 | 
| +  // Reposition the bubble based on the updated arrow and view.
 | 
| +  SetAnchorView(anchor_view);
 | 
| +}
 | 
| +
 | 
| +//////////////////////////////////////////////////////////////////////////////
 | 
| +// ChooserBubbleUi
 | 
| +
 | 
| +ChooserBubbleUi::ChooserBubbleUi(Browser* browser,
 | 
| +                                 WebUsbPermissionBubbleRequest* request)
 | 
| +    : browser_(browser), request_(request), bubble_delegate_(nullptr) {
 | 
| +  DCHECK(browser);
 | 
| +}
 | 
| +
 | 
| +ChooserBubbleUi::~ChooserBubbleUi() {}
 | 
| +
 | 
| +void ChooserBubbleUi::Show(BubbleReference bubble_reference) {
 | 
| +  if (bubble_delegate_)
 | 
| +    bubble_delegate_->Close();
 | 
| +  bubble_delegate_ = new ChooserBubbleUiDelegate(
 | 
| +      GetAnchorView(), GetAnchorArrow(), this, request_);
 | 
| +
 | 
| +  // Set |parent_window| because some valid anchors can become hidden.
 | 
| +  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
 | 
| +      browser_->window()->GetNativeWindow());
 | 
| +  bubble_delegate_->set_parent_window(widget->GetNativeView());
 | 
| +
 | 
| +  views::BubbleDelegateView::CreateBubble(bubble_delegate_)->Show();
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUi::Close() {
 | 
| +  if (bubble_delegate_) {
 | 
| +    bubble_delegate_->Close();
 | 
| +    bubble_delegate_ = nullptr;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUi::UpdateAnchorPosition() {
 | 
| +  bubble_delegate_->UpdateAnchor(GetAnchorView(), GetAnchorArrow());
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUi::Connect(int index) {
 | 
| +  request_->Connect(index);
 | 
| +  Close();
 | 
| +}
 | 
| +
 | 
| +void ChooserBubbleUi::Cancel() {
 | 
| +  request_->Cancel();
 | 
| +  Close();
 | 
| +}
 | 
| +
 | 
| +views::View* ChooserBubbleUi::GetAnchorView() {
 | 
| +  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
 | 
| +
 | 
| +  if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR))
 | 
| +    return browser_view->GetLocationBarView()->location_icon_view();
 | 
| +
 | 
| +  if (browser_view->IsFullscreenBubbleVisible())
 | 
| +    return browser_view->exclusive_access_bubble()->GetView();
 | 
| +
 | 
| +  return browser_view->top_container();
 | 
| +}
 | 
| +
 | 
| +views::BubbleBorder::Arrow ChooserBubbleUi::GetAnchorArrow() {
 | 
| +  if (browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR))
 | 
| +    return views::BubbleBorder::TOP_LEFT;
 | 
| +  return views::BubbleBorder::NONE;
 | 
| +}
 | 
| 
 |