1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/browser_actions_container.h"
7 #include "base/compiler_specific.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/stl_util.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_system.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/extensions/tab_helper.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sessions/session_tab_helper.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/view_ids.h"
20 #include "chrome/browser/ui/views/browser_action_view.h"
21 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
22 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
23 #include "chrome/browser/ui/views/extensions/extension_popup.h"
24 #include "chrome/browser/ui/views/toolbar_view.h"
25 #include "chrome/common/pref_names.h"
26 #include "grit/generated_resources.h"
27 #include "grit/theme_resources.h"
28 #include "grit/ui_resources.h"
29 #include "ui/base/accessibility/accessible_view_state.h"
30 #include "ui/base/dragdrop/drag_utils.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/base/theme_provider.h"
34 #include "ui/gfx/animation/slide_animation.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/views/controls/resize_area.h"
37 #include "ui/views/metrics.h"
38 #include "ui/views/widget/widget.h"
40 using extensions::Extension;
44 // Horizontal spacing between most items in the container, as well as after the
45 // last item or chevron (if visible).
46 const int kItemSpacing = ToolbarView::kStandardSpacing;
48 // Horizontal spacing before the chevron (if visible).
49 const int kChevronSpacing = kItemSpacing - 2;
54 bool BrowserActionsContainer::disable_animations_during_testing_ = false;
56 ////////////////////////////////////////////////////////////////////////////////
57 // BrowserActionsContainer
59 BrowserActionsContainer::BrowserActionsContainer(Browser* browser,
61 : profile_(browser->profile()),
63 owner_view_(owner_view),
70 suppress_chevron_(false),
72 animation_target_size_(0),
73 drop_indicator_position_(-1),
75 show_menu_task_factory_(this) {
76 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
78 ExtensionService* service =
79 extensions::ExtensionSystem::Get(profile_)->extension_service();
81 model_ = service->toolbar_model();
82 model_->AddObserver(this);
85 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
87 owner_view->GetFocusManager(),
88 extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
91 resize_animation_.reset(new gfx::SlideAnimation(this));
92 resize_area_ = new views::ResizeArea(this);
93 AddChildView(resize_area_);
95 chevron_ = new views::MenuButton(NULL, string16(), this, false);
96 chevron_->set_border(NULL);
97 chevron_->EnableCanvasFlippingForRTLUI(true);
98 chevron_->SetAccessibleName(
99 l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
100 chevron_->SetVisible(false);
101 AddChildView(chevron_);
104 BrowserActionsContainer::~BrowserActionsContainer() {
106 overflow_menu_->set_observer(NULL);
108 model_->RemoveObserver(this);
109 StopShowFolderDropMenuTimer();
111 popup_->GetWidget()->RemoveObserver(this);
113 DeleteBrowserActionViews();
116 void BrowserActionsContainer::Init() {
119 // We wait to set the container width until now so that the chevron images
120 // will be loaded. The width calculation needs to know the chevron size.
122 !profile_->GetPrefs()->HasPrefPath(prefs::kExtensionToolbarSize)) {
123 // Migration code to the new VisibleIconCount pref.
124 // TODO(mpcomplete): remove this after users are upgraded to 5.0.
125 int predefined_width =
126 profile_->GetPrefs()->GetInteger(prefs::kBrowserActionContainerWidth);
127 if (predefined_width != 0)
128 model_->SetVisibleIconCount(WidthToIconCount(predefined_width));
130 if (model_ && model_->extensions_initialized())
134 BrowserActionView* BrowserActionsContainer::GetBrowserActionView(
135 ExtensionAction* action) {
136 for (BrowserActionViews::iterator i(browser_action_views_.begin());
137 i != browser_action_views_.end(); ++i) {
138 if ((*i)->button()->browser_action() == action)
144 void BrowserActionsContainer::RefreshBrowserActionViews() {
145 for (size_t i = 0; i < browser_action_views_.size(); ++i)
146 browser_action_views_[i]->button()->UpdateState();
149 void BrowserActionsContainer::CreateBrowserActionViews() {
150 DCHECK(browser_action_views_.empty());
154 const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
155 for (extensions::ExtensionList::const_iterator i(toolbar_items.begin());
156 i != toolbar_items.end(); ++i) {
157 if (!ShouldDisplayBrowserAction(i->get()))
160 BrowserActionView* view = new BrowserActionView(i->get(), browser_, this);
161 browser_action_views_.push_back(view);
166 void BrowserActionsContainer::DeleteBrowserActionViews() {
168 STLDeleteElements(&browser_action_views_);
171 size_t BrowserActionsContainer::VisibleBrowserActions() const {
172 size_t visible_actions = 0;
173 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
174 if (browser_action_views_[i]->visible())
177 return visible_actions;
180 gfx::Size BrowserActionsContainer::GetPreferredSize() {
181 if (browser_action_views_.empty())
182 return gfx::Size(ToolbarView::kStandardSpacing, 0);
184 // We calculate the size of the view by taking the current width and
185 // subtracting resize_amount_ (the latter represents how far the user is
186 // resizing the view or, if animating the snapping, how far to animate it).
187 // But we also clamp it to a minimum size and the maximum size, so that the
188 // container can never shrink too far or take up more space than it needs. In
189 // other words: ContainerMinSize() < width() - resize < ClampTo(MAX).
190 int clamped_width = std::min(
191 std::max(ContainerMinSize(), container_width_ - resize_amount_),
192 IconCountToWidth(-1, false));
193 return gfx::Size(clamped_width, 0);
196 void BrowserActionsContainer::Layout() {
197 if (browser_action_views_.empty()) {
203 resize_area_->SetBounds(0, ToolbarView::kVertSpacing, kItemSpacing,
206 // If the icons don't all fit, show the chevron (unless suppressed).
207 int max_x = GetPreferredSize().width();
208 if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_) {
209 chevron_->SetVisible(true);
210 gfx::Size chevron_size(chevron_->GetPreferredSize());
212 ToolbarView::kStandardSpacing + chevron_size.width() + kChevronSpacing;
214 width() - ToolbarView::kStandardSpacing - chevron_size.width(),
215 ToolbarView::kVertSpacing, chevron_size.width(), chevron_size.height());
217 chevron_->SetVisible(false);
220 // Now draw the icons for the browser actions in the available space.
221 int icon_width = IconWidth(false);
222 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
223 BrowserActionView* view = browser_action_views_[i];
224 int x = ToolbarView::kStandardSpacing + (i * IconWidth(true));
225 if (x + icon_width <= max_x) {
226 view->SetBounds(x, 0, icon_width, height());
227 view->SetVisible(true);
229 view->SetVisible(false);
234 bool BrowserActionsContainer::GetDropFormats(
236 std::set<OSExchangeData::CustomFormat>* custom_formats) {
237 custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat());
242 bool BrowserActionsContainer::AreDropTypesRequired() {
246 bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
247 BrowserActionDragData drop_data;
248 return drop_data.Read(data) ? drop_data.IsFromProfile(profile_) : false;
251 void BrowserActionsContainer::OnDragEntered(
252 const ui::DropTargetEvent& event) {
255 int BrowserActionsContainer::OnDragUpdated(
256 const ui::DropTargetEvent& event) {
257 // First check if we are above the chevron (overflow) menu.
258 if (GetEventHandlerForPoint(event.location()) == chevron_) {
259 if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
260 StartShowFolderDropMenuTimer();
261 return ui::DragDropTypes::DRAG_MOVE;
263 StopShowFolderDropMenuTimer();
265 // Figure out where to display the indicator. This is a complex calculation:
267 // First, we figure out how much space is to the left of the icon area, so we
268 // can calculate the true offset into the icon area.
269 int width_before_icons = ToolbarView::kStandardSpacing +
270 (base::i18n::IsRTL() ?
271 (chevron_->GetPreferredSize().width() + kChevronSpacing) : 0);
272 int offset_into_icon_area = event.x() - width_before_icons;
274 // Next, we determine which icon to place the indicator in front of. We want
275 // to place the indicator in front of icon n when the cursor is between the
276 // midpoints of icons (n - 1) and n. To do this we take the offset into the
277 // icon area and transform it as follows:
282 // |[IC|ON] [IC|ON] [IC|ON]
283 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
284 // Here the "*" represents the offset into the icon area, and since it's
285 // between a and b, we want to return "1".
287 // Transformed "icon area":
290 // |[ICON] |[ICON] |[ICON] |
291 // If we shift both our offset and our divider points later by half an icon
292 // plus one spacing unit, then it becomes very easy to calculate how many
293 // divider points we've passed, because they're the multiples of "one icon
295 int before_icon_unclamped = (offset_into_icon_area + (IconWidth(false) / 2) +
296 kItemSpacing) / IconWidth(true);
298 // Because the user can drag outside the container bounds, we need to clamp to
299 // the valid range. Note that the maximum allowable value is (num icons), not
300 // (num icons - 1), because we represent the indicator being past the last
301 // icon as being "before the (last + 1) icon".
302 int before_icon = std::min(std::max(before_icon_unclamped, 0),
303 static_cast<int>(VisibleBrowserActions()));
305 // Now we convert back to a pixel offset into the container. We want to place
306 // the center of the drop indicator at the midpoint of the space before our
308 SetDropIndicator(width_before_icons + (before_icon * IconWidth(true)) -
311 return ui::DragDropTypes::DRAG_MOVE;
314 void BrowserActionsContainer::OnDragExited() {
315 StopShowFolderDropMenuTimer();
316 drop_indicator_position_ = -1;
320 int BrowserActionsContainer::OnPerformDrop(
321 const ui::DropTargetEvent& event) {
322 BrowserActionDragData data;
323 if (!data.Read(event.data()))
324 return ui::DragDropTypes::DRAG_NONE;
326 // Make sure we have the same view as we started with.
327 DCHECK_EQ(browser_action_views_[data.index()]->button()->extension()->id(),
332 for (; i < browser_action_views_.size(); ++i) {
333 int view_x = browser_action_views_[i]->GetMirroredBounds().x();
334 if (!browser_action_views_[i]->visible() ||
335 (base::i18n::IsRTL() ? (view_x < drop_indicator_position_) :
336 (view_x >= drop_indicator_position_))) {
337 // We have reached the end of the visible icons or found one that has a
338 // higher x position than the drop point.
343 // |i| now points to the item to the right of the drop indicator*, which is
344 // correct when dragging an icon to the left. When dragging to the right,
345 // however, we want the icon being dragged to get the index of the item to
346 // the left of the drop indicator, so we subtract one.
347 // * Well, it can also point to the end, but not when dragging to the left. :)
348 if (i > data.index())
351 if (profile_->IsOffTheRecord())
352 i = model_->IncognitoIndexToOriginal(i);
354 model_->MoveBrowserAction(
355 browser_action_views_[data.index()]->button()->extension(), i);
357 OnDragExited(); // Perform clean up after dragging.
358 return ui::DragDropTypes::DRAG_MOVE;
361 void BrowserActionsContainer::GetAccessibleState(
362 ui::AccessibleViewState* state) {
363 state->role = ui::AccessibilityTypes::ROLE_GROUPING;
364 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
367 void BrowserActionsContainer::OnMenuButtonClicked(views::View* source,
368 const gfx::Point& point) {
369 if (source == chevron_) {
370 overflow_menu_ = new BrowserActionOverflowMenuController(
371 this, browser_, chevron_, browser_action_views_,
372 VisibleBrowserActions());
373 overflow_menu_->set_observer(this);
374 overflow_menu_->RunMenu(GetWidget(), false);
378 void BrowserActionsContainer::WriteDragDataForView(View* sender,
379 const gfx::Point& press_pt,
380 OSExchangeData* data) {
383 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
384 BrowserActionButton* button = browser_action_views_[i]->button();
385 if (button == sender) {
386 // Set the dragging image for the icon.
387 gfx::ImageSkia badge(browser_action_views_[i]->GetIconWithBadge());
388 drag_utils::SetDragImageOnDataObject(badge, button->size(),
389 press_pt.OffsetFromOrigin(),
392 // Fill in the remaining info.
393 BrowserActionDragData drag_data(
394 browser_action_views_[i]->button()->extension()->id(), i);
395 drag_data.Write(profile_, data);
401 int BrowserActionsContainer::GetDragOperationsForView(View* sender,
402 const gfx::Point& p) {
403 return ui::DragDropTypes::DRAG_MOVE;
406 bool BrowserActionsContainer::CanStartDragForView(View* sender,
407 const gfx::Point& press_pt,
408 const gfx::Point& p) {
412 void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
413 if (!done_resizing) {
414 resize_amount_ = resize_amount;
415 OnBrowserActionVisibilityChanged();
419 // Up until now we've only been modifying the resize_amount, but now it is
420 // time to set the container size to the size we have resized to, and then
421 // animate to the nearest icon count size if necessary (which may be 0).
422 int max_width = IconCountToWidth(-1, false);
424 std::min(std::max(0, container_width_ - resize_amount), max_width);
425 SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
426 WidthToIconCount(container_width_));
429 void BrowserActionsContainer::AnimationProgressed(
430 const gfx::Animation* animation) {
431 DCHECK_EQ(resize_animation_.get(), animation);
432 resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
433 (container_width_ - animation_target_size_));
434 OnBrowserActionVisibilityChanged();
437 void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
438 container_width_ = animation_target_size_;
439 animation_target_size_ = 0;
441 OnBrowserActionVisibilityChanged();
442 suppress_chevron_ = false;
445 void BrowserActionsContainer::NotifyMenuDeleted(
446 BrowserActionOverflowMenuController* controller) {
447 DCHECK_EQ(overflow_menu_, controller);
448 overflow_menu_ = NULL;
451 void BrowserActionsContainer::OnWidgetDestroying(views::Widget* widget) {
452 DCHECK_EQ(popup_->GetWidget(), widget);
453 popup_->GetWidget()->RemoveObserver(this);
455 // |popup_button_| is NULL if the extension has been removed.
457 popup_button_->SetButtonNotPushed();
458 popup_button_ = NULL;
462 void BrowserActionsContainer::InspectPopup(ExtensionAction* action) {
463 BrowserActionView* view = GetBrowserActionView(action);
464 ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT, true);
467 int BrowserActionsContainer::GetCurrentTabId() const {
468 content::WebContents* active_tab =
469 browser_->tab_strip_model()->GetActiveWebContents();
473 return SessionTabHelper::FromWebContents(active_tab)->session_id().id();
476 void BrowserActionsContainer::OnBrowserActionExecuted(
477 BrowserActionButton* button) {
478 ShowPopup(button, ExtensionPopup::SHOW, true);
481 void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
482 SetVisible(!browser_action_views_.empty());
483 owner_view_->Layout();
484 owner_view_->SchedulePaint();
487 gfx::Point BrowserActionsContainer::GetViewContentOffset() const {
488 return gfx::Point(0, ToolbarView::kVertSpacing);
491 extensions::ActiveTabPermissionGranter*
492 BrowserActionsContainer::GetActiveTabPermissionGranter() {
493 content::WebContents* web_contents =
494 browser_->tab_strip_model()->GetActiveWebContents();
497 return extensions::TabHelper::FromWebContents(web_contents)->
498 active_tab_permission_granter();
501 void BrowserActionsContainer::MoveBrowserAction(const std::string& extension_id,
503 ExtensionService* service =
504 extensions::ExtensionSystem::Get(profile_)->extension_service();
506 const Extension* extension = service->GetExtensionById(extension_id, false);
507 model_->MoveBrowserAction(extension, new_index);
512 void BrowserActionsContainer::HidePopup() {
513 // Remove this as an observer and clear |popup_| and |popup_button_| here,
514 // since we might change them before OnWidgetDestroying() gets called.
516 popup_->GetWidget()->RemoveObserver(this);
517 popup_->GetWidget()->Close();
521 popup_button_->SetButtonNotPushed();
522 popup_button_ = NULL;
526 void BrowserActionsContainer::TestExecuteBrowserAction(int index) {
527 BrowserActionButton* button = browser_action_views_[index]->button();
528 OnBrowserActionExecuted(button);
531 void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
532 model_->SetVisibleIconCount(icons);
533 chevron_->SetVisible(icons < browser_action_views_.size());
534 container_width_ = IconCountToWidth(icons, chevron_->visible());
539 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
540 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
541 // dragging (like we do for tab dragging).
542 if (drop_indicator_position_ > -1) {
543 // The two-pixel width drop indicator.
544 static const int kDropIndicatorWidth = 2;
545 gfx::Rect indicator_bounds(
546 drop_indicator_position_ - (kDropIndicatorWidth / 2),
547 ToolbarView::kVertSpacing, kDropIndicatorWidth, IconHeight());
549 // Color of the drop indicator.
550 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
551 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
555 void BrowserActionsContainer::OnThemeChanged() {
559 void BrowserActionsContainer::ViewHierarchyChanged(
560 const ViewHierarchyChangedDetails& details) {
561 // No extensions (e.g., incognito).
565 if (details.is_add && details.child == this) {
566 // Initial toolbar button creation and placement in the widget hierarchy.
567 // We do this here instead of in the constructor because AddBrowserAction
568 // calls Layout on the Toolbar, which needs this object to be constructed
569 // before its Layout function is called.
570 CreateBrowserActionViews();
575 int BrowserActionsContainer::IconWidth(bool include_padding) {
576 static bool initialized = false;
577 static int icon_width = 0;
580 icon_width = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
581 IDR_BROWSER_ACTION)->width();
583 return icon_width + (include_padding ? kItemSpacing : 0);
587 int BrowserActionsContainer::IconHeight() {
588 static bool initialized = false;
589 static int icon_height = 0;
592 icon_height = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
593 IDR_BROWSER_ACTION)->height();
598 void BrowserActionsContainer::BrowserActionAdded(const Extension* extension,
601 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
602 DCHECK(browser_action_views_[i]->button()->extension() != extension) <<
603 "Asked to add a browser action view for an extension that already "
609 if (!ShouldDisplayBrowserAction(extension))
612 size_t visible_actions = VisibleBrowserActions();
614 // Add the new browser action to the vector and the view hierarchy.
615 if (profile_->IsOffTheRecord())
616 index = model_->OriginalIndexToIncognito(index);
617 BrowserActionView* view = new BrowserActionView(extension, browser_, this);
618 browser_action_views_.insert(browser_action_views_.begin() + index, view);
619 AddChildViewAt(view, index);
621 // If we are still initializing the container, don't bother animating.
622 if (!model_->extensions_initialized())
625 // Enlarge the container if it was already at maximum size and we're not in
626 // the middle of upgrading.
627 if ((model_->GetVisibleIconCount() < 0) &&
628 !extensions::ExtensionSystem::Get(profile_)->extension_service()->
629 IsBeingUpgraded(extension)) {
630 suppress_chevron_ = true;
631 SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, visible_actions + 1);
633 // Just redraw the (possibly modified) visible icon set.
634 OnBrowserActionVisibilityChanged();
638 void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) {
641 if (popup_ && popup_->host()->extension() == extension)
644 size_t visible_actions = VisibleBrowserActions();
645 for (BrowserActionViews::iterator i(browser_action_views_.begin());
646 i != browser_action_views_.end(); ++i) {
647 if ((*i)->button()->extension() == extension) {
649 browser_action_views_.erase(i);
651 // If the extension is being upgraded we don't want the bar to shrink
652 // because the icon is just going to get re-added to the same location.
653 if (extensions::ExtensionSystem::Get(profile_)->extension_service()->
654 IsBeingUpgraded(extension))
657 if (browser_action_views_.size() > visible_actions) {
658 // If we have more icons than we can show, then we must not be changing
659 // the container size (since we either removed an icon from the main
660 // area and one from the overflow list will have shifted in, or we
661 // removed an entry directly from the overflow list).
662 OnBrowserActionVisibilityChanged();
664 // Either we went from overflow to no-overflow, or we shrunk the no-
665 // overflow container by 1. Either way the size changed, so animate.
666 chevron_->SetVisible(false);
667 SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
668 browser_action_views_.size());
675 void BrowserActionsContainer::BrowserActionMoved(const Extension* extension,
677 if (!ShouldDisplayBrowserAction(extension))
680 if (profile_->IsOffTheRecord())
681 index = model_->OriginalIndexToIncognito(index);
683 DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size()));
685 DeleteBrowserActionViews();
686 CreateBrowserActionViews();
691 bool BrowserActionsContainer::BrowserActionShowPopup(
692 const extensions::Extension* extension) {
693 // Do not override other popups and only show in active window. The window
694 // must also have a toolbar, otherwise it should not be showing popups.
695 // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
698 !browser_->window()->IsActive() ||
699 !browser_->window()->IsToolbarVisible()) {
703 for (BrowserActionViews::iterator it = browser_action_views_.begin();
704 it != browser_action_views_.end(); ++it) {
705 BrowserActionButton* button = (*it)->button();
706 if (button && button->extension() == extension)
707 return ShowPopup(button, ExtensionPopup::SHOW, false);
712 void BrowserActionsContainer::ModelLoaded() {
716 void BrowserActionsContainer::LoadImages() {
717 ui::ThemeProvider* tp = GetThemeProvider();
718 chevron_->SetIcon(*tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
719 chevron_->SetHoverIcon(*tp->GetImageSkiaNamed(
720 IDR_BROWSER_ACTIONS_OVERFLOW_H));
721 chevron_->SetPushedIcon(*tp->GetImageSkiaNamed(
722 IDR_BROWSER_ACTIONS_OVERFLOW_P));
725 void BrowserActionsContainer::SetContainerWidth() {
726 int visible_actions = model_->GetVisibleIconCount();
727 if (visible_actions < 0) // All icons should be visible.
728 visible_actions = model_->toolbar_items().size();
729 chevron_->SetVisible(
730 static_cast<size_t>(visible_actions) < model_->toolbar_items().size());
731 container_width_ = IconCountToWidth(visible_actions, chevron_->visible());
734 void BrowserActionsContainer::CloseOverflowMenu() {
736 overflow_menu_->CancelMenu();
739 void BrowserActionsContainer::StopShowFolderDropMenuTimer() {
740 show_menu_task_factory_.InvalidateWeakPtrs();
743 void BrowserActionsContainer::StartShowFolderDropMenuTimer() {
744 base::MessageLoop::current()->PostDelayedTask(
746 base::Bind(&BrowserActionsContainer::ShowDropFolder,
747 show_menu_task_factory_.GetWeakPtr()),
748 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
751 void BrowserActionsContainer::ShowDropFolder() {
752 DCHECK(!overflow_menu_);
753 SetDropIndicator(-1);
754 overflow_menu_ = new BrowserActionOverflowMenuController(
755 this, browser_, chevron_, browser_action_views_, VisibleBrowserActions());
756 overflow_menu_->set_observer(this);
757 overflow_menu_->RunMenu(GetWidget(), true);
760 void BrowserActionsContainer::SetDropIndicator(int x_pos) {
761 if (drop_indicator_position_ != x_pos) {
762 drop_indicator_position_ = x_pos;
767 int BrowserActionsContainer::IconCountToWidth(int icons,
768 bool display_chevron) const {
770 icons = browser_action_views_.size();
771 if ((icons == 0) && !display_chevron)
772 return ToolbarView::kStandardSpacing;
774 (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing);
775 int chevron_size = display_chevron ?
776 (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0;
777 return ToolbarView::kStandardSpacing + icons_size + chevron_size +
778 ToolbarView::kStandardSpacing;
781 size_t BrowserActionsContainer::WidthToIconCount(int pixels) const {
782 // Check for widths large enough to show the entire icon set.
783 if (pixels >= IconCountToWidth(-1, false))
784 return browser_action_views_.size();
786 // We need to reserve space for the resize area, chevron, and the spacing on
787 // either side of the chevron.
788 int available_space = pixels - ToolbarView::kStandardSpacing -
789 chevron_->GetPreferredSize().width() - kChevronSpacing -
790 ToolbarView::kStandardSpacing;
791 // Now we add an extra between-item padding value so the space can be divided
792 // evenly by (size of icon with padding).
793 return static_cast<size_t>(
794 std::max(0, available_space + kItemSpacing) / IconWidth(true));
797 int BrowserActionsContainer::ContainerMinSize() const {
798 return ToolbarView::kStandardSpacing + kChevronSpacing +
799 chevron_->GetPreferredSize().width() + ToolbarView::kStandardSpacing;
802 void BrowserActionsContainer::SaveDesiredSizeAndAnimate(
803 gfx::Tween::Type tween_type,
804 size_t num_visible_icons) {
805 // Save off the desired number of visible icons. We do this now instead of at
806 // the end of the animation so that even if the browser is shut down while
807 // animating, the right value will be restored on next run.
808 // NOTE: Don't save the icon count in incognito because there may be fewer
809 // icons in that mode. The result is that the container in a normal window is
810 // always at least as wide as in an incognito window.
811 if (!profile_->IsOffTheRecord())
812 model_->SetVisibleIconCount(num_visible_icons);
814 int target_size = IconCountToWidth(num_visible_icons,
815 num_visible_icons < browser_action_views_.size());
816 if (!disable_animations_during_testing_) {
817 // Animate! We have to set the animation_target_size_ after calling Reset(),
818 // because that could end up calling AnimationEnded which clears the value.
819 resize_animation_->Reset();
820 resize_animation_->SetTweenType(tween_type);
821 animation_target_size_ = target_size;
822 resize_animation_->Show();
824 animation_target_size_ = target_size;
825 AnimationEnded(resize_animation_.get());
829 bool BrowserActionsContainer::ShouldDisplayBrowserAction(
830 const Extension* extension) {
831 // Only display incognito-enabled extensions while in incognito mode.
833 (!profile_->IsOffTheRecord() ||
834 extension_util::IsIncognitoEnabled(
836 extensions::ExtensionSystem::Get(profile_)->extension_service()));
839 bool BrowserActionsContainer::ShowPopup(
840 BrowserActionButton* button,
841 ExtensionPopup::ShowAction show_action,
843 const Extension* extension = button->extension();
845 if (model_->ExecuteBrowserAction(
846 extension, browser_, &popup_url, should_grant) !=
847 ExtensionToolbarModel::ACTION_SHOW_POPUP) {
851 // If we're showing the same popup, just hide it and return.
852 bool same_showing = popup_ && button == popup_button_;
854 // Always hide the current popup, even if it's not the same.
855 // Only one popup should be visible at a time.
861 // We can get the execute event for browser actions that are not visible,
862 // since buttons can be activated from the overflow menu (chevron). In that
863 // case we show the popup as originating from the chevron.
864 View* reference_view = button->parent()->visible() ? button : chevron_;
865 views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ?
866 views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT;
867 popup_ = ExtensionPopup::ShowPopup(popup_url,
872 popup_->GetWidget()->AddObserver(this);
873 popup_button_ = button;
875 // Only set button as pushed if it was triggered by a user click.
877 popup_button_->SetButtonPushed();