| // Copyright 2013 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/toolbar/app_menu_button.h" | 
 |  | 
 | #include "base/location.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/time/time.h" | 
 | #include "chrome/browser/themes/theme_properties.h" | 
 | #include "chrome/browser/ui/browser.h" | 
 | #include "chrome/browser/ui/browser_otr_state.h" | 
 | #include "chrome/browser/ui/layout_constants.h" | 
 | #include "chrome/browser/ui/toolbar/app_menu_model.h" | 
 | #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h" | 
 | #include "chrome/browser/ui/views/toolbar/app_menu.h" | 
 | #include "chrome/browser/ui/views/toolbar/toolbar_view.h" | 
 | #include "chrome/grit/theme_resources.h" | 
 | #include "extensions/common/feature_switch.h" | 
 | #include "ui/base/resource/resource_bundle.h" | 
 | #include "ui/base/theme_provider.h" | 
 | #include "ui/gfx/color_palette.h" | 
 | #include "ui/gfx/paint_vector_icon.h" | 
 | #include "ui/gfx/vector_icons_public.h" | 
 | #include "ui/keyboard/keyboard_controller.h" | 
 | #include "ui/views/controls/button/label_button_border.h" | 
 | #include "ui/views/controls/menu/menu_listener.h" | 
 | #include "ui/views/metrics.h" | 
 |  | 
 | // static | 
 | bool AppMenuButton::g_open_app_immediately_for_testing = false; | 
 |  | 
 | AppMenuButton::AppMenuButton(ToolbarView* toolbar_view) | 
 |     : views::MenuButton(base::string16(), toolbar_view, false), | 
 |       severity_(AppMenuIconController::Severity::NONE), | 
 |       type_(AppMenuIconController::IconType::NONE), | 
 |       toolbar_view_(toolbar_view), | 
 |       allow_extension_dragging_( | 
 |           extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()), | 
 |       margin_trailing_(0), | 
 |       weak_factory_(this) { | 
 |   SetInkDropMode(InkDropMode::ON); | 
 |   SetFocusPainter(nullptr); | 
 | } | 
 |  | 
 | AppMenuButton::~AppMenuButton() {} | 
 |  | 
 | void AppMenuButton::SetSeverity(AppMenuIconController::IconType type, | 
 |                                 AppMenuIconController::Severity severity, | 
 |                                 bool animate) { | 
 |   type_ = type; | 
 |   severity_ = severity; | 
 |   UpdateIcon(); | 
 | } | 
 |  | 
 | void AppMenuButton::ShowMenu(bool for_drop) { | 
 |   if (menu_ && menu_->IsShowing()) | 
 |     return; | 
 |  | 
 | #if defined(USE_AURA) | 
 |   keyboard::KeyboardController* keyboard_controller = | 
 |       keyboard::KeyboardController::GetInstance(); | 
 |   if (keyboard_controller && keyboard_controller->keyboard_visible()) { | 
 |     keyboard_controller->HideKeyboard( | 
 |         keyboard::KeyboardController::HIDE_REASON_AUTOMATIC); | 
 |   } | 
 | #endif | 
 |  | 
 |   Browser* browser = toolbar_view_->browser(); | 
 |  | 
 |   menu_.reset(new AppMenu(browser, for_drop ? AppMenu::FOR_DROP : 0)); | 
 |   menu_model_.reset(new AppMenuModel(toolbar_view_, browser)); | 
 |   menu_->Init(menu_model_.get()); | 
 |  | 
 |   for (views::MenuListener& observer : menu_listeners_) | 
 |     observer.OnMenuOpened(); | 
 |  | 
 |   base::TimeTicks menu_open_time = base::TimeTicks::Now(); | 
 |   menu_->RunMenu(this); | 
 |  | 
 |   if (!for_drop) { | 
 |     // Record the time-to-action for the menu. We don't record in the case of a | 
 |     // drag-and-drop command because menus opened for drag-and-drop don't block | 
 |     // the message loop. | 
 |     UMA_HISTOGRAM_TIMES("Toolbar.AppMenuTimeToAction", | 
 |                         base::TimeTicks::Now() - menu_open_time); | 
 |   } | 
 | } | 
 |  | 
 | void AppMenuButton::CloseMenu() { | 
 |   if (menu_) | 
 |     menu_->CloseMenu(); | 
 |   menu_.reset(); | 
 | } | 
 |  | 
 | bool AppMenuButton::IsMenuShowing() const { | 
 |   return menu_ && menu_->IsShowing(); | 
 | } | 
 |  | 
 | void AppMenuButton::AddMenuListener(views::MenuListener* listener) { | 
 |   menu_listeners_.AddObserver(listener); | 
 | } | 
 |  | 
 | void AppMenuButton::RemoveMenuListener(views::MenuListener* listener) { | 
 |   menu_listeners_.RemoveObserver(listener); | 
 | } | 
 |  | 
 | gfx::Size AppMenuButton::GetPreferredSize() const { | 
 |   gfx::Size size(image()->GetPreferredSize()); | 
 |   gfx::Insets insets(GetLayoutInsets(TOOLBAR_BUTTON)); | 
 |   size.Enlarge(insets.width(), insets.height()); | 
 |   return size; | 
 | } | 
 |  | 
 | void AppMenuButton::UpdateIcon() { | 
 |   SkColor color = gfx::kPlaceholderColor; | 
 |   const ui::NativeTheme* native_theme = GetNativeTheme(); | 
 |   switch (severity_) { | 
 |     case AppMenuIconController::Severity::NONE: | 
 |       color = GetThemeProvider()->GetColor( | 
 |           ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON); | 
 |       break; | 
 |     case AppMenuIconController::Severity::LOW: | 
 |       color = native_theme->GetSystemColor( | 
 |           ui::NativeTheme::kColorId_AlertSeverityLow); | 
 |       break; | 
 |     case AppMenuIconController::Severity::MEDIUM: | 
 |       color = native_theme->GetSystemColor( | 
 |           ui::NativeTheme::kColorId_AlertSeverityMedium); | 
 |       break; | 
 |     case AppMenuIconController::Severity::HIGH: | 
 |       color = native_theme->GetSystemColor( | 
 |           ui::NativeTheme::kColorId_AlertSeverityHigh); | 
 |       break; | 
 |   } | 
 |  | 
 |   gfx::VectorIconId icon_id = gfx::VectorIconId::VECTOR_ICON_NONE; | 
 |   switch (type_) { | 
 |     case AppMenuIconController::IconType::NONE: | 
 |       icon_id = gfx::VectorIconId::BROWSER_TOOLS; | 
 |       DCHECK_EQ(AppMenuIconController::Severity::NONE, severity_); | 
 |       break; | 
 |     case AppMenuIconController::IconType::UPGRADE_NOTIFICATION: | 
 |       icon_id = gfx::VectorIconId::BROWSER_TOOLS_UPDATE; | 
 |       break; | 
 |     case AppMenuIconController::IconType::GLOBAL_ERROR: | 
 |     case AppMenuIconController::IconType::INCOMPATIBILITY_WARNING: | 
 |       icon_id = gfx::VectorIconId::BROWSER_TOOLS_ERROR; | 
 |       break; | 
 |   } | 
 |  | 
 |   SetImage(views::Button::STATE_NORMAL, gfx::CreateVectorIcon(icon_id, color)); | 
 | } | 
 |  | 
 | void AppMenuButton::SetTrailingMargin(int margin) { | 
 |   margin_trailing_ = margin; | 
 |   UpdateThemedBorder(); | 
 |   InvalidateLayout(); | 
 | } | 
 |  | 
 | const char* AppMenuButton::GetClassName() const { | 
 |   return "AppMenuButton"; | 
 | } | 
 |  | 
 | std::unique_ptr<views::LabelButtonBorder> AppMenuButton::CreateDefaultBorder() | 
 |     const { | 
 |   std::unique_ptr<views::LabelButtonBorder> border = | 
 |       MenuButton::CreateDefaultBorder(); | 
 |  | 
 |   // Adjust border insets to follow the margin change, | 
 |   // which will be reflected in where the border is painted | 
 |   // through GetThemePaintRect(). | 
 |   gfx::Insets insets(border->GetInsets()); | 
 |   insets += gfx::Insets(0, 0, 0, margin_trailing_); | 
 |   border->set_insets(insets); | 
 |  | 
 |   return border; | 
 | } | 
 |  | 
 | gfx::Rect AppMenuButton::GetThemePaintRect() const { | 
 |   gfx::Rect rect(MenuButton::GetThemePaintRect()); | 
 |   rect.Inset(0, 0, margin_trailing_, 0); | 
 |   return rect; | 
 | } | 
 |  | 
 | bool AppMenuButton::GetDropFormats( | 
 |     int* formats, | 
 |     std::set<ui::Clipboard::FormatType>* format_types) { | 
 |   return allow_extension_dragging_ ? | 
 |       BrowserActionDragData::GetDropFormats(format_types) : | 
 |       views::View::GetDropFormats(formats, format_types); | 
 | } | 
 |  | 
 | bool AppMenuButton::AreDropTypesRequired() { | 
 |   return allow_extension_dragging_ ? | 
 |       BrowserActionDragData::AreDropTypesRequired() : | 
 |       views::View::AreDropTypesRequired(); | 
 | } | 
 |  | 
 | bool AppMenuButton::CanDrop(const ui::OSExchangeData& data) { | 
 |   return allow_extension_dragging_ ? | 
 |       BrowserActionDragData::CanDrop(data, | 
 |                                      toolbar_view_->browser()->profile()) : | 
 |       views::View::CanDrop(data); | 
 | } | 
 |  | 
 | void AppMenuButton::OnDragEntered(const ui::DropTargetEvent& event) { | 
 |   DCHECK(allow_extension_dragging_); | 
 |   DCHECK(!weak_factory_.HasWeakPtrs()); | 
 |   if (!g_open_app_immediately_for_testing) { | 
 |     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 
 |         FROM_HERE, | 
 |         base::Bind(&AppMenuButton::ShowMenu, weak_factory_.GetWeakPtr(), true), | 
 |         base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay())); | 
 |   } else { | 
 |     ShowMenu(true); | 
 |   } | 
 | } | 
 |  | 
 | int AppMenuButton::OnDragUpdated(const ui::DropTargetEvent& event) { | 
 |   DCHECK(allow_extension_dragging_); | 
 |   return ui::DragDropTypes::DRAG_MOVE; | 
 | } | 
 |  | 
 | void AppMenuButton::OnDragExited() { | 
 |   DCHECK(allow_extension_dragging_); | 
 |   weak_factory_.InvalidateWeakPtrs(); | 
 | } | 
 |  | 
 | int AppMenuButton::OnPerformDrop(const ui::DropTargetEvent& event) { | 
 |   DCHECK(allow_extension_dragging_); | 
 |   return ui::DragDropTypes::DRAG_MOVE; | 
 | } |