browser_actions_container.cc revision 116680a4aac90f2aa7413d9095a592090648e557
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// found in the LICENSE file.
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/compiler_specific.h"
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/prefs/pref_service.h"
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "base/stl_util.h"
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/extension_view_host.h"
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/extensions/tab_helper.h"
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/sessions/session_tab_helper.h"
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/view_ids.h"
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/extensions/extension_popup.h"
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/browser_action_view.h"
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/extensions/command.h"
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "chrome/common/pref_names.h"
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/browser/extension_system.h"
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/browser/pref_names.h"
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/browser/runtime_data.h"
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "extensions/common/feature_switch.h"
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "grit/generated_resources.h"
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "grit/theme_resources.h"
3306f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)#include "grit/ui_resources.h"
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
3551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "ui/accessibility/ax_view_state.h"
3606f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)#include "ui/base/dragdrop/drag_utils.h"
37bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
3853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "ui/base/nine_image_painter_factory.h"
3953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
401e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "ui/base/theme_provider.h"
41e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "ui/gfx/animation/slide_animation.h"
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/gfx/canvas.h"
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/gfx/geometry/rect.h"
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/views/controls/button/label_button_border.h"
45bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)#include "ui/views/controls/resize_area.h"
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/views/metrics.h"
47c0e19a689c8ac22cdc96b291a8d33a5d3b0b34a4Torne (Richard Coles)#include "ui/views/painter.h"
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "ui/views/widget/widget.h"
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using extensions::Extension;
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)namespace {
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Horizontal spacing between most items in the container, as well as after the
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// last item or chevron (if visible).
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const int kItemSpacing = ToolbarView::kStandardSpacing;
57bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Horizontal spacing before the chevron (if visible).
59bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)const int kChevronSpacing = kItemSpacing - 2;
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// The maximum number of icons to show per row when in overflow mode (showing
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// icons in the application menu).
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// TODO(devlin): Compute the right number of icons to show, depending on the
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)//               menu width.
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#if defined(OS_LINUX)
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const int kIconsPerMenuRow = 8;  // The menu on Linux is wider.
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#else
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const int kIconsPerMenuRow = 7;
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#endif
70bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)
7153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)// A version of MenuButton with almost empty insets to fit properly on the
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// toolbar.
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class ChevronMenuButton : public views::MenuButton {
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) public:
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  ChevronMenuButton(views::ButtonListener* listener,
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    const base::string16& text,
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    views::MenuButtonListener* menu_button_listener,
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    bool show_menu_marker)
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      : views::MenuButton(listener,
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                          text,
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                          menu_button_listener,
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                          show_menu_marker) {
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  }
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  virtual ~ChevronMenuButton() {}
8651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  virtual scoped_ptr<views::LabelButtonBorder> CreateDefaultBorder() const
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)      OVERRIDE {
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // The chevron resource was designed to not have any insets.
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    scoped_ptr<views::LabelButtonBorder> border =
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        views::MenuButton::CreateDefaultBorder();
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    border->set_insets(gfx::Insets());
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return border.Pass();
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  }
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) private:
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ChevronMenuButton);
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
100}  // namespace
101
102// static
103bool BrowserActionsContainer::disable_animations_during_testing_ = false;
104
105////////////////////////////////////////////////////////////////////////////////
106// BrowserActionsContainer
107
108BrowserActionsContainer::BrowserActionsContainer(
109    Browser* browser,
110    View* owner_view,
111    BrowserActionsContainer* main_container)
112    : profile_(browser->profile()),
113      browser_(browser),
114      owner_view_(owner_view),
115      main_container_(main_container),
116      popup_(NULL),
117      popup_button_(NULL),
118      model_(NULL),
119      container_width_(0),
120      resize_area_(NULL),
121      chevron_(NULL),
122      overflow_menu_(NULL),
123      suppress_chevron_(false),
124      resize_amount_(0),
125      animation_target_size_(0),
126      drop_indicator_position_(-1),
127      task_factory_(this),
128      show_menu_task_factory_(this) {
129  set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
130
131  model_ = extensions::ExtensionToolbarModel::Get(browser->profile());
132  if (model_)
133    model_->AddObserver(this);
134
135  bool overflow_experiment =
136      extensions::FeatureSwitch::extension_action_redesign()->IsEnabled();
137  DCHECK(!in_overflow_mode() || overflow_experiment);
138
139  if (!in_overflow_mode()) {
140    extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
141        browser->profile(),
142        owner_view->GetFocusManager(),
143        extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
144        this));
145
146    resize_animation_.reset(new gfx::SlideAnimation(this));
147    resize_area_ = new views::ResizeArea(this);
148    AddChildView(resize_area_);
149
150    // 'Main' mode doesn't need a chevron overflow when overflow is shown inside
151    // the Chrome menu.
152    if (!overflow_experiment) {
153      chevron_ = new ChevronMenuButton(NULL, base::string16(), this, false);
154      chevron_->SetBorder(views::Border::NullBorder());
155      chevron_->EnableCanvasFlippingForRTLUI(true);
156      chevron_->SetAccessibleName(
157          l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
158      chevron_->SetVisible(false);
159      AddChildView(chevron_);
160    }
161  }
162}
163
164BrowserActionsContainer::~BrowserActionsContainer() {
165  FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
166                    observers_,
167                    OnBrowserActionsContainerDestroyed());
168
169  if (overflow_menu_)
170    overflow_menu_->set_observer(NULL);
171  if (model_)
172    model_->RemoveObserver(this);
173  StopShowFolderDropMenuTimer();
174  if (popup_)
175    popup_->GetWidget()->RemoveObserver(this);
176  HidePopup();
177  DeleteBrowserActionViews();
178}
179
180void BrowserActionsContainer::Init() {
181  LoadImages();
182
183  // We wait to set the container width until now so that the chevron images
184  // will be loaded.  The width calculation needs to know the chevron size.
185  if (model_ &&
186      !profile_->GetPrefs()->HasPrefPath(
187          extensions::pref_names::kToolbarSize)) {
188    // Migration code to the new VisibleIconCount pref.
189    // TODO(mpcomplete): remove this after users are upgraded to 5.0.
190    int predefined_width = profile_->GetPrefs()->GetInteger(
191        extensions::pref_names::kBrowserActionContainerWidth);
192    if (predefined_width != 0)
193      model_->SetVisibleIconCount(WidthToIconCount(predefined_width));
194  }
195  if (model_ && model_->extensions_initialized())
196    SetContainerWidth();
197}
198
199BrowserActionView* BrowserActionsContainer::GetBrowserActionView(
200    ExtensionAction* action) {
201  for (BrowserActionViews::iterator i(browser_action_views_.begin());
202       i != browser_action_views_.end(); ++i) {
203    if ((*i)->button()->browser_action() == action)
204      return *i;
205  }
206  return NULL;
207}
208
209void BrowserActionsContainer::RefreshBrowserActionViews() {
210  for (size_t i = 0; i < browser_action_views_.size(); ++i)
211    browser_action_views_[i]->button()->UpdateState();
212}
213
214void BrowserActionsContainer::CreateBrowserActionViews() {
215  DCHECK(browser_action_views_.empty());
216  if (!model_)
217    return;
218
219  const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
220  for (extensions::ExtensionList::const_iterator i(toolbar_items.begin());
221       i != toolbar_items.end(); ++i) {
222    if (!ShouldDisplayBrowserAction(i->get()))
223      continue;
224
225    BrowserActionView* view = new BrowserActionView(i->get(), browser_, this);
226    browser_action_views_.push_back(view);
227    AddChildView(view);
228  }
229}
230
231void BrowserActionsContainer::DeleteBrowserActionViews() {
232  HidePopup();
233  if (overflow_menu_)
234    overflow_menu_->NotifyBrowserActionViewsDeleting();
235  STLDeleteElements(&browser_action_views_);
236}
237
238size_t BrowserActionsContainer::VisibleBrowserActions() const {
239  size_t visible_actions = 0;
240  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
241    if (browser_action_views_[i]->visible())
242      ++visible_actions;
243  }
244  return visible_actions;
245}
246
247size_t BrowserActionsContainer::VisibleBrowserActionsAfterAnimation() const {
248  if (!animating())
249    return VisibleBrowserActions();
250
251  return WidthToIconCount(animation_target_size_);
252}
253
254void BrowserActionsContainer::ExecuteExtensionCommand(
255    const extensions::Extension* extension,
256    const extensions::Command& command) {
257  // Global commands are handled by the ExtensionCommandsGlobalRegistry
258  // instance.
259  DCHECK(!command.global());
260  extension_keybinding_registry_->ExecuteCommand(extension->id(),
261                                                 command.accelerator());
262}
263
264bool BrowserActionsContainer::ShownInsideMenu() const {
265  return in_overflow_mode();
266}
267
268void BrowserActionsContainer::AddObserver(
269    BrowserActionsContainerObserver* observer) {
270  observers_.AddObserver(observer);
271}
272
273void BrowserActionsContainer::RemoveObserver(
274    BrowserActionsContainerObserver* observer) {
275  observers_.RemoveObserver(observer);
276}
277
278gfx::Size BrowserActionsContainer::GetPreferredSize() const {
279  size_t icon_count = browser_action_views_.size() -
280      (in_overflow_mode() ? main_container_->VisibleBrowserActions() : 0);
281
282  // If there are no actions to show, or we are in overflow mode and the main
283  // container is already showing them all, then no further work is required.
284  if (icon_count == 0)
285    return gfx::Size();
286
287  if (in_overflow_mode()) {
288    // When in overflow, y is multiline, so the pixel count is IconHeight()
289    // times the number of rows needed.
290    return gfx::Size(
291        IconCountToWidth(kIconsPerMenuRow, false),
292        (((icon_count - 1) / kIconsPerMenuRow) + 1) * IconHeight());
293  }
294
295  // We calculate the size of the view by taking the current width and
296  // subtracting resize_amount_ (the latter represents how far the user is
297  // resizing the view or, if animating the snapping, how far to animate it).
298  // But we also clamp it to a minimum size and the maximum size, so that the
299  // container can never shrink too far or take up more space than it needs.
300  // In other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX).
301  int preferred_width = std::min(
302      std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_),
303      IconCountToWidth(-1, false));
304  return gfx::Size(preferred_width, IconHeight());
305}
306
307gfx::Size BrowserActionsContainer::GetMinimumSize() const {
308  int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1, false));
309  return gfx::Size(min_width, IconHeight());
310}
311
312void BrowserActionsContainer::Layout() {
313  if (browser_action_views_.empty()) {
314    SetVisible(false);
315    return;
316  }
317
318  SetVisible(true);
319  if (resize_area_)
320    resize_area_->SetBounds(0, 0, kItemSpacing, height());
321
322  // If the icons don't all fit, show the chevron (unless suppressed).
323  int max_x = GetPreferredSize().width();
324  if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_ && chevron_) {
325    chevron_->SetVisible(true);
326    gfx::Size chevron_size(chevron_->GetPreferredSize());
327    max_x -=
328        ToolbarView::kStandardSpacing + chevron_size.width() + kChevronSpacing;
329    chevron_->SetBounds(
330        width() - ToolbarView::kStandardSpacing - chevron_size.width(),
331        0,
332        chevron_size.width(),
333        chevron_size.height());
334  } else if (chevron_) {
335    chevron_->SetVisible(false);
336  }
337
338  // Now draw the icons for the browser actions in the available space.
339  int icon_width = IconWidth(false);
340  if (in_overflow_mode()) {
341    for (size_t i = 0;
342         i < main_container_->VisibleBrowserActionsAfterAnimation(); ++i) {
343      // Ensure that any browser actions shown in the main view are hidden in
344      // the overflow view.
345      browser_action_views_[i]->SetVisible(false);
346    }
347
348    for (size_t i = main_container_->VisibleBrowserActionsAfterAnimation();
349         i < browser_action_views_.size(); ++i) {
350      BrowserActionView* view = browser_action_views_[i];
351      size_t index = i - main_container_->VisibleBrowserActionsAfterAnimation();
352      int row_index = static_cast<int>(index) / kIconsPerMenuRow;
353      int x = (index * IconWidth(true)) -
354          (row_index * IconWidth(true) * kIconsPerMenuRow);
355      gfx::Rect rect_bounds(
356          x, IconHeight() * row_index, icon_width, IconHeight());
357      view->SetBoundsRect(rect_bounds);
358      view->SetVisible(true);
359    }
360  } else {
361    for (BrowserActionViews::const_iterator it = browser_action_views_.begin();
362         it < browser_action_views_.end(); ++it) {
363      BrowserActionView* view = *it;
364      int x = ToolbarView::kStandardSpacing +
365          ((it - browser_action_views_.begin()) * IconWidth(true));
366      view->SetVisible(x + icon_width <= max_x);
367      if (view->visible())
368        view->SetBounds(x, 0, icon_width, IconHeight());
369    }
370  }
371}
372
373bool BrowserActionsContainer::GetDropFormats(
374    int* formats,
375    std::set<OSExchangeData::CustomFormat>* custom_formats) {
376  custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat());
377
378  return true;
379}
380
381bool BrowserActionsContainer::AreDropTypesRequired() {
382  return true;
383}
384
385bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
386  BrowserActionDragData drop_data;
387  return drop_data.Read(data) ? drop_data.IsFromProfile(profile_) : false;
388}
389
390void BrowserActionsContainer::OnDragEntered(
391    const ui::DropTargetEvent& event) {
392}
393
394int BrowserActionsContainer::OnDragUpdated(
395    const ui::DropTargetEvent& event) {
396  // First check if we are above the chevron (overflow) menu.
397  if (GetEventHandlerForPoint(event.location()) == chevron_) {
398    if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
399      StartShowFolderDropMenuTimer();
400    return ui::DragDropTypes::DRAG_MOVE;
401  }
402  StopShowFolderDropMenuTimer();
403
404  // TODO(devlin): This calculation needs to take 'overflow' mode into account
405  // once the wrench menu becomes a drag target for browser action icons.
406
407  // Figure out where to display the indicator.  This is a complex calculation:
408
409  // First, we figure out how much space is to the left of the icon area, so we
410  // can calculate the true offset into the icon area.
411  int width_before_icons = ToolbarView::kStandardSpacing;
412  if (chevron_ && base::i18n::IsRTL()) {
413    width_before_icons +=
414        chevron_->GetPreferredSize().width() + kChevronSpacing;
415  }
416  int offset_into_icon_area = event.x() - width_before_icons;
417
418  // Next, we determine which icon to place the indicator in front of.  We want
419  // to place the indicator in front of icon n when the cursor is between the
420  // midpoints of icons (n - 1) and n.  To do this we take the offset into the
421  // icon area and transform it as follows:
422  //
423  // Real icon area:
424  //   0   a     *  b        c
425  //   |   |        |        |
426  //   |[IC|ON]  [IC|ON]  [IC|ON]
427  // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
428  // Here the "*" represents the offset into the icon area, and since it's
429  // between a and b, we want to return "1".
430  //
431  // Transformed "icon area":
432  //   0        a     *  b        c
433  //   |        |        |        |
434  //   |[ICON]  |[ICON]  |[ICON]  |
435  // If we shift both our offset and our divider points later by half an icon
436  // plus one spacing unit, then it becomes very easy to calculate how many
437  // divider points we've passed, because they're the multiples of "one icon
438  // plus padding".
439  int before_icon_unclamped = (offset_into_icon_area + (IconWidth(false) / 2) +
440      kItemSpacing) / IconWidth(true);
441
442  // Because the user can drag outside the container bounds, we need to clamp to
443  // the valid range.  Note that the maximum allowable value is (num icons), not
444  // (num icons - 1), because we represent the indicator being past the last
445  // icon as being "before the (last + 1) icon".
446  int before_icon = std::min(std::max(before_icon_unclamped, 0),
447                             static_cast<int>(VisibleBrowserActions()));
448
449  // Now we convert back to a pixel offset into the container.  We want to place
450  // the center of the drop indicator at the midpoint of the space before our
451  // chosen icon.
452  SetDropIndicator(width_before_icons + (before_icon * IconWidth(true)) -
453      (kItemSpacing / 2));
454
455  return ui::DragDropTypes::DRAG_MOVE;
456}
457
458void BrowserActionsContainer::OnDragExited() {
459  StopShowFolderDropMenuTimer();
460  drop_indicator_position_ = -1;
461  SchedulePaint();
462}
463
464int BrowserActionsContainer::OnPerformDrop(
465    const ui::DropTargetEvent& event) {
466  BrowserActionDragData data;
467  if (!data.Read(event.data()))
468    return ui::DragDropTypes::DRAG_NONE;
469
470  // Make sure we have the same view as we started with.
471  DCHECK_EQ(browser_action_views_[data.index()]->button()->extension()->id(),
472            data.id());
473  DCHECK(model_);
474
475  size_t i = 0;
476  for (; i < browser_action_views_.size(); ++i) {
477    int view_x = browser_action_views_[i]->GetMirroredBounds().x();
478    if (!browser_action_views_[i]->visible() ||
479        (base::i18n::IsRTL() ? (view_x < drop_indicator_position_) :
480            (view_x >= drop_indicator_position_))) {
481      // We have reached the end of the visible icons or found one that has a
482      // higher x position than the drop point.
483      break;
484    }
485  }
486
487  // |i| now points to the item to the right of the drop indicator*, which is
488  // correct when dragging an icon to the left. When dragging to the right,
489  // however, we want the icon being dragged to get the index of the item to
490  // the left of the drop indicator, so we subtract one.
491  // * Well, it can also point to the end, but not when dragging to the left. :)
492  if (i > data.index())
493    --i;
494
495  if (profile_->IsOffTheRecord())
496    i = model_->IncognitoIndexToOriginal(i);
497
498  model_->MoveBrowserAction(
499      browser_action_views_[data.index()]->button()->extension(), i);
500
501  OnDragExited();  // Perform clean up after dragging.
502  return ui::DragDropTypes::DRAG_MOVE;
503}
504
505void BrowserActionsContainer::GetAccessibleState(
506    ui::AXViewState* state) {
507  state->role = ui::AX_ROLE_GROUP;
508  state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
509}
510
511void BrowserActionsContainer::OnMenuButtonClicked(views::View* source,
512                                                  const gfx::Point& point) {
513  if (source == chevron_) {
514    overflow_menu_ =
515        new BrowserActionOverflowMenuController(this,
516                                                browser_,
517                                                chevron_,
518                                                browser_action_views_,
519                                                VisibleBrowserActions(),
520                                                false);
521    overflow_menu_->set_observer(this);
522    overflow_menu_->RunMenu(GetWidget());
523  }
524}
525
526void BrowserActionsContainer::WriteDragDataForView(View* sender,
527                                                   const gfx::Point& press_pt,
528                                                   OSExchangeData* data) {
529  DCHECK(data);
530
531  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
532    BrowserActionButton* button = browser_action_views_[i]->button();
533    if (button == sender) {
534      // Set the dragging image for the icon.
535      gfx::ImageSkia badge(browser_action_views_[i]->GetIconWithBadge());
536      drag_utils::SetDragImageOnDataObject(badge,
537                                           press_pt.OffsetFromOrigin(),
538                                           data);
539
540      // Fill in the remaining info.
541      BrowserActionDragData drag_data(
542          browser_action_views_[i]->button()->extension()->id(), i);
543      drag_data.Write(profile_, data);
544      break;
545    }
546  }
547}
548
549int BrowserActionsContainer::GetDragOperationsForView(View* sender,
550                                                      const gfx::Point& p) {
551  return ui::DragDropTypes::DRAG_MOVE;
552}
553
554bool BrowserActionsContainer::CanStartDragForView(View* sender,
555                                                  const gfx::Point& press_pt,
556                                                  const gfx::Point& p) {
557  // We don't allow dragging while we're highlighting.
558  return !model_->is_highlighting();
559}
560
561void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
562  if (!done_resizing) {
563    resize_amount_ = resize_amount;
564    OnBrowserActionVisibilityChanged();
565    return;
566  }
567
568  // Up until now we've only been modifying the resize_amount, but now it is
569  // time to set the container size to the size we have resized to, and then
570  // animate to the nearest icon count size if necessary (which may be 0).
571  int max_width = IconCountToWidth(-1, false);
572  container_width_ =
573      std::min(std::max(0, container_width_ - resize_amount), max_width);
574  SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
575                            WidthToIconCount(container_width_));
576}
577
578void BrowserActionsContainer::AnimationProgressed(
579    const gfx::Animation* animation) {
580  DCHECK_EQ(resize_animation_.get(), animation);
581  resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
582      (container_width_ - animation_target_size_));
583  OnBrowserActionVisibilityChanged();
584}
585
586void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
587  container_width_ = animation_target_size_;
588  animation_target_size_ = 0;
589  resize_amount_ = 0;
590  suppress_chevron_ = false;
591  OnBrowserActionVisibilityChanged();
592
593  FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
594                    observers_,
595                    OnBrowserActionsContainerAnimationEnded());
596}
597
598void BrowserActionsContainer::NotifyMenuDeleted(
599    BrowserActionOverflowMenuController* controller) {
600  DCHECK_EQ(overflow_menu_, controller);
601  overflow_menu_ = NULL;
602}
603
604void BrowserActionsContainer::OnWidgetDestroying(views::Widget* widget) {
605  DCHECK_EQ(popup_->GetWidget(), widget);
606  popup_->GetWidget()->RemoveObserver(this);
607  popup_ = NULL;
608  // |popup_button_| is NULL if the extension has been removed.
609  if (popup_button_) {
610    popup_button_->SetButtonNotPushed();
611    popup_button_ = NULL;
612  }
613}
614
615void BrowserActionsContainer::InspectPopup(ExtensionAction* action) {
616  BrowserActionView* view = GetBrowserActionView(action);
617  ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT, true);
618}
619
620int BrowserActionsContainer::GetCurrentTabId() const {
621  content::WebContents* active_tab =
622      browser_->tab_strip_model()->GetActiveWebContents();
623  if (!active_tab)
624    return -1;
625
626  return SessionTabHelper::FromWebContents(active_tab)->session_id().id();
627}
628
629void BrowserActionsContainer::OnBrowserActionExecuted(
630    BrowserActionButton* button) {
631  ShowPopup(button, ExtensionPopup::SHOW, true);
632}
633
634void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
635  SetVisible(!browser_action_views_.empty());
636  if (owner_view_) {
637    owner_view_->Layout();
638    owner_view_->SchedulePaint();
639  }
640}
641
642extensions::ActiveTabPermissionGranter*
643    BrowserActionsContainer::GetActiveTabPermissionGranter() {
644  content::WebContents* web_contents =
645      browser_->tab_strip_model()->GetActiveWebContents();
646  if (!web_contents)
647    return NULL;
648  return extensions::TabHelper::FromWebContents(web_contents)->
649      active_tab_permission_granter();
650}
651
652void BrowserActionsContainer::MoveBrowserAction(const std::string& extension_id,
653                                                size_t new_index) {
654  ExtensionService* service =
655      extensions::ExtensionSystem::Get(profile_)->extension_service();
656  if (service) {
657    const Extension* extension = service->GetExtensionById(extension_id, false);
658    model_->MoveBrowserAction(extension, new_index);
659    SchedulePaint();
660  }
661}
662
663bool BrowserActionsContainer::ShowPopup(const extensions::Extension* extension,
664                                        bool should_grant) {
665  // Do not override other popups and only show in active window. The window
666  // must also have a toolbar, otherwise it should not be showing popups.
667  // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
668  // fixed.
669  if (popup_ ||
670      !browser_->window()->IsActive() ||
671      !browser_->window()->IsToolbarVisible()) {
672    return false;
673  }
674
675  for (BrowserActionViews::iterator it = browser_action_views_.begin();
676       it != browser_action_views_.end(); ++it) {
677    BrowserActionButton* button = (*it)->button();
678    if (button && button->extension() == extension)
679      return ShowPopup(button, ExtensionPopup::SHOW, should_grant);
680  }
681  return false;
682}
683
684void BrowserActionsContainer::HidePopup() {
685  // Remove this as an observer and clear |popup_| and |popup_button_| here,
686  // since we might change them before OnWidgetDestroying() gets called.
687  if (popup_) {
688    popup_->GetWidget()->RemoveObserver(this);
689    popup_->GetWidget()->Close();
690    popup_ = NULL;
691  }
692  if (popup_button_) {
693    popup_button_->SetButtonNotPushed();
694    popup_button_ = NULL;
695  }
696}
697
698void BrowserActionsContainer::TestExecuteBrowserAction(int index) {
699  BrowserActionButton* button = browser_action_views_[index]->button();
700  OnBrowserActionExecuted(button);
701}
702
703void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
704  model_->SetVisibleIconCount(icons);
705  chevron_->SetVisible(icons < browser_action_views_.size());
706  container_width_ = IconCountToWidth(icons, chevron_->visible());
707  Layout();
708  SchedulePaint();
709}
710
711void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
712  // If the views haven't been initialized yet, wait for the next call to
713  // paint (one will be triggered by entering highlight mode).
714  if (model_->is_highlighting() && !browser_action_views_.empty()) {
715    views::Painter::PaintPainterAt(
716        canvas, highlight_painter_.get(), GetLocalBounds());
717  }
718
719  // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
720  // dragging (like we do for tab dragging).
721  if (drop_indicator_position_ > -1) {
722    // The two-pixel width drop indicator.
723    static const int kDropIndicatorWidth = 2;
724    gfx::Rect indicator_bounds(
725        drop_indicator_position_ - (kDropIndicatorWidth / 2),
726        0,
727        kDropIndicatorWidth,
728        height());
729
730    // Color of the drop indicator.
731    static const SkColor kDropIndicatorColor = SK_ColorBLACK;
732    canvas->FillRect(indicator_bounds, kDropIndicatorColor);
733  }
734}
735
736void BrowserActionsContainer::OnThemeChanged() {
737  LoadImages();
738}
739
740void BrowserActionsContainer::ViewHierarchyChanged(
741    const ViewHierarchyChangedDetails& details) {
742  // No extensions (e.g., incognito).
743  if (!model_)
744    return;
745
746  if (details.is_add && details.child == this) {
747    // Initial toolbar button creation and placement in the widget hierarchy.
748    // We do this here instead of in the constructor because AddBrowserAction
749    // calls Layout on the Toolbar, which needs this object to be constructed
750    // before its Layout function is called.
751    CreateBrowserActionViews();
752  }
753}
754
755// static
756int BrowserActionsContainer::IconWidth(bool include_padding) {
757  static bool initialized = false;
758  static int icon_width = 0;
759  if (!initialized) {
760    initialized = true;
761    icon_width = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
762        IDR_BROWSER_ACTION)->width();
763  }
764  return icon_width + (include_padding ? kItemSpacing : 0);
765}
766
767// static
768int BrowserActionsContainer::IconHeight() {
769  static bool initialized = false;
770  static int icon_height = 0;
771  if (!initialized) {
772    initialized = true;
773    icon_height = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
774        IDR_BROWSER_ACTION)->height();
775  }
776  return icon_height;
777}
778
779void BrowserActionsContainer::BrowserActionAdded(const Extension* extension,
780                                                 int index) {
781#if defined(DEBUG)
782  for (size_t i = 0; i < browser_action_views_.size(); ++i) {
783    DCHECK(browser_action_views_[i]->button()->extension() != extension) <<
784           "Asked to add a browser action view for an extension that already "
785           "exists.";
786  }
787#endif
788  CloseOverflowMenu();
789
790  if (!ShouldDisplayBrowserAction(extension))
791    return;
792
793  size_t visible_actions = VisibleBrowserActionsAfterAnimation();
794
795  // Add the new browser action to the vector and the view hierarchy.
796  if (profile_->IsOffTheRecord())
797    index = model_->OriginalIndexToIncognito(index);
798  BrowserActionView* view = new BrowserActionView(extension, browser_, this);
799  browser_action_views_.insert(browser_action_views_.begin() + index, view);
800  AddChildViewAt(view, index);
801
802  // If we are still initializing the container, don't bother animating.
803  if (!model_->extensions_initialized())
804    return;
805
806  // Enlarge the container if it was already at maximum size and we're not in
807  // the middle of upgrading.
808  if ((model_->GetVisibleIconCount() < 0) &&
809      !extensions::ExtensionSystem::Get(profile_)->runtime_data()->
810          IsBeingUpgraded(extension)) {
811    suppress_chevron_ = true;
812    SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, visible_actions + 1);
813  } else {
814    // Just redraw the (possibly modified) visible icon set.
815    OnBrowserActionVisibilityChanged();
816  }
817}
818
819void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) {
820  CloseOverflowMenu();
821
822  if (popup_ && popup_->host()->extension() == extension)
823    HidePopup();
824
825  size_t visible_actions = VisibleBrowserActionsAfterAnimation();
826  for (BrowserActionViews::iterator i(browser_action_views_.begin());
827       i != browser_action_views_.end(); ++i) {
828    if ((*i)->button()->extension() == extension) {
829      delete *i;
830      browser_action_views_.erase(i);
831
832      // If the extension is being upgraded we don't want the bar to shrink
833      // because the icon is just going to get re-added to the same location.
834      if (extensions::ExtensionSystem::Get(profile_)->runtime_data()->
835              IsBeingUpgraded(extension))
836        return;
837
838      if (browser_action_views_.size() > visible_actions) {
839        // If we have more icons than we can show, then we must not be changing
840        // the container size (since we either removed an icon from the main
841        // area and one from the overflow list will have shifted in, or we
842        // removed an entry directly from the overflow list).
843        OnBrowserActionVisibilityChanged();
844      } else {
845        // Either we went from overflow to no-overflow, or we shrunk the no-
846        // overflow container by 1.  Either way the size changed, so animate.
847        if (chevron_)
848          chevron_->SetVisible(false);
849        SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
850                                  browser_action_views_.size());
851      }
852      return;  // We have found the action to remove, bail out.
853    }
854  }
855}
856
857void BrowserActionsContainer::BrowserActionMoved(const Extension* extension,
858                                                 int index) {
859  if (!ShouldDisplayBrowserAction(extension))
860    return;
861
862  if (profile_->IsOffTheRecord())
863    index = model_->OriginalIndexToIncognito(index);
864
865  DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size()));
866
867  DeleteBrowserActionViews();
868  CreateBrowserActionViews();
869  Layout();
870  SchedulePaint();
871}
872
873bool BrowserActionsContainer::BrowserActionShowPopup(
874    const extensions::Extension* extension) {
875  return ShowPopup(extension, false);
876}
877
878void BrowserActionsContainer::VisibleCountChanged() {
879  SetContainerWidth();
880}
881
882void BrowserActionsContainer::HighlightModeChanged(bool is_highlighting) {
883  // The visual highlighting is done in OnPaint(). It's a bit of a pain that
884  // we delete and recreate everything here, but that's how it's done in
885  // BrowserActionMoved(), too. If we want to optimize it, we could move the
886  // existing icons, instead of deleting it all.
887  DeleteBrowserActionViews();
888  CreateBrowserActionViews();
889  SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, browser_action_views_.size());
890}
891
892void BrowserActionsContainer::LoadImages() {
893  ui::ThemeProvider* tp = GetThemeProvider();
894  if (!tp || !chevron_)
895    return;
896
897  chevron_->SetImage(views::Button::STATE_NORMAL,
898                     *tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
899
900  const int kImages[] = IMAGE_GRID(IDR_DEVELOPER_MODE_HIGHLIGHT);
901  highlight_painter_.reset(views::Painter::CreateImageGridPainter(kImages));
902}
903
904void BrowserActionsContainer::SetContainerWidth() {
905  // The slave only draws the overflow (what isn't visible in the other
906  // container).
907  int visible_actions = in_overflow_mode() ?
908      model_->toolbar_items().size() - model_->GetVisibleIconCount() :
909      model_->GetVisibleIconCount();
910  if (visible_actions < 0)  // All icons should be visible.
911    visible_actions = model_->toolbar_items().size();
912  if (chevron_) {
913    chevron_->SetVisible(
914      static_cast<size_t>(visible_actions) < model_->toolbar_items().size());
915  }
916  container_width_ =
917      IconCountToWidth(visible_actions, chevron_ && chevron_->visible());
918}
919
920void BrowserActionsContainer::CloseOverflowMenu() {
921  if (overflow_menu_)
922    overflow_menu_->CancelMenu();
923}
924
925void BrowserActionsContainer::StopShowFolderDropMenuTimer() {
926  show_menu_task_factory_.InvalidateWeakPtrs();
927}
928
929void BrowserActionsContainer::StartShowFolderDropMenuTimer() {
930  base::MessageLoop::current()->PostDelayedTask(
931      FROM_HERE,
932      base::Bind(&BrowserActionsContainer::ShowDropFolder,
933                 show_menu_task_factory_.GetWeakPtr()),
934      base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
935}
936
937void BrowserActionsContainer::ShowDropFolder() {
938  DCHECK(!overflow_menu_);
939  SetDropIndicator(-1);
940  overflow_menu_ =
941      new BrowserActionOverflowMenuController(this,
942                                              browser_,
943                                              chevron_,
944                                              browser_action_views_,
945                                              VisibleBrowserActions(),
946                                              true);
947  overflow_menu_->set_observer(this);
948  overflow_menu_->RunMenu(GetWidget());
949}
950
951void BrowserActionsContainer::SetDropIndicator(int x_pos) {
952  if (drop_indicator_position_ != x_pos) {
953    drop_indicator_position_ = x_pos;
954    SchedulePaint();
955  }
956}
957
958int BrowserActionsContainer::IconCountToWidth(int icons,
959                                              bool display_chevron) const {
960  if (icons < 0)
961    icons = browser_action_views_.size();
962  if ((icons == 0) && !display_chevron)
963    return ToolbarView::kStandardSpacing;
964  int icons_size =
965      (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing);
966  int chevron_size = chevron_ && display_chevron ?
967      (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0;
968  return ToolbarView::kStandardSpacing + icons_size + chevron_size +
969      ToolbarView::kStandardSpacing;
970}
971
972size_t BrowserActionsContainer::WidthToIconCount(int pixels) const {
973  // Check for widths large enough to show the entire icon set.
974  if (pixels >= IconCountToWidth(-1, false))
975    return browser_action_views_.size();
976
977  // We need to reserve space for the resize area, chevron, and the spacing on
978  // either side of the chevron.
979  int available_space = pixels - ToolbarView::kStandardSpacing -
980      (chevron_ ? chevron_->GetPreferredSize().width() : 0) -
981      kChevronSpacing - ToolbarView::kStandardSpacing;
982  // Now we add an extra between-item padding value so the space can be divided
983  // evenly by (size of icon with padding).
984  return static_cast<size_t>(
985      std::max(0, available_space + kItemSpacing) / IconWidth(true));
986}
987
988int BrowserActionsContainer::MinimumNonemptyWidth() const {
989  if (!chevron_)
990    return ToolbarView::kStandardSpacing;
991  return (ToolbarView::kStandardSpacing * 2) + kChevronSpacing +
992      chevron_->GetPreferredSize().width();
993}
994
995void BrowserActionsContainer::SaveDesiredSizeAndAnimate(
996    gfx::Tween::Type tween_type,
997    size_t num_visible_icons) {
998  // Save off the desired number of visible icons.  We do this now instead of at
999  // the end of the animation so that even if the browser is shut down while
1000  // animating, the right value will be restored on next run.
1001  // NOTE: Don't save the icon count in incognito because there may be fewer
1002  // icons in that mode. The result is that the container in a normal window is
1003  // always at least as wide as in an incognito window.
1004  if (!profile_->IsOffTheRecord())
1005    model_->SetVisibleIconCount(num_visible_icons);
1006  int target_size = IconCountToWidth(num_visible_icons,
1007      num_visible_icons < browser_action_views_.size());
1008  if (resize_animation_ && !disable_animations_during_testing_) {
1009    // Animate! We have to set the animation_target_size_ after calling Reset(),
1010    // because that could end up calling AnimationEnded which clears the value.
1011    resize_animation_->Reset();
1012    resize_animation_->SetTweenType(tween_type);
1013    animation_target_size_ = target_size;
1014    resize_animation_->Show();
1015  } else {
1016    animation_target_size_ = target_size;
1017    AnimationEnded(resize_animation_.get());
1018  }
1019}
1020
1021bool BrowserActionsContainer::ShouldDisplayBrowserAction(
1022    const Extension* extension) {
1023  // Only display incognito-enabled extensions while in incognito mode.
1024  return !profile_->IsOffTheRecord() ||
1025      extensions::util::IsIncognitoEnabled(extension->id(), profile_);
1026}
1027
1028bool BrowserActionsContainer::ShowPopup(
1029    BrowserActionButton* button,
1030    ExtensionPopup::ShowAction show_action,
1031    bool should_grant) {
1032  const Extension* extension = button->extension();
1033  GURL popup_url;
1034  if (model_->ExecuteBrowserAction(
1035          extension, browser_, &popup_url, should_grant) !=
1036      extensions::ExtensionToolbarModel::ACTION_SHOW_POPUP) {
1037    return false;
1038  }
1039
1040  // If we're showing the same popup, just hide it and return.
1041  bool same_showing = popup_ && button == popup_button_;
1042
1043  // Always hide the current popup, even if it's not the same.
1044  // Only one popup should be visible at a time.
1045  HidePopup();
1046
1047  if (same_showing)
1048    return false;
1049
1050  // We can get the execute event for browser actions that are not visible,
1051  // since buttons can be activated from the overflow menu (chevron). In that
1052  // case we show the popup as originating from the chevron.
1053  View* reference_view = button->parent()->visible() ? button : chevron_;
1054  popup_ = ExtensionPopup::ShowPopup(popup_url, browser_, reference_view,
1055                                     views::BubbleBorder::TOP_RIGHT,
1056                                     show_action);
1057  popup_->GetWidget()->AddObserver(this);
1058  popup_button_ = button;
1059
1060  // Only set button as pushed if it was triggered by a user click.
1061  if (should_grant)
1062    popup_button_->SetButtonPushed();
1063  return true;
1064}
1065