browser_actions_container.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/extensions/extension_view_host.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/session_tab_helper.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/view_ids.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_popup.h"
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/browser_action_view.h"
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/common/extensions/command.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/pref_names.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/runtime_data.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_resources.h"
33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "third_party/skia/include/core/SkColor.h"
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/accessibility/ax_view_state.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/dragdrop/drag_utils.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ui/base/nine_image_painter_factory.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/theme_provider.h"
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/slide_animation.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ui/gfx/geometry/rect.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/resize_area.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/metrics.h"
45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ui/views/painter.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Horizontal spacing between most items in the container, as well as after the
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// last item or chevron (if visible).
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kItemSpacing = ToolbarView::kStandardSpacing;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Horizontal spacing before the chevron (if visible).
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kChevronSpacing = kItemSpacing - 2;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::disable_animations_during_testing_ = false;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BrowserActionsContainer
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BrowserActionsContainer::BrowserActionsContainer(Browser* browser,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 View* owner_view)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_(browser->profile()),
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      browser_(browser),
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      owner_view_(owner_view),
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_(NULL),
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      popup_button_(NULL),
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      model_(NULL),
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container_width_(0),
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chevron_(NULL),
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      overflow_menu_(NULL),
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suppress_chevron_(false),
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resize_amount_(0),
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animation_target_size_(0),
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_indicator_position_(-1),
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      task_factory_(this),
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      show_menu_task_factory_(this) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  model_ = extensions::ExtensionToolbarModel::Get(browser->profile());
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (model_)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_->AddObserver(this);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      browser->profile(),
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      owner_view->GetFocusManager(),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  resize_animation_.reset(new gfx::SlideAnimation(this));
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_area_ = new views::ResizeArea(this);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddChildView(resize_area_);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  chevron_ = new views::MenuButton(NULL, base::string16(), this, false);
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  chevron_->SetBorder(views::Border::NullBorder());
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->EnableCanvasFlippingForRTLUI(true);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetAccessibleName(
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetVisible(false);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddChildView(chevron_);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BrowserActionsContainer::~BrowserActionsContainer() {
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    observers_,
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    OnBrowserActionsContainerDestroyed());
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (overflow_menu_)
1157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    overflow_menu_->set_observer(NULL);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (model_)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_->RemoveObserver(this);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopShowFolderDropMenuTimer();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->GetWidget()->RemoveObserver(this);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HidePopup();
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeleteBrowserActionViews();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::Init() {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LoadImages();
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We wait to set the container width until now so that the chevron images
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will be loaded.  The width calculation needs to know the chevron size.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (model_ &&
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      !profile_->GetPrefs()->HasPrefPath(
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          extensions::pref_names::kToolbarSize)) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Migration code to the new VisibleIconCount pref.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(mpcomplete): remove this after users are upgraded to 5.0.
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int predefined_width = profile_->GetPrefs()->GetInteger(
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        extensions::pref_names::kBrowserActionContainerWidth);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (predefined_width != 0)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      model_->SetVisibleIconCount(WidthToIconCount(predefined_width));
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (model_ && model_->extensions_initialized())
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetContainerWidth();
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BrowserActionView* BrowserActionsContainer::GetBrowserActionView(
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionAction* action) {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BrowserActionViews::iterator i(browser_action_views_.begin());
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != browser_action_views_.end(); ++i) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((*i)->button()->browser_action() == action)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return *i;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::RefreshBrowserActionViews() {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < browser_action_views_.size(); ++i)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_action_views_[i]->button()->UpdateState();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::CreateBrowserActionViews() {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(browser_action_views_.empty());
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!model_)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (extensions::ExtensionList::const_iterator i(toolbar_items.begin());
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != toolbar_items.end(); ++i) {
1677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (!ShouldDisplayBrowserAction(i->get()))
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    BrowserActionView* view = new BrowserActionView(i->get(), browser_, this);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_action_views_.push_back(view);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddChildView(view);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::DeleteBrowserActionViews() {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HidePopup();
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteElements(&browser_action_views_);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t BrowserActionsContainer::VisibleBrowserActions() const {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t visible_actions = 0;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (browser_action_views_[i]->visible())
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++visible_actions;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  VLOG(4) << "BAC::VisibleBrowserActions() returns " << visible_actions
18846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          << " with size=" << browser_action_views_.size();
18946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return visible_actions;
19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
19146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
19246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)size_t BrowserActionsContainer::VisibleBrowserActionsAfterAnimation() const {
19346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!animating())
19446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    return VisibleBrowserActions();
19546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
19646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  size_t visible_actions = WidthToIconCount(animation_target_size_);
19746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  VLOG(4) << "BAC::VisibleBrowserActionsAfterAnimation() returns "
19846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          << visible_actions
19946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          << " with size=" << browser_action_views_.size();
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return visible_actions;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void BrowserActionsContainer::ExecuteExtensionCommand(
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const extensions::Extension* extension,
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const extensions::Command& command) {
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Global commands are handled by the ExtensionCommandsGlobalRegistry
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // instance.
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(!command.global());
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  extension_keybinding_registry_->ExecuteCommand(extension->id(),
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                                 command.accelerator());
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid BrowserActionsContainer::AddObserver(
214effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    BrowserActionsContainerObserver* observer) {
215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  observers_.AddObserver(observer);
216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid BrowserActionsContainer::RemoveObserver(
219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    BrowserActionsContainerObserver* observer) {
220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  observers_.RemoveObserver(observer);
221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
222effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)gfx::Size BrowserActionsContainer::GetPreferredSize() const {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We calculate the size of the view by taking the current width and
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // subtracting resize_amount_ (the latter represents how far the user is
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // resizing the view or, if animating the snapping, how far to animate it).
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // But we also clamp it to a minimum size and the maximum size, so that the
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // container can never shrink too far or take up more space than it needs. In
229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX).
230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  int preferred_width = std::min(
231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_),
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IconCountToWidth(-1, false));
233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Height will be ignored by the ToolbarView.
234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return gfx::Size(preferred_width, 0);
235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)gfx::Size BrowserActionsContainer::GetMinimumSize() const {
238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1, false));
239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Height will be ignored by the ToolbarView.
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return gfx::Size(min_width, 0);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::Layout() {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (browser_action_views_.empty()) {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetVisible(false);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetVisible(true);
250effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  resize_area_->SetBounds(0, 0, kItemSpacing, height());
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the icons don't all fit, show the chevron (unless suppressed).
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_x = GetPreferredSize().width();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chevron_->SetVisible(true);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Size chevron_size(chevron_->GetPreferredSize());
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_x -=
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ToolbarView::kStandardSpacing + chevron_size.width() + kChevronSpacing;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chevron_->SetBounds(
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        width() - ToolbarView::kStandardSpacing - chevron_size.width(),
261effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        0,
262effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        chevron_size.width(),
263effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        chevron_size.height());
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chevron_->SetVisible(false);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now draw the icons for the browser actions in the available space.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int icon_width = IconWidth(false);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionView* view = browser_action_views_[i];
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int x = ToolbarView::kStandardSpacing + (i * IconWidth(true));
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (x + icon_width <= max_x) {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view->SetBounds(x, 0, icon_width, height());
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view->SetVisible(true);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      view->SetVisible(false);
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::GetDropFormats(
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* formats,
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::set<OSExchangeData::CustomFormat>* custom_formats) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat());
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::AreDropTypesRequired() {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserActionDragData drop_data;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drop_data.Read(data) ? drop_data.IsFromProfile(profile_) : false;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnDragEntered(
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::DropTargetEvent& event) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::OnDragUpdated(
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::DropTargetEvent& event) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First check if we are above the chevron (overflow) menu.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetEventHandlerForPoint(event.location()) == chevron_) {
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartShowFolderDropMenuTimer();
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_MOVE;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopShowFolderDropMenuTimer();
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out where to display the indicator.  This is a complex calculation:
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First, we figure out how much space is to the left of the icon area, so we
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // can calculate the true offset into the icon area.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width_before_icons = ToolbarView::kStandardSpacing +
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (base::i18n::IsRTL() ?
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (chevron_->GetPreferredSize().width() + kChevronSpacing) : 0);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int offset_into_icon_area = event.x() - width_before_icons;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Next, we determine which icon to place the indicator in front of.  We want
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to place the indicator in front of icon n when the cursor is between the
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // midpoints of icons (n - 1) and n.  To do this we take the offset into the
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // icon area and transform it as follows:
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Real icon area:
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   0   a     *  b        c
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   |   |        |        |
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   |[IC|ON]  [IC|ON]  [IC|ON]
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Here the "*" represents the offset into the icon area, and since it's
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // between a and b, we want to return "1".
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Transformed "icon area":
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   0        a     *  b        c
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   |        |        |        |
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   |[ICON]  |[ICON]  |[ICON]  |
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we shift both our offset and our divider points later by half an icon
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // plus one spacing unit, then it becomes very easy to calculate how many
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // divider points we've passed, because they're the multiples of "one icon
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // plus padding".
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int before_icon_unclamped = (offset_into_icon_area + (IconWidth(false) / 2) +
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kItemSpacing) / IconWidth(true);
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Because the user can drag outside the container bounds, we need to clamp to
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the valid range.  Note that the maximum allowable value is (num icons), not
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (num icons - 1), because we represent the indicator being past the last
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // icon as being "before the (last + 1) icon".
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int before_icon = std::min(std::max(before_icon_unclamped, 0),
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             static_cast<int>(VisibleBrowserActions()));
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now we convert back to a pixel offset into the container.  We want to place
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the center of the drop indicator at the midpoint of the space before our
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // chosen icon.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDropIndicator(width_before_icons + (before_icon * IconWidth(true)) -
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kItemSpacing / 2));
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::DragDropTypes::DRAG_MOVE;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnDragExited() {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopShowFolderDropMenuTimer();
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drop_indicator_position_ = -1;
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::OnPerformDrop(
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::DropTargetEvent& event) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserActionDragData data;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!data.Read(event.data()))
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_NONE;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure we have the same view as we started with.
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(browser_action_views_[data.index()]->button()->extension()->id(),
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            data.id());
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(model_);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t i = 0;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; i < browser_action_views_.size(); ++i) {
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int view_x = browser_action_views_[i]->GetMirroredBounds().x();
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!browser_action_views_[i]->visible() ||
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (base::i18n::IsRTL() ? (view_x < drop_indicator_position_) :
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (view_x >= drop_indicator_position_))) {
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have reached the end of the visible icons or found one that has a
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // higher x position than the drop point.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |i| now points to the item to the right of the drop indicator*, which is
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // correct when dragging an icon to the left. When dragging to the right,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // however, we want the icon being dragged to get the index of the item to
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the left of the drop indicator, so we subtract one.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * Well, it can also point to the end, but not when dragging to the left. :)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (i > data.index())
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --i;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_->IsOffTheRecord())
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    i = model_->IncognitoIndexToOriginal(i);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model_->MoveBrowserAction(
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      browser_action_views_[data.index()]->button()->extension(), i);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnDragExited();  // Perform clean up after dragging.
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::DragDropTypes::DRAG_MOVE;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::GetAccessibleState(
410a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    ui::AXViewState* state) {
411a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state->role = ui::AX_ROLE_GROUP;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnMenuButtonClicked(views::View* source,
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  const gfx::Point& point) {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source == chevron_) {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    overflow_menu_ = new BrowserActionOverflowMenuController(
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this, browser_, chevron_, browser_action_views_,
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        VisibleBrowserActions());
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    overflow_menu_->set_observer(this);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    overflow_menu_->RunMenu(GetWidget(), false);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::WriteDragDataForView(View* sender,
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                   const gfx::Point& press_pt,
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                   OSExchangeData* data) {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(data);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionButton* button = browser_action_views_[i]->button();
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (button == sender) {
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Set the dragging image for the icon.
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::ImageSkia badge(browser_action_views_[i]->GetIconWithBadge());
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_utils::SetDragImageOnDataObject(badge, button->size(),
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           press_pt.OffsetFromOrigin(),
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           data);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Fill in the remaining info.
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserActionDragData drag_data(
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          browser_action_views_[i]->button()->extension()->id(), i);
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_data.Write(profile_, data);
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::GetDragOperationsForView(View* sender,
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                      const gfx::Point& p) {
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::DragDropTypes::DRAG_MOVE;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::CanStartDragForView(View* sender,
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  const gfx::Point& press_pt,
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  const gfx::Point& p) {
457effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // We don't allow dragging while we're highlighting.
458effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return !model_->is_highlighting();
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!done_resizing) {
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize_amount_ = resize_amount;
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnBrowserActionVisibilityChanged();
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Up until now we've only been modifying the resize_amount, but now it is
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // time to set the container size to the size we have resized to, and then
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // animate to the nearest icon count size if necessary (which may be 0).
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_width = IconCountToWidth(-1, false);
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  container_width_ =
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::min(std::max(0, container_width_ - resize_amount), max_width);
474d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            WidthToIconCount(container_width_));
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::AnimationProgressed(
479d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(resize_animation_.get(), animation);
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (container_width_ - animation_target_size_));
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnBrowserActionVisibilityChanged();
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
486d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  container_width_ = animation_target_size_;
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  animation_target_size_ = 0;
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_amount_ = 0;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  suppress_chevron_ = false;
491a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  OnBrowserActionVisibilityChanged();
492effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
493effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
494effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    observers_,
495effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    OnBrowserActionsContainerAnimationEnded());
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::NotifyMenuDeleted(
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionOverflowMenuController* controller) {
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(overflow_menu_, controller);
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  overflow_menu_ = NULL;
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BrowserActionsContainer::OnWidgetDestroying(views::Widget* widget) {
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(popup_->GetWidget(), widget);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->GetWidget()->RemoveObserver(this);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_ = NULL;
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |popup_button_| is NULL if the extension has been removed.
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_button_) {
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_button_->SetButtonNotPushed();
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_button_ = NULL;
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::InspectPopup(ExtensionAction* action) {
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserActionView* view = GetBrowserActionView(action);
5178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT, true);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::GetCurrentTabId() const {
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* active_tab =
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser_->tab_strip_model()->GetActiveWebContents();
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!active_tab)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SessionTabHelper::FromWebContents(active_tab)->session_id().id();
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnBrowserActionExecuted(
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionButton* button) {
5318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ShowPopup(button, ExtensionPopup::SHOW, true);
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetVisible(!browser_action_views_.empty());
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  owner_view_->Layout();
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  owner_view_->SchedulePaint();
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extensions::ActiveTabPermissionGranter*
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionsContainer::GetActiveTabPermissionGranter() {
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* web_contents =
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser_->tab_strip_model()->GetActiveWebContents();
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!web_contents)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extensions::TabHelper::FromWebContents(web_contents)->
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      active_tab_permission_granter();
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::MoveBrowserAction(const std::string& extension_id,
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                size_t new_index) {
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service =
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile_)->extension_service();
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (service) {
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension = service->GetExtensionById(extension_id, false);
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_->MoveBrowserAction(extension, new_index);
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
561a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool BrowserActionsContainer::ShowPopup(const extensions::Extension* extension,
562a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                        bool should_grant) {
563a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Do not override other popups and only show in active window. The window
564a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // must also have a toolbar, otherwise it should not be showing popups.
565a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
566a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // fixed.
567a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (popup_ ||
568a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !browser_->window()->IsActive() ||
569a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !browser_->window()->IsToolbarVisible()) {
570a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
571a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
572a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
573a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (BrowserActionViews::iterator it = browser_action_views_.begin();
574a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       it != browser_action_views_.end(); ++it) {
575a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    BrowserActionButton* button = (*it)->button();
576a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (button && button->extension() == extension)
577a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return ShowPopup(button, ExtensionPopup::SHOW, should_grant);
578a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
579a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return false;
580a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
581a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::HidePopup() {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove this as an observer and clear |popup_| and |popup_button_| here,
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // since we might change them before OnWidgetDestroying() gets called.
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_) {
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->GetWidget()->RemoveObserver(this);
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_->GetWidget()->Close();
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_ = NULL;
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_button_) {
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_button_->SetButtonNotPushed();
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    popup_button_ = NULL;
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::TestExecuteBrowserAction(int index) {
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserActionButton* button = browser_action_views_[index]->button();
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnBrowserActionExecuted(button);
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model_->SetVisibleIconCount(icons);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetVisible(icons < browser_action_views_.size());
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  container_width_ = IconCountToWidth(icons, chevron_->visible());
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Layout();
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
610effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // If the views haven't been initialized yet, wait for the next call to
611effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // paint (one will be triggered by entering highlight mode).
612effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (model_->is_highlighting() && !browser_action_views_.empty()) {
613effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    views::Painter::PaintPainterAt(
614effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        canvas, highlight_painter_.get(), GetLocalBounds());
615effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
616effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dragging (like we do for tab dragging).
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_indicator_position_ > -1) {
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The two-pixel width drop indicator.
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static const int kDropIndicatorWidth = 2;
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect indicator_bounds(
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        drop_indicator_position_ - (kDropIndicatorWidth / 2),
624effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        0,
625effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        kDropIndicatorWidth,
626effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        height());
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Color of the drop indicator.
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static const SkColor kDropIndicatorColor = SK_ColorBLACK;
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->FillRect(indicator_bounds, kDropIndicatorColor);
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::OnThemeChanged() {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LoadImages();
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
638a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)void BrowserActionsContainer::ViewHierarchyChanged(
639a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    const ViewHierarchyChangedDetails& details) {
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No extensions (e.g., incognito).
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!model_)
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
644a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (details.is_add && details.child == this) {
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Initial toolbar button creation and placement in the widget hierarchy.
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We do this here instead of in the constructor because AddBrowserAction
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // calls Layout on the Toolbar, which needs this object to be constructed
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // before its Layout function is called.
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CreateBrowserActionViews();
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::IconWidth(bool include_padding) {
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool initialized = false;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int icon_width = 0;
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!initialized) {
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initialized = true;
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_width = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IDR_BROWSER_ACTION)->width();
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return icon_width + (include_padding ? kItemSpacing : 0);
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::IconHeight() {
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool initialized = false;
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int icon_height = 0;
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!initialized) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initialized = true;
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_height = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IDR_BROWSER_ACTION)->height();
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return icon_height;
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::BrowserActionAdded(const Extension* extension,
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 int index) {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(DEBUG)
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(browser_action_views_[i]->button()->extension() != extension) <<
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           "Asked to add a browser action view for an extension that already "
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           "exists.";
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseOverflowMenu();
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
68846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!ShouldDisplayBrowserAction(extension)) {
68946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "Should not display: " << extension->name().c_str();
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
69146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
69346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  size_t visible_actions = VisibleBrowserActionsAfterAnimation();
69446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  VLOG(4) << "Got back " << visible_actions << " visible.";
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the new browser action to the vector and the view hierarchy.
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_->IsOffTheRecord())
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = model_->OriginalIndexToIncognito(index);
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserActionView* view = new BrowserActionView(extension, browser_, this);
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser_action_views_.insert(browser_action_views_.begin() + index, view);
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddChildViewAt(view, index);
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we are still initializing the container, don't bother animating.
70446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!model_->extensions_initialized()) {
70546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "Still initializing";
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
70746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Enlarge the container if it was already at maximum size and we're not in
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the middle of upgrading.
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((model_->GetVisibleIconCount() < 0) &&
7125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      !extensions::ExtensionSystem::Get(profile_)->runtime_data()->
7132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          IsBeingUpgraded(extension)) {
71446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "At max, Save and animate";
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppress_chevron_ = true;
716d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, visible_actions + 1);
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
71846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "Not at max";
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Just redraw the (possibly modified) visible icon set.
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OnBrowserActionVisibilityChanged();
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) {
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseOverflowMenu();
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (popup_ && popup_->host()->extension() == extension)
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HidePopup();
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
73046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  size_t visible_actions = VisibleBrowserActionsAfterAnimation();
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BrowserActionViews::iterator i(browser_action_views_.begin());
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != browser_action_views_.end(); ++i) {
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((*i)->button()->extension() == extension) {
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete *i;
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      browser_action_views_.erase(i);
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the extension is being upgraded we don't want the bar to shrink
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // because the icon is just going to get re-added to the same location.
7395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (extensions::ExtensionSystem::Get(profile_)->runtime_data()->
7402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              IsBeingUpgraded(extension))
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (browser_action_views_.size() > visible_actions) {
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If we have more icons than we can show, then we must not be changing
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the container size (since we either removed an icon from the main
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // area and one from the overflow list will have shifted in, or we
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // removed an entry directly from the overflow list).
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        OnBrowserActionVisibilityChanged();
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Either we went from overflow to no-overflow, or we shrunk the no-
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // overflow container by 1.  Either way the size changed, so animate.
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chevron_->SetVisible(false);
753d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  browser_action_views_.size());
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::BrowserActionMoved(const Extension* extension,
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 int index) {
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShouldDisplayBrowserAction(extension))
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_->IsOffTheRecord())
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = model_->OriginalIndexToIncognito(index);
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size()));
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeleteBrowserActionViews();
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateBrowserActionViews();
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Layout();
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)bool BrowserActionsContainer::BrowserActionShowPopup(
7788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const extensions::Extension* extension) {
779a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return ShowPopup(extension, false);
7808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
7818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
7825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void BrowserActionsContainer::VisibleCountChanged() {
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetContainerWidth();
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
786effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid BrowserActionsContainer::HighlightModeChanged(bool is_highlighting) {
787effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // The visual highlighting is done in OnPaint(). It's a bit of a pain that
788effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // we delete and recreate everything here, but that's how it's done in
789effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // BrowserActionMoved(), too. If we want to optimize it, we could move the
790effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // existing icons, instead of deleting it all.
791effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DeleteBrowserActionViews();
792effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  CreateBrowserActionViews();
793effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, browser_action_views_.size());
794effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
795effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::LoadImages() {
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::ThemeProvider* tp = GetThemeProvider();
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetIcon(*tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetHoverIcon(*tp->GetImageSkiaNamed(
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDR_BROWSER_ACTIONS_OVERFLOW_H));
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetPushedIcon(*tp->GetImageSkiaNamed(
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDR_BROWSER_ACTIONS_OVERFLOW_P));
803effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
804effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const int kImages[] = IMAGE_GRID(IDR_DEVELOPER_MODE_HIGHLIGHT);
805effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  highlight_painter_.reset(views::Painter::CreateImageGridPainter(kImages));
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::SetContainerWidth() {
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int visible_actions = model_->GetVisibleIconCount();
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (visible_actions < 0)  // All icons should be visible.
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    visible_actions = model_->toolbar_items().size();
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chevron_->SetVisible(
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<size_t>(visible_actions) < model_->toolbar_items().size());
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  container_width_ = IconCountToWidth(visible_actions, chevron_->visible());
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::CloseOverflowMenu() {
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (overflow_menu_)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    overflow_menu_->CancelMenu();
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::StopShowFolderDropMenuTimer() {
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  show_menu_task_factory_.InvalidateWeakPtrs();
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::StartShowFolderDropMenuTimer() {
827b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::MessageLoop::current()->PostDelayedTask(
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&BrowserActionsContainer::ShowDropFolder,
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 show_menu_task_factory_.GetWeakPtr()),
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::ShowDropFolder() {
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!overflow_menu_);
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDropIndicator(-1);
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  overflow_menu_ = new BrowserActionOverflowMenuController(
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, browser_, chevron_, browser_action_views_, VisibleBrowserActions());
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  overflow_menu_->set_observer(this);
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  overflow_menu_->RunMenu(GetWidget(), true);
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::SetDropIndicator(int x_pos) {
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_indicator_position_ != x_pos) {
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_indicator_position_ = x_pos;
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BrowserActionsContainer::IconCountToWidth(int icons,
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              bool display_chevron) const {
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icons < 0)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icons = browser_action_views_.size();
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((icons == 0) && !display_chevron)
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ToolbarView::kStandardSpacing;
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int icons_size =
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing);
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int chevron_size = display_chevron ?
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0;
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ToolbarView::kStandardSpacing + icons_size + chevron_size +
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ToolbarView::kStandardSpacing;
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t BrowserActionsContainer::WidthToIconCount(int pixels) const {
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check for widths large enough to show the entire icon set.
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pixels >= IconCountToWidth(-1, false))
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return browser_action_views_.size();
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We need to reserve space for the resize area, chevron, and the spacing on
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either side of the chevron.
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int available_space = pixels - ToolbarView::kStandardSpacing -
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chevron_->GetPreferredSize().width() - kChevronSpacing -
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ToolbarView::kStandardSpacing;
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now we add an extra between-item padding value so the space can be divided
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // evenly by (size of icon with padding).
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<size_t>(
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::max(0, available_space + kItemSpacing) / IconWidth(true));
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
880cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int BrowserActionsContainer::MinimumNonemptyWidth() const {
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ToolbarView::kStandardSpacing + kChevronSpacing +
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chevron_->GetPreferredSize().width() + ToolbarView::kStandardSpacing;
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BrowserActionsContainer::SaveDesiredSizeAndAnimate(
886d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    gfx::Tween::Type tween_type,
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t num_visible_icons) {
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Save off the desired number of visible icons.  We do this now instead of at
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the end of the animation so that even if the browser is shut down while
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // animating, the right value will be restored on next run.
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: Don't save the icon count in incognito because there may be fewer
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // icons in that mode. The result is that the container in a normal window is
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // always at least as wide as in an incognito window.
89446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!profile_->IsOffTheRecord()) {
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    model_->SetVisibleIconCount(num_visible_icons);
89646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "Setting visible count: " << num_visible_icons;
89746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  } else {
89846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    VLOG(4) << "|Skipping| setting visible count: " << num_visible_icons;
89946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int target_size = IconCountToWidth(num_visible_icons,
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_visible_icons < browser_action_views_.size());
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!disable_animations_during_testing_) {
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Animate! We have to set the animation_target_size_ after calling Reset(),
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // because that could end up calling AnimationEnded which clears the value.
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize_animation_->Reset();
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize_animation_->SetTweenType(tween_type);
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    animation_target_size_ = target_size;
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resize_animation_->Show();
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    animation_target_size_ = target_size;
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AnimationEnded(resize_animation_.get());
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BrowserActionsContainer::ShouldDisplayBrowserAction(
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only display incognito-enabled extensions while in incognito mode.
9185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return !profile_->IsOffTheRecord() ||
9195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extensions::util::IsIncognitoEnabled(extension->id(), profile_);
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)bool BrowserActionsContainer::ShowPopup(
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserActionButton* button,
9248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    ExtensionPopup::ShowAction show_action,
9258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool should_grant) {
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Extension* extension = button->extension();
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL popup_url;
9288bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (model_->ExecuteBrowserAction(
9298bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          extension, browser_, &popup_url, should_grant) !=
930effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      extensions::ExtensionToolbarModel::ACTION_SHOW_POPUP) {
9318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return false;
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're showing the same popup, just hide it and return.
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool same_showing = popup_ && button == popup_button_;
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Always hide the current popup, even if it's not the same.
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only one popup should be visible at a time.
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HidePopup();
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (same_showing)
9428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return false;
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We can get the execute event for browser actions that are not visible,
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // since buttons can be activated from the overflow menu (chevron). In that
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // case we show the popup as originating from the chevron.
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  View* reference_view = button->parent()->visible() ? button : chevron_;
948a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  popup_ = ExtensionPopup::ShowPopup(popup_url, browser_, reference_view,
949a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                     views::BubbleBorder::TOP_RIGHT,
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     show_action);
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_->GetWidget()->AddObserver(this);
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popup_button_ = button;
9538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
9548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Only set button as pushed if it was triggered by a user click.
9558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (should_grant)
9568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    popup_button_->SetButtonPushed();
9578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return true;
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
959