Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / toolbar / browser_actions_container.cc
1 // Copyright 2013 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.
4
5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/stl_util.h"
9 #include "chrome/browser/extensions/extension_action_manager.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/extensions/extension_view_host.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/view_ids.h"
18 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
19 #include "chrome/browser/ui/views/extensions/extension_popup.h"
20 #include "chrome/browser/ui/views/frame/browser_view.h"
21 #include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
22 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
23 #include "chrome/common/extensions/command.h"
24 #include "chrome/grit/generated_resources.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/runtime_data.h"
28 #include "extensions/common/feature_switch.h"
29 #include "grit/theme_resources.h"
30 #include "third_party/skia/include/core/SkColor.h"
31 #include "ui/accessibility/ax_view_state.h"
32 #include "ui/base/dragdrop/drag_utils.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/nine_image_painter_factory.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/base/theme_provider.h"
37 #include "ui/gfx/animation/slide_animation.h"
38 #include "ui/gfx/canvas.h"
39 #include "ui/gfx/geometry/rect.h"
40 #include "ui/resources/grit/ui_resources.h"
41 #include "ui/views/controls/button/label_button_border.h"
42 #include "ui/views/controls/button/menu_button.h"
43 #include "ui/views/controls/resize_area.h"
44 #include "ui/views/metrics.h"
45 #include "ui/views/painter.h"
46 #include "ui/views/widget/widget.h"
47
48 using extensions::Extension;
49
50 namespace {
51
52 // Horizontal spacing before the chevron (if visible).
53 const int kChevronSpacing = ToolbarView::kStandardSpacing - 2;
54
55 // A version of MenuButton with almost empty insets to fit properly on the
56 // toolbar.
57 class ChevronMenuButton : public views::MenuButton {
58  public:
59   ChevronMenuButton(views::ButtonListener* listener,
60                     const base::string16& text,
61                     views::MenuButtonListener* menu_button_listener,
62                     bool show_menu_marker)
63       : views::MenuButton(listener,
64                           text,
65                           menu_button_listener,
66                           show_menu_marker) {
67   }
68
69   virtual ~ChevronMenuButton() {}
70
71   virtual scoped_ptr<views::LabelButtonBorder> CreateDefaultBorder() const
72       OVERRIDE {
73     // The chevron resource was designed to not have any insets.
74     scoped_ptr<views::LabelButtonBorder> border =
75         views::MenuButton::CreateDefaultBorder();
76     border->set_insets(gfx::Insets());
77     return border.Pass();
78   }
79
80  private:
81   DISALLOW_COPY_AND_ASSIGN(ChevronMenuButton);
82 };
83
84 }  // namespace
85
86 ////////////////////////////////////////////////////////////////////////////////
87 // BrowserActionsContainer::DropPosition
88
89 struct BrowserActionsContainer::DropPosition {
90   DropPosition(size_t row, size_t icon_in_row);
91
92   // The (0-indexed) row into which the action will be dropped.
93   size_t row;
94
95   // The (0-indexed) icon in the row before the action will be dropped.
96   size_t icon_in_row;
97 };
98
99 BrowserActionsContainer::DropPosition::DropPosition(
100     size_t row, size_t icon_in_row)
101     : row(row), icon_in_row(icon_in_row) {
102 }
103
104 ////////////////////////////////////////////////////////////////////////////////
105 // BrowserActionsContainer
106
107 // static
108 int BrowserActionsContainer::icons_per_overflow_menu_row_ = 1;
109
110 // static
111 const int BrowserActionsContainer::kItemSpacing = ToolbarView::kStandardSpacing;
112
113 // static
114 bool BrowserActionsContainer::disable_animations_during_testing_ = false;
115
116 BrowserActionsContainer::BrowserActionsContainer(
117     Browser* browser,
118     View* owner_view,
119     BrowserActionsContainer* main_container)
120     : initialized_(false),
121       profile_(browser->profile()),
122       browser_(browser),
123       owner_view_(owner_view),
124       main_container_(main_container),
125       popup_owner_(NULL),
126       model_(NULL),
127       container_width_(0),
128       resize_area_(NULL),
129       chevron_(NULL),
130       overflow_menu_(NULL),
131       suppress_chevron_(false),
132       resize_amount_(0),
133       animation_target_size_(0),
134       show_menu_task_factory_(this) {
135   set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
136
137   model_ = extensions::ExtensionToolbarModel::Get(browser->profile());
138   if (model_)
139     model_->AddObserver(this);
140
141   bool overflow_experiment =
142       extensions::FeatureSwitch::extension_action_redesign()->IsEnabled();
143   DCHECK(!in_overflow_mode() || overflow_experiment);
144
145   if (!in_overflow_mode()) {
146     extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
147         browser->profile(),
148         owner_view->GetFocusManager(),
149         extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
150         this));
151
152     resize_animation_.reset(new gfx::SlideAnimation(this));
153     resize_area_ = new views::ResizeArea(this);
154     AddChildView(resize_area_);
155
156     // 'Main' mode doesn't need a chevron overflow when overflow is shown inside
157     // the Chrome menu.
158     if (!overflow_experiment) {
159       chevron_ = new ChevronMenuButton(NULL, base::string16(), this, false);
160       chevron_->EnableCanvasFlippingForRTLUI(true);
161       chevron_->SetAccessibleName(
162           l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
163       chevron_->SetVisible(false);
164       AddChildView(chevron_);
165     }
166   }
167 }
168
169 BrowserActionsContainer::~BrowserActionsContainer() {
170   FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
171                     observers_,
172                     OnBrowserActionsContainerDestroyed());
173
174   if (overflow_menu_)
175     overflow_menu_->set_observer(NULL);
176   if (model_)
177     model_->RemoveObserver(this);
178   StopShowFolderDropMenuTimer();
179   HideActivePopup();
180   DeleteBrowserActionViews();
181 }
182
183 void BrowserActionsContainer::Init() {
184   LoadImages();
185
186   // We wait to set the container width until now so that the chevron images
187   // will be loaded.  The width calculation needs to know the chevron size.
188   if (model_ && model_->extensions_initialized()) {
189     container_width_ = GetPreferredWidth();
190     SetChevronVisibility();
191   }
192
193   initialized_ = true;
194 }
195
196 BrowserActionView* BrowserActionsContainer::GetViewForExtension(
197     const Extension* extension) {
198   for (BrowserActionViews::iterator view = browser_action_views_.begin();
199        view != browser_action_views_.end(); ++view) {
200     if ((*view)->extension() == extension)
201       return *view;
202   }
203   return NULL;
204 }
205
206 void BrowserActionsContainer::RefreshBrowserActionViews() {
207   for (size_t i = 0; i < browser_action_views_.size(); ++i)
208     browser_action_views_[i]->UpdateState();
209 }
210
211 void BrowserActionsContainer::CreateBrowserActionViews() {
212   DCHECK(browser_action_views_.empty());
213   if (!model_)
214     return;
215
216   extensions::ExtensionActionManager* action_manager =
217       extensions::ExtensionActionManager::Get(profile_);
218   const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
219   for (extensions::ExtensionList::const_iterator i(toolbar_items.begin());
220        i != toolbar_items.end(); ++i) {
221     if (!ShouldDisplayBrowserAction(i->get()))
222       continue;
223
224     BrowserActionView* view =
225         new BrowserActionView(i->get(),
226                               action_manager->GetExtensionAction(**i),
227                               browser_,
228                               this);
229     browser_action_views_.push_back(view);
230     AddChildView(view);
231   }
232 }
233
234 void BrowserActionsContainer::DeleteBrowserActionViews() {
235   HideActivePopup();
236   if (overflow_menu_)
237     overflow_menu_->NotifyBrowserActionViewsDeleting();
238   STLDeleteElements(&browser_action_views_);
239 }
240
241 size_t BrowserActionsContainer::VisibleBrowserActions() const {
242   size_t visible_actions = 0;
243   for (size_t i = 0; i < browser_action_views_.size(); ++i) {
244     if (browser_action_views_[i]->visible())
245       ++visible_actions;
246   }
247   return visible_actions;
248 }
249
250 size_t BrowserActionsContainer::VisibleBrowserActionsAfterAnimation() const {
251   if (!animating())
252     return VisibleBrowserActions();
253
254   return WidthToIconCount(animation_target_size_);
255 }
256
257 void BrowserActionsContainer::ExecuteExtensionCommand(
258     const extensions::Extension* extension,
259     const extensions::Command& command) {
260   // Global commands are handled by the ExtensionCommandsGlobalRegistry
261   // instance.
262   DCHECK(!command.global());
263   extension_keybinding_registry_->ExecuteCommand(extension->id(),
264                                                  command.accelerator());
265 }
266
267 void BrowserActionsContainer::NotifyActionMovedToOverflow() {
268   // When an action is moved to overflow, we shrink the size of the container
269   // by 1.
270   if (!profile_->IsOffTheRecord()) {
271     int icon_count = model_->GetVisibleIconCount();
272     // Since this happens when an icon moves from the main bar to overflow, we
273     // can't possibly have had no visible icons on the main bar.
274     DCHECK_NE(0, icon_count);
275     if (icon_count == -1)
276       icon_count = browser_action_views_.size();
277     model_->SetVisibleIconCount(icon_count - 1);
278   }
279   Animate(gfx::Tween::EASE_OUT,
280           VisibleBrowserActionsAfterAnimation() - 1);
281 }
282
283 bool BrowserActionsContainer::ShownInsideMenu() const {
284   return in_overflow_mode();
285 }
286
287 void BrowserActionsContainer::OnBrowserActionViewDragDone() {
288   ToolbarVisibleCountChanged();
289   FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
290                     observers_,
291                     OnBrowserActionDragDone());
292 }
293
294 views::MenuButton* BrowserActionsContainer::GetOverflowReferenceView() {
295   // With traditional overflow, the reference is the chevron. With the
296   // redesign, we use the wrench menu instead.
297   return chevron_ ?
298       chevron_ :
299       BrowserView::GetBrowserViewForBrowser(browser_)->toolbar()->app_menu();
300 }
301
302 void BrowserActionsContainer::SetPopupOwner(BrowserActionView* popup_owner) {
303   // We should never be setting a popup owner when one already exists, and
304   // never unsetting one when one wasn't set.
305   DCHECK((!popup_owner_ && popup_owner) ||
306          (popup_owner_ && !popup_owner));
307   popup_owner_ = popup_owner;
308 }
309
310 void BrowserActionsContainer::HideActivePopup() {
311   if (popup_owner_)
312     popup_owner_->view_controller()->HidePopup();
313 }
314
315 BrowserActionView* BrowserActionsContainer::GetMainViewForExtension(
316     const Extension* extension) {
317   return in_overflow_mode() ?
318       main_container_->GetViewForExtension(extension) :
319       GetViewForExtension(extension);
320 }
321
322 void BrowserActionsContainer::AddObserver(
323     BrowserActionsContainerObserver* observer) {
324   observers_.AddObserver(observer);
325 }
326
327 void BrowserActionsContainer::RemoveObserver(
328     BrowserActionsContainerObserver* observer) {
329   observers_.RemoveObserver(observer);
330 }
331
332 gfx::Size BrowserActionsContainer::GetPreferredSize() const {
333   if (in_overflow_mode()) {
334     int icon_count = GetIconCount();
335     // In overflow, we always have a preferred size of a full row (even if we
336     // don't use it), and always of at least one row. The parent may decide to
337     // show us even when empty, e.g. as a drag target for dragging in icons from
338     // the main container.
339     int row_count =
340         ((std::max(0, icon_count - 1)) / icons_per_overflow_menu_row_) + 1;
341     return gfx::Size(
342         IconCountToWidth(icons_per_overflow_menu_row_, false),
343         row_count * IconHeight());
344   }
345
346   // If there are no actions to show, then don't show the container at all.
347   if (browser_action_views_.empty())
348     return gfx::Size();
349
350   // We calculate the size of the view by taking the current width and
351   // subtracting resize_amount_ (the latter represents how far the user is
352   // resizing the view or, if animating the snapping, how far to animate it).
353   // But we also clamp it to a minimum size and the maximum size, so that the
354   // container can never shrink too far or take up more space than it needs.
355   // In other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX).
356   int preferred_width = std::min(
357       std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_),
358       IconCountToWidth(-1, false));
359   return gfx::Size(preferred_width, IconHeight());
360 }
361
362 int BrowserActionsContainer::GetHeightForWidth(int width) const {
363   if (in_overflow_mode())
364     icons_per_overflow_menu_row_ = (width - kItemSpacing) / IconWidth(true);
365   return GetPreferredSize().height();
366 }
367
368 gfx::Size BrowserActionsContainer::GetMinimumSize() const {
369   int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1, false));
370   return gfx::Size(min_width, IconHeight());
371 }
372
373 void BrowserActionsContainer::Layout() {
374   if (browser_action_views_.empty()) {
375     SetVisible(false);
376     return;
377   }
378
379   SetVisible(true);
380   if (resize_area_)
381     resize_area_->SetBounds(0, 0, kItemSpacing, height());
382
383   // If the icons don't all fit, show the chevron (unless suppressed).
384   int max_x = GetPreferredSize().width();
385   if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_ && chevron_) {
386     chevron_->SetVisible(true);
387     gfx::Size chevron_size(chevron_->GetPreferredSize());
388     max_x -=
389         ToolbarView::kStandardSpacing + chevron_size.width() + kChevronSpacing;
390     chevron_->SetBounds(
391         width() - ToolbarView::kStandardSpacing - chevron_size.width(),
392         0,
393         chevron_size.width(),
394         chevron_size.height());
395   } else if (chevron_) {
396     chevron_->SetVisible(false);
397   }
398
399   // Now draw the icons for the browser actions in the available space.
400   int icon_width = IconWidth(false);
401   if (in_overflow_mode()) {
402     for (size_t i = 0;
403          i < main_container_->VisibleBrowserActionsAfterAnimation(); ++i) {
404       // Ensure that any browser actions shown in the main view are hidden in
405       // the overflow view.
406       browser_action_views_[i]->SetVisible(false);
407     }
408
409     for (size_t i = main_container_->VisibleBrowserActionsAfterAnimation();
410          i < browser_action_views_.size(); ++i) {
411       BrowserActionView* view = browser_action_views_[i];
412       size_t index = i - main_container_->VisibleBrowserActionsAfterAnimation();
413       int row_index = static_cast<int>(index) / icons_per_overflow_menu_row_;
414       int x = kItemSpacing + (index * IconWidth(true)) -
415           (row_index * IconWidth(true) * icons_per_overflow_menu_row_);
416       gfx::Rect rect_bounds(
417           x, IconHeight() * row_index, icon_width, IconHeight());
418       view->SetBoundsRect(rect_bounds);
419       view->SetVisible(true);
420     }
421   } else {
422     for (BrowserActionViews::const_iterator it = browser_action_views_.begin();
423          it < browser_action_views_.end(); ++it) {
424       BrowserActionView* view = *it;
425       int x = ToolbarView::kStandardSpacing +
426           ((it - browser_action_views_.begin()) * IconWidth(true));
427       view->SetVisible(x + icon_width <= max_x);
428       if (view->visible())
429         view->SetBounds(x, 0, icon_width, IconHeight());
430     }
431   }
432 }
433
434 bool BrowserActionsContainer::GetDropFormats(
435     int* formats,
436     std::set<OSExchangeData::CustomFormat>* custom_formats) {
437   return BrowserActionDragData::GetDropFormats(custom_formats);
438 }
439
440 bool BrowserActionsContainer::AreDropTypesRequired() {
441   return BrowserActionDragData::AreDropTypesRequired();
442 }
443
444 bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
445   return BrowserActionDragData::CanDrop(data, profile_);
446 }
447
448 int BrowserActionsContainer::OnDragUpdated(
449     const ui::DropTargetEvent& event) {
450   // First check if we are above the chevron (overflow) menu.
451   if (chevron_ && GetEventHandlerForPoint(event.location()) == chevron_) {
452     if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
453       StartShowFolderDropMenuTimer();
454     return ui::DragDropTypes::DRAG_MOVE;
455   }
456   StopShowFolderDropMenuTimer();
457
458   size_t row_index = 0;
459   size_t before_icon_in_row = 0;
460   // If there are no visible browser actions (such as when dragging an icon to
461   // an empty overflow/main container), then 0, 0 for row, column is correct.
462   if (VisibleBrowserActions() != 0) {
463     // Figure out where to display the indicator. This is a complex calculation:
464
465     // First, we subtract out the padding to the left of the icon area, which is
466     // ToolbarView::kStandardSpacing. If we're right-to-left, we also mirror the
467     // event.x() so that our calculations are consistent with left-to-right.
468     int offset_into_icon_area =
469         GetMirroredXInView(event.x()) - ToolbarView::kStandardSpacing;
470
471     // Next, figure out what row we're on. This only matters for overflow mode,
472     // but the calculation is the same for both.
473     row_index = event.y() / IconHeight();
474
475     // Sanity check - we should never be on a different row in the main
476     // container.
477     DCHECK(in_overflow_mode() || row_index == 0);
478
479     // Next, we determine which icon to place the indicator in front of. We want
480     // to place the indicator in front of icon n when the cursor is between the
481     // midpoints of icons (n - 1) and n.  To do this we take the offset into the
482     // icon area and transform it as follows:
483     //
484     // Real icon area:
485     //   0   a     *  b        c
486     //   |   |        |        |
487     //   |[IC|ON]  [IC|ON]  [IC|ON]
488     // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
489     // Here the "*" represents the offset into the icon area, and since it's
490     // between a and b, we want to return "1".
491     //
492     // Transformed "icon area":
493     //   0        a     *  b        c
494     //   |        |        |        |
495     //   |[ICON]  |[ICON]  |[ICON]  |
496     // If we shift both our offset and our divider points later by half an icon
497     // plus one spacing unit, then it becomes very easy to calculate how many
498     // divider points we've passed, because they're the multiples of "one icon
499     // plus padding".
500     int before_icon_unclamped =
501         (offset_into_icon_area + (IconWidth(false) / 2) +
502         kItemSpacing) / IconWidth(true);
503
504     // We need to figure out how many icons are visible on the relevant row.
505     // In the main container, this will just be the visible actions.
506     int visible_icons_on_row = VisibleBrowserActionsAfterAnimation();
507     if (in_overflow_mode()) {
508       // If this is the final row of the overflow, then this is the remainder of
509       // visible icons. Otherwise, it's a full row (kIconsPerRow).
510       visible_icons_on_row =
511           row_index ==
512               static_cast<size_t>(visible_icons_on_row /
513                                   icons_per_overflow_menu_row_) ?
514                   visible_icons_on_row % icons_per_overflow_menu_row_ :
515                   icons_per_overflow_menu_row_;
516     }
517
518     // Because the user can drag outside the container bounds, we need to clamp
519     // to the valid range. Note that the maximum allowable value is (num icons),
520     // not (num icons - 1), because we represent the indicator being past the
521     // last icon as being "before the (last + 1) icon".
522     before_icon_in_row =
523         std::min(std::max(before_icon_unclamped, 0), visible_icons_on_row);
524   }
525
526   if (!drop_position_.get() ||
527       !(drop_position_->row == row_index &&
528         drop_position_->icon_in_row == before_icon_in_row)) {
529     drop_position_.reset(new DropPosition(row_index, before_icon_in_row));
530     SchedulePaint();
531   }
532
533   return ui::DragDropTypes::DRAG_MOVE;
534 }
535
536 void BrowserActionsContainer::OnDragExited() {
537   StopShowFolderDropMenuTimer();
538   drop_position_.reset();
539   SchedulePaint();
540 }
541
542 int BrowserActionsContainer::OnPerformDrop(
543     const ui::DropTargetEvent& event) {
544   BrowserActionDragData data;
545   if (!data.Read(event.data()))
546     return ui::DragDropTypes::DRAG_NONE;
547
548   // Make sure we have the same view as we started with.
549   DCHECK_EQ(browser_action_views_[data.index()]->extension()->id(),
550             data.id());
551   DCHECK(model_);
552
553   size_t i = drop_position_->row * icons_per_overflow_menu_row_ +
554              drop_position_->icon_in_row;
555   if (in_overflow_mode())
556     i += main_container_->VisibleBrowserActionsAfterAnimation();
557   // |i| now points to the item to the right of the drop indicator*, which is
558   // correct when dragging an icon to the left. When dragging to the right,
559   // however, we want the icon being dragged to get the index of the item to
560   // the left of the drop indicator, so we subtract one.
561   // * Well, it can also point to the end, but not when dragging to the left. :)
562   if (i > data.index())
563     --i;
564
565   if (profile_->IsOffTheRecord())
566     i = model_->IncognitoIndexToOriginal(i);
567
568   // If this was a drag between containers, we will have to adjust the number of
569   // visible icons.
570   bool drag_between_containers =
571       !browser_action_views_[data.index()]->visible();
572   model_->MoveExtensionIcon(
573       browser_action_views_[data.index()]->extension(), i);
574
575   if (drag_between_containers) {
576     // Add one for the dropped icon.
577     size_t new_icon_count = VisibleBrowserActionsAfterAnimation() + 1;
578
579     // Let the main container update the model.
580     if (in_overflow_mode())
581       main_container_->NotifyActionMovedToOverflow();
582     else if (!profile_->IsOffTheRecord())  // This is the main container.
583       model_->SetVisibleIconCount(model_->GetVisibleIconCount() + 1);
584
585     // The size changed, so we need to animate.
586     Animate(gfx::Tween::EASE_OUT, new_icon_count);
587   }
588
589   OnDragExited();  // Perform clean up after dragging.
590   return ui::DragDropTypes::DRAG_MOVE;
591 }
592
593 void BrowserActionsContainer::GetAccessibleState(
594     ui::AXViewState* state) {
595   state->role = ui::AX_ROLE_GROUP;
596   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
597 }
598
599 void BrowserActionsContainer::OnMenuButtonClicked(views::View* source,
600                                                   const gfx::Point& point) {
601   if (source == chevron_) {
602     overflow_menu_ =
603         new BrowserActionOverflowMenuController(this,
604                                                 browser_,
605                                                 chevron_,
606                                                 browser_action_views_,
607                                                 VisibleBrowserActions(),
608                                                 false);
609     overflow_menu_->set_observer(this);
610     overflow_menu_->RunMenu(GetWidget());
611   }
612 }
613
614 void BrowserActionsContainer::WriteDragDataForView(View* sender,
615                                                    const gfx::Point& press_pt,
616                                                    OSExchangeData* data) {
617   DCHECK(data);
618
619   for (size_t i = 0; i < browser_action_views_.size(); ++i) {
620     BrowserActionView* view = browser_action_views_[i];
621     if (view == sender) {
622       // Set the dragging image for the icon.
623       gfx::ImageSkia badge(view->GetIconWithBadge());
624       drag_utils::SetDragImageOnDataObject(badge,
625                                            press_pt.OffsetFromOrigin(),
626                                            data);
627
628       // Fill in the remaining info.
629       BrowserActionDragData drag_data(view->extension()->id(), i);
630       drag_data.Write(profile_, data);
631       break;
632     }
633   }
634 }
635
636 int BrowserActionsContainer::GetDragOperationsForView(View* sender,
637                                                       const gfx::Point& p) {
638   return ui::DragDropTypes::DRAG_MOVE;
639 }
640
641 bool BrowserActionsContainer::CanStartDragForView(View* sender,
642                                                   const gfx::Point& press_pt,
643                                                   const gfx::Point& p) {
644   // We don't allow dragging while we're highlighting.
645   return !model_->is_highlighting();
646 }
647
648 void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
649   if (!done_resizing) {
650     resize_amount_ = resize_amount;
651     OnBrowserActionVisibilityChanged();
652     return;
653   }
654
655   // Up until now we've only been modifying the resize_amount, but now it is
656   // time to set the container size to the size we have resized to, and then
657   // animate to the nearest icon count size if necessary (which may be 0).
658   int max_width = IconCountToWidth(-1, false);
659   container_width_ =
660       std::min(std::max(0, container_width_ - resize_amount), max_width);
661
662   // Save off the desired number of visible icons.  We do this now instead of at
663   // the end of the animation so that even if the browser is shut down while
664   // animating, the right value will be restored on next run.
665   // NOTE: Don't save the icon count in incognito because there may be fewer
666   // icons in that mode. The result is that the container in a normal window is
667   // always at least as wide as in an incognito window.
668   int visible_icons = WidthToIconCount(container_width_);
669   if (!profile_->IsOffTheRecord())
670     model_->SetVisibleIconCount(visible_icons);
671   Animate(gfx::Tween::EASE_OUT, visible_icons);
672 }
673
674 void BrowserActionsContainer::AnimationProgressed(
675     const gfx::Animation* animation) {
676   DCHECK_EQ(resize_animation_.get(), animation);
677   resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
678       (container_width_ - animation_target_size_));
679   OnBrowserActionVisibilityChanged();
680 }
681
682 void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
683   container_width_ = animation_target_size_;
684   animation_target_size_ = 0;
685   resize_amount_ = 0;
686   suppress_chevron_ = false;
687   SetChevronVisibility();
688   OnBrowserActionVisibilityChanged();
689
690   FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
691                     observers_,
692                     OnBrowserActionsContainerAnimationEnded());
693 }
694
695 void BrowserActionsContainer::NotifyMenuDeleted(
696     BrowserActionOverflowMenuController* controller) {
697   DCHECK_EQ(overflow_menu_, controller);
698   overflow_menu_ = NULL;
699 }
700
701 content::WebContents* BrowserActionsContainer::GetCurrentWebContents() {
702   return browser_->tab_strip_model()->GetActiveWebContents();
703 }
704
705 extensions::ActiveTabPermissionGranter*
706     BrowserActionsContainer::GetActiveTabPermissionGranter() {
707   content::WebContents* web_contents =
708       browser_->tab_strip_model()->GetActiveWebContents();
709   if (!web_contents)
710     return NULL;
711   return extensions::TabHelper::FromWebContents(web_contents)->
712       active_tab_permission_granter();
713 }
714
715 ExtensionPopup* BrowserActionsContainer::TestGetPopup() {
716   return popup_owner_ ? popup_owner_->view_controller()->popup() : NULL;
717 }
718
719 void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
720   model_->SetVisibleIconCountForTest(icons);
721 }
722
723 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
724   // If the views haven't been initialized yet, wait for the next call to
725   // paint (one will be triggered by entering highlight mode).
726   if (model_->is_highlighting() && !browser_action_views_.empty()) {
727     views::Painter::PaintPainterAt(
728         canvas, highlight_painter_.get(), GetLocalBounds());
729   }
730
731   // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
732   // dragging (like we do for tab dragging).
733   if (drop_position_.get()) {
734     // The two-pixel width drop indicator.
735     static const int kDropIndicatorWidth = 2;
736
737     // Convert back to a pixel offset into the container.  First find the X
738     // coordinate of the drop icon.
739     int drop_icon_x = ToolbarView::kStandardSpacing +
740         (drop_position_->icon_in_row * IconWidth(true));
741     // Next, find the space before the drop icon. This will either be
742     // kItemSpacing or ToolbarView::kStandardSpacing, depending on whether this
743     // is the first icon.
744     // NOTE: Right now, these are the same. But let's do this right for if they
745     // ever aren't.
746     int space_before_drop_icon = drop_position_->icon_in_row == 0 ?
747         ToolbarView::kStandardSpacing : kItemSpacing;
748     // Now place the drop indicator halfway between this and the end of the
749     // previous icon.  If there is an odd amount of available space between the
750     // two icons (or the icon and the address bar) after subtracting the drop
751     // indicator width, this calculation puts the extra pixel on the left side
752     // of the indicator, since when the indicator is between the address bar and
753     // the first icon, it looks better closer to the icon.
754     int drop_indicator_x = drop_icon_x -
755         ((space_before_drop_icon + kDropIndicatorWidth) / 2);
756     int row_height = IconHeight();
757     int drop_indicator_y = row_height * drop_position_->row;
758     gfx::Rect indicator_bounds(drop_indicator_x,
759                                drop_indicator_y,
760                                kDropIndicatorWidth,
761                                row_height);
762     indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
763
764     // Color of the drop indicator.
765     static const SkColor kDropIndicatorColor = SK_ColorBLACK;
766     canvas->FillRect(indicator_bounds, kDropIndicatorColor);
767   }
768 }
769
770 void BrowserActionsContainer::OnThemeChanged() {
771   LoadImages();
772 }
773
774 void BrowserActionsContainer::ViewHierarchyChanged(
775     const ViewHierarchyChangedDetails& details) {
776   // No extensions (e.g., incognito).
777   if (!model_)
778     return;
779
780   if (details.is_add && details.child == this) {
781     // Initial toolbar button creation and placement in the widget hierarchy.
782     // We do this here instead of in the constructor because AddBrowserAction
783     // calls Layout on the Toolbar, which needs this object to be constructed
784     // before its Layout function is called.
785     CreateBrowserActionViews();
786   }
787 }
788
789 // static
790 int BrowserActionsContainer::IconWidth(bool include_padding) {
791   static bool initialized = false;
792   static int icon_width = 0;
793   if (!initialized) {
794     initialized = true;
795     icon_width = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
796         IDR_BROWSER_ACTION)->width();
797   }
798   return icon_width + (include_padding ? kItemSpacing : 0);
799 }
800
801 // static
802 int BrowserActionsContainer::IconHeight() {
803   static bool initialized = false;
804   static int icon_height = 0;
805   if (!initialized) {
806     initialized = true;
807     icon_height = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
808         IDR_BROWSER_ACTION)->height();
809   }
810   return icon_height;
811 }
812
813 void BrowserActionsContainer::ToolbarExtensionAdded(const Extension* extension,
814                                                     int index) {
815 #if defined(DEBUG)
816   for (size_t i = 0; i < browser_action_views_.size(); ++i) {
817     DCHECK(browser_action_views_[i]->extension() != extension) <<
818            "Asked to add a browser action view for an extension that already "
819            "exists.";
820   }
821 #endif
822   CloseOverflowMenu();
823
824   if (!ShouldDisplayBrowserAction(extension))
825     return;
826
827   // Add the new browser action to the vector and the view hierarchy.
828   if (profile_->IsOffTheRecord())
829     index = model_->OriginalIndexToIncognito(index);
830   BrowserActionView* view =
831       new BrowserActionView(extension,
832                             extensions::ExtensionActionManager::Get(profile_)->
833                                 GetExtensionAction(*extension),
834                             browser_,
835                             this);
836   browser_action_views_.insert(browser_action_views_.begin() + index, view);
837   AddChildViewAt(view, index);
838
839   // If we are still initializing the container, don't bother animating.
840   if (!model_->extensions_initialized())
841     return;
842
843   // If this is just an upgrade, then don't worry about resizing.
844   if (!extensions::ExtensionSystem::Get(profile_)->runtime_data()->
845           IsBeingUpgraded(extension)) {
846     // We need to resize if either:
847     // - The container is set to display all icons (visible count = -1), or
848     // - The container will need to expand to include the chevron. This can
849     //   happen when the container is set to display <n> icons, where <n> is
850     //   the number of icons before the new icon. With the new icon, the chevron
851     //   will need to be displayed.
852     int model_icon_count = model_->GetVisibleIconCount();
853     if (model_icon_count == -1 ||
854         (static_cast<size_t>(model_icon_count) < browser_action_views_.size() &&
855          (chevron_ && !chevron_->visible()))) {
856       suppress_chevron_ = true;
857       Animate(gfx::Tween::LINEAR, GetIconCount());
858       return;
859     }
860   }
861
862   // Otherwise, we don't have to resize, so just redraw the (possibly modified)
863   // visible icon set.
864   OnBrowserActionVisibilityChanged();
865 }
866
867 void BrowserActionsContainer::ToolbarExtensionRemoved(
868     const Extension* extension) {
869   CloseOverflowMenu();
870
871   size_t visible_actions = VisibleBrowserActionsAfterAnimation();
872   for (BrowserActionViews::iterator i(browser_action_views_.begin());
873        i != browser_action_views_.end(); ++i) {
874     if ((*i)->extension() == extension) {
875       delete *i;
876       browser_action_views_.erase(i);
877
878       // If the extension is being upgraded we don't want the bar to shrink
879       // because the icon is just going to get re-added to the same location.
880       if (extensions::ExtensionSystem::Get(profile_)->runtime_data()->
881               IsBeingUpgraded(extension))
882         return;
883
884       if (browser_action_views_.size() > visible_actions) {
885         // If we have more icons than we can show, then we must not be changing
886         // the container size (since we either removed an icon from the main
887         // area and one from the overflow list will have shifted in, or we
888         // removed an entry directly from the overflow list).
889         OnBrowserActionVisibilityChanged();
890       } else {
891         // Either we went from overflow to no-overflow, or we shrunk the no-
892         // overflow container by 1.  Either way the size changed, so animate.
893         if (chevron_)
894           chevron_->SetVisible(false);
895         Animate(gfx::Tween::EASE_OUT, browser_action_views_.size());
896       }
897       return;  // We have found the action to remove, bail out.
898     }
899   }
900 }
901
902 void BrowserActionsContainer::ToolbarExtensionMoved(const Extension* extension,
903                                                     int index) {
904   if (!ShouldDisplayBrowserAction(extension))
905     return;
906
907   if (profile_->IsOffTheRecord())
908     index = model_->OriginalIndexToIncognito(index);
909
910   DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size()));
911
912   BrowserActionViews::iterator iter = browser_action_views_.begin();
913   int old_index = 0;
914   while (iter != browser_action_views_.end() &&
915          (*iter)->extension() != extension) {
916     ++iter;
917     ++old_index;
918   }
919
920   DCHECK(iter != browser_action_views_.end());
921   if (old_index == index)
922     return;  // Already in place.
923
924   BrowserActionView* moved_view = *iter;
925   browser_action_views_.erase(iter);
926   browser_action_views_.insert(
927       browser_action_views_.begin() + index, moved_view);
928
929   Layout();
930   SchedulePaint();
931 }
932
933 void BrowserActionsContainer::ToolbarExtensionUpdated(
934     const Extension* extension) {
935   BrowserActionView* view = GetViewForExtension(extension);
936   if (view)
937     view->UpdateState();
938 }
939
940 bool BrowserActionsContainer::ShowExtensionActionPopup(
941     const Extension* extension,
942     bool grant_active_tab) {
943   // Don't override another popup, and only show in the active window.
944   if (popup_owner_ || !browser_->window()->IsActive())
945     return false;
946
947   BrowserActionView* view = GetViewForExtension(extension);
948   return view && view->view_controller()->ExecuteAction(ExtensionPopup::SHOW,
949                                                         grant_active_tab);
950 }
951
952 void BrowserActionsContainer::ToolbarVisibleCountChanged() {
953   if (GetPreferredWidth() != container_width_)
954     Animate(gfx::Tween::EASE_OUT, GetIconCount());
955 }
956
957 void BrowserActionsContainer::ToolbarHighlightModeChanged(
958     bool is_highlighting) {
959   // The visual highlighting is done in OnPaint(). It's a bit of a pain that
960   // we delete and recreate everything here, but given everything else going on
961   // (the lack of highlight, n more extensions appearing, etc), it's not worth
962   // the extra complexity to create and insert only the new extensions.
963   DeleteBrowserActionViews();
964   CreateBrowserActionViews();
965   Animate(gfx::Tween::LINEAR, GetIconCount());
966 }
967
968 Browser* BrowserActionsContainer::GetBrowser() {
969   return browser_;
970 }
971
972 void BrowserActionsContainer::LoadImages() {
973   ui::ThemeProvider* tp = GetThemeProvider();
974   if (!tp || !chevron_)
975     return;
976
977   chevron_->SetImage(views::Button::STATE_NORMAL,
978                      *tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
979
980   const int kImages[] = IMAGE_GRID(IDR_DEVELOPER_MODE_HIGHLIGHT);
981   highlight_painter_.reset(views::Painter::CreateImageGridPainter(kImages));
982 }
983
984 void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
985   SetVisible(!browser_action_views_.empty());
986   if (owner_view_) {
987     owner_view_->Layout();
988     owner_view_->SchedulePaint();
989   } else {
990     // In overflow mode, we don't have an owner view, but we still have to
991     // update ourselves.
992     Layout();
993     SchedulePaint();
994   }
995 }
996
997 int BrowserActionsContainer::GetPreferredWidth() {
998   size_t visible_actions = GetIconCount();
999   return IconCountToWidth(
1000       visible_actions,
1001       chevron_ && visible_actions < browser_action_views_.size());
1002 }
1003
1004 void BrowserActionsContainer::SetChevronVisibility() {
1005   if (chevron_) {
1006     chevron_->SetVisible(
1007         VisibleBrowserActionsAfterAnimation() < browser_action_views_.size());
1008   }
1009 }
1010
1011 void BrowserActionsContainer::CloseOverflowMenu() {
1012   if (overflow_menu_)
1013     overflow_menu_->CancelMenu();
1014 }
1015
1016 void BrowserActionsContainer::StopShowFolderDropMenuTimer() {
1017   show_menu_task_factory_.InvalidateWeakPtrs();
1018 }
1019
1020 void BrowserActionsContainer::StartShowFolderDropMenuTimer() {
1021   base::MessageLoop::current()->PostDelayedTask(
1022       FROM_HERE,
1023       base::Bind(&BrowserActionsContainer::ShowDropFolder,
1024                  show_menu_task_factory_.GetWeakPtr()),
1025       base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1026 }
1027
1028 void BrowserActionsContainer::ShowDropFolder() {
1029   DCHECK(!overflow_menu_);
1030   overflow_menu_ =
1031       new BrowserActionOverflowMenuController(this,
1032                                               browser_,
1033                                               chevron_,
1034                                               browser_action_views_,
1035                                               VisibleBrowserActions(),
1036                                               true);
1037   overflow_menu_->set_observer(this);
1038   overflow_menu_->RunMenu(GetWidget());
1039 }
1040
1041 int BrowserActionsContainer::IconCountToWidth(int icons,
1042                                               bool display_chevron) const {
1043   if (icons < 0)
1044     icons = browser_action_views_.size();
1045   if ((icons == 0) && !display_chevron)
1046     return ToolbarView::kStandardSpacing;
1047   int icons_size =
1048       (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing);
1049   int chevron_size = chevron_ && display_chevron ?
1050       (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0;
1051   // In overflow mode, our padding is to use item spacing on either end (just so
1052   // we can see the drop indicator). Otherwise we use the standard toolbar
1053   // spacing.
1054   // Note: These are actually the same thing, but, on the offchance one
1055   // changes, let's get it right.
1056   int padding =
1057       2 * (in_overflow_mode() ? kItemSpacing : ToolbarView::kStandardSpacing);
1058   return icons_size + chevron_size + padding;
1059 }
1060
1061 size_t BrowserActionsContainer::WidthToIconCount(int pixels) const {
1062   // Check for widths large enough to show the entire icon set.
1063   if (pixels >= IconCountToWidth(-1, false))
1064     return browser_action_views_.size();
1065
1066   // We reserve space for the padding on either side of the toolbar...
1067   int available_space = pixels - (ToolbarView::kStandardSpacing * 2);
1068   // ... and, if the chevron is enabled, the chevron.
1069   if (chevron_)
1070     available_space -= (chevron_->GetPreferredSize().width() + kChevronSpacing);
1071
1072   // Now we add an extra between-item padding value so the space can be divided
1073   // evenly by (size of icon with padding).
1074   return static_cast<size_t>(
1075       std::max(0, available_space + kItemSpacing) / IconWidth(true));
1076 }
1077
1078 int BrowserActionsContainer::MinimumNonemptyWidth() const {
1079   if (!chevron_)
1080     return ToolbarView::kStandardSpacing;
1081   return (ToolbarView::kStandardSpacing * 2) + kChevronSpacing +
1082       chevron_->GetPreferredSize().width();
1083 }
1084
1085 void BrowserActionsContainer::Animate(gfx::Tween::Type tween_type,
1086                                       size_t num_visible_icons) {
1087   int target_size = IconCountToWidth(num_visible_icons,
1088       num_visible_icons < browser_action_views_.size());
1089   if (resize_animation_ && !disable_animations_during_testing_) {
1090     // Animate! We have to set the animation_target_size_ after calling Reset(),
1091     // because that could end up calling AnimationEnded which clears the value.
1092     resize_animation_->Reset();
1093     resize_animation_->SetTweenType(tween_type);
1094     animation_target_size_ = target_size;
1095     resize_animation_->Show();
1096   } else {
1097     animation_target_size_ = target_size;
1098     AnimationEnded(resize_animation_.get());
1099   }
1100 }
1101
1102 bool BrowserActionsContainer::ShouldDisplayBrowserAction(
1103     const Extension* extension) const {
1104   // Only display incognito-enabled extensions while in incognito mode.
1105   return !profile_->IsOffTheRecord() ||
1106       extensions::util::IsIncognitoEnabled(extension->id(), profile_);
1107 }
1108
1109 size_t BrowserActionsContainer::GetIconCount() const {
1110   if (!model_)
1111     return 0u;
1112
1113   const extensions::ExtensionList& extensions = model_->toolbar_items();
1114
1115   // Find the absolute value for the model's visible count.
1116   int model_visible_size = model_->GetVisibleIconCount();
1117   size_t absolute_model_visible_size =
1118       model_visible_size == -1 ? extensions.size() : model_visible_size;
1119
1120   // Find the number of icons which could be displayed.
1121   size_t displayable_icon_count = 0u;
1122   size_t main_displayed = 0u;
1123   for (size_t i = 0; i < extensions.size(); ++i) {
1124     // Should there be an icon for this extension at all?
1125     if (ShouldDisplayBrowserAction(extensions[i].get())) {
1126       ++displayable_icon_count;
1127       // Should we display it on the main bar? If this is an incognito window,
1128       // icons have the same overflow status they do in a regular window.
1129       main_displayed += i < absolute_model_visible_size ? 1u : 0u;
1130     }
1131   }
1132
1133   // If this is an existing (initialized) container from an incognito profile,
1134   // we can't trust the model (because the incognito bars don't adjust model
1135   // settings). Instead, we go off what we currently have displayed.
1136   if (initialized_ && profile_->IsOffTheRecord()) {
1137     main_displayed = in_overflow_mode() ?
1138         main_container_->VisibleBrowserActionsAfterAnimation() :
1139         VisibleBrowserActionsAfterAnimation();
1140   }
1141
1142   // The overflow displays any (displayable) icons not shown by the main bar.
1143   return in_overflow_mode() ?
1144       displayable_icon_count - main_displayed : main_displayed;
1145 }