Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / browser / ui / toolbar / toolbar_actions_bar.cc
blobfef0d5dd37075987dcb1293e4cf5fd644c5dc7cd
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
7 #include "base/auto_reset.h"
8 #include "base/profiler/scoped_tracker.h"
9 #include "chrome/browser/extensions/extension_action_manager.h"
10 #include "chrome/browser/extensions/extension_message_bubble_controller.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/sessions/session_tab_helper.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
17 #include "chrome/browser/ui/extensions/extension_message_bubble_factory.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
20 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
21 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h"
22 #include "chrome/common/pref_names.h"
23 #include "components/crx_file/id_util.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/runtime_data.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/feature_switch.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/gfx/image/image_skia.h"
33 namespace {
35 using WeakToolbarActions = std::vector<ToolbarActionViewController*>;
37 // Matches ToolbarView::kStandardSpacing;
38 const int kLeftPadding = 3;
39 const int kRightPadding = kLeftPadding;
40 const int kItemSpacing = kLeftPadding;
41 const int kOverflowLeftPadding = kItemSpacing;
42 const int kOverflowRightPadding = kItemSpacing;
44 enum DimensionType { WIDTH, HEIGHT };
46 // Returns the width or height of the toolbar action icon size.
47 int GetIconDimension(DimensionType type) {
48 static bool initialized = false;
49 static int icon_height = 0;
50 static int icon_width = 0;
51 if (!initialized) {
52 initialized = true;
53 gfx::ImageSkia* skia =
54 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
55 IDR_BROWSER_ACTION);
56 icon_height = skia->height();
57 icon_width = skia->width();
59 return type == WIDTH ? icon_width : icon_height;
62 // Takes a reference vector |reference| of length n, where n is less than or
63 // equal to the length of |to_sort|, and rearranges |to_sort| so that
64 // |to_sort|'s first n elements match the n elements of |reference| (the order
65 // of any remaining elements in |to_sort| is unspecified).
66 // |equal| is used to compare the elements of |to_sort| and |reference|.
67 // This allows us to sort a vector to match another vector of two different
68 // types without needing to construct a more cumbersome comparator class.
69 // |FunctionType| should equate to (something similar to)
70 // bool Equal(const Type1&, const Type2&), but we can't enforce this
71 // because of MSVC compilation limitations.
72 template<typename Type1, typename Type2, typename FunctionType>
73 void SortContainer(std::vector<Type1>* to_sort,
74 const std::vector<Type2>& reference,
75 FunctionType equal) {
76 DCHECK_GE(to_sort->size(), reference.size()) <<
77 "|to_sort| must contain all elements in |reference|.";
78 if (reference.empty())
79 return;
80 // Run through the each element and compare it to the reference. If something
81 // is out of place, find the correct spot for it.
82 for (size_t i = 0; i < reference.size() - 1; ++i) {
83 if (!equal(to_sort->at(i), reference[i])) {
84 // Find the correct index (it's guaranteed to be after our current
85 // index, since everything up to this point is correct), and swap.
86 size_t j = i + 1;
87 while (!equal(to_sort->at(j), reference[i])) {
88 ++j;
89 DCHECK_LE(j, to_sort->size()) <<
90 "Item in |reference| not found in |to_sort|.";
92 std::swap(to_sort->at(i), to_sort->at(j));
97 } // namespace
99 // static
100 bool ToolbarActionsBar::disable_animations_for_testing_ = false;
102 // static
103 bool ToolbarActionsBar::send_overflowed_action_changes_ = true;
105 ToolbarActionsBar::PlatformSettings::PlatformSettings(bool in_overflow_mode)
106 : left_padding(in_overflow_mode ? kOverflowLeftPadding : kLeftPadding),
107 right_padding(in_overflow_mode ? kOverflowRightPadding : kRightPadding),
108 item_spacing(kItemSpacing),
109 icons_per_overflow_menu_row(1),
110 chevron_enabled(!extensions::FeatureSwitch::extension_action_redesign()->
111 IsEnabled()) {
114 ToolbarActionsBar::ToolbarActionsBar(ToolbarActionsBarDelegate* delegate,
115 Browser* browser,
116 ToolbarActionsBar* main_bar)
117 : delegate_(delegate),
118 browser_(browser),
119 model_(extensions::ExtensionToolbarModel::Get(browser_->profile())),
120 main_bar_(main_bar),
121 platform_settings_(main_bar != nullptr),
122 popup_owner_(nullptr),
123 model_observer_(this),
124 suppress_layout_(false),
125 suppress_animation_(true),
126 overflowed_action_wants_to_run_(false),
127 checked_extension_bubble_(false),
128 popped_out_action_(nullptr),
129 weak_ptr_factory_(this) {
130 if (model_) // |model_| can be null in unittests.
131 model_observer_.Add(model_);
134 ToolbarActionsBar::~ToolbarActionsBar() {
135 // We don't just call DeleteActions() here because it makes assumptions about
136 // the order of deletion between the views and the ToolbarActionsBar.
137 DCHECK(toolbar_actions_.empty()) <<
138 "Must call DeleteActions() before destruction.";
141 // static
142 int ToolbarActionsBar::IconWidth(bool include_padding) {
143 return GetIconDimension(WIDTH) + (include_padding ? kItemSpacing : 0);
146 // static
147 int ToolbarActionsBar::IconHeight() {
148 return GetIconDimension(HEIGHT);
151 // static
152 void ToolbarActionsBar::RegisterProfilePrefs(
153 user_prefs::PrefRegistrySyncable* registry) {
154 registry->RegisterBooleanPref(
155 prefs::kToolbarIconSurfacingBubbleAcknowledged,
156 false,
157 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
158 registry->RegisterInt64Pref(prefs::kToolbarIconSurfacingBubbleLastShowTime,
162 gfx::Size ToolbarActionsBar::GetPreferredSize() const {
163 int icon_count = GetIconCount();
164 if (in_overflow_mode()) {
165 // In overflow, we always have a preferred size of a full row (even if we
166 // don't use it), and always of at least one row. The parent may decide to
167 // show us even when empty, e.g. as a drag target for dragging in icons from
168 // the main container.
169 int row_count = ((std::max(0, icon_count - 1)) /
170 platform_settings_.icons_per_overflow_menu_row) + 1;
171 return gfx::Size(
172 IconCountToWidth(platform_settings_.icons_per_overflow_menu_row),
173 row_count * IconHeight());
176 // If there are no actions to show (and this isn't an overflow container),
177 // then don't show the container at all.
178 if (toolbar_actions_.empty())
179 return gfx::Size();
181 return gfx::Size(IconCountToWidth(icon_count), IconHeight());
184 int ToolbarActionsBar::GetMinimumWidth() const {
185 if (!platform_settings_.chevron_enabled || toolbar_actions_.empty())
186 return kLeftPadding;
187 return kLeftPadding + delegate_->GetChevronWidth() + kRightPadding;
190 int ToolbarActionsBar::GetMaximumWidth() const {
191 return IconCountToWidth(-1);
194 int ToolbarActionsBar::IconCountToWidth(int icons) const {
195 if (icons < 0)
196 icons = toolbar_actions_.size();
197 bool display_chevron =
198 platform_settings_.chevron_enabled &&
199 static_cast<size_t>(icons) < toolbar_actions_.size();
200 if (icons == 0 && !display_chevron)
201 return platform_settings_.left_padding;
202 int icons_size = (icons == 0) ? 0 :
203 (icons * IconWidth(true)) - platform_settings_.item_spacing;
204 int chevron_size = display_chevron ? delegate_->GetChevronWidth() : 0;
205 int padding = platform_settings_.left_padding +
206 platform_settings_.right_padding;
207 return icons_size + chevron_size + padding;
210 size_t ToolbarActionsBar::WidthToIconCount(int pixels) const {
211 // Check for widths large enough to show the entire icon set.
212 if (pixels >= IconCountToWidth(-1))
213 return toolbar_actions_.size();
215 // We reserve space for the padding on either side of the toolbar...
216 int available_space = pixels -
217 (platform_settings_.left_padding + platform_settings_.right_padding);
218 // ... and, if the chevron is enabled, the chevron.
219 if (platform_settings_.chevron_enabled)
220 available_space -= delegate_->GetChevronWidth();
222 // Now we add an extra between-item padding value so the space can be divided
223 // evenly by (size of icon with padding).
224 return static_cast<size_t>(std::max(
225 0, available_space + platform_settings_.item_spacing) / IconWidth(true));
228 size_t ToolbarActionsBar::GetIconCount() const {
229 if (!model_)
230 return 0u;
232 int pop_out_modifier = 0;
233 // If there is a popped out action, it could affect the number of visible
234 // icons - but only if it wouldn't otherwise be visible.
235 if (popped_out_action_) {
236 size_t popped_out_index =
237 std::find(toolbar_actions_.begin(),
238 toolbar_actions_.end(),
239 popped_out_action_) - toolbar_actions_.begin();
240 pop_out_modifier = popped_out_index >= model_->visible_icon_count() ? 1 : 0;
243 // We purposefully do not account for any "popped out" actions in overflow
244 // mode. This is because the popup cannot be showing while the overflow menu
245 // is open, so there's no concern there. Also, if the user has a popped out
246 // action, and immediately opens the overflow menu, we *want* the action there
247 // (since it will close the popup, but do so asynchronously, and we don't
248 // want to "slide" the action back in.
249 size_t visible_icons = in_overflow_mode() ?
250 toolbar_actions_.size() - model_->visible_icon_count() :
251 model_->visible_icon_count() + pop_out_modifier;
253 #if DCHECK_IS_ON()
254 // Good time for some sanity checks: We should never try to display more
255 // icons than we have, and we should always have a view per item in the model.
256 // (The only exception is if this is in initialization.)
257 if (!toolbar_actions_.empty() && !suppress_layout_ &&
258 model_->extensions_initialized()) {
259 size_t num_extension_actions = 0u;
260 for (ToolbarActionViewController* action : toolbar_actions_) {
261 // No component action should ever have a valid extension id, so we can
262 // use this to check the extension amount.
263 if (crx_file::id_util::IdIsValid(action->GetId()))
264 ++num_extension_actions;
267 int num_component_actions =
268 ComponentToolbarActionsFactory::GetInstance()->
269 GetNumComponentActions();
270 size_t num_total_actions = num_extension_actions + num_component_actions;
272 DCHECK_LE(visible_icons, num_total_actions);
273 DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions);
275 #endif
277 return visible_icons;
280 std::vector<ToolbarActionViewController*>
281 ToolbarActionsBar::GetActions() const {
282 std::vector<ToolbarActionViewController*> actions = toolbar_actions_.get();
284 // If there is an action that should be popped out, and it's not visible by
285 // default, make it the final action in the list.
286 if (popped_out_action_) {
287 size_t index =
288 std::find(actions.begin(), actions.end(), popped_out_action_) -
289 actions.begin();
290 DCHECK_NE(actions.size(), index);
291 size_t visible = GetIconCount();
292 if (index >= visible) {
293 size_t rindex = actions.size() - index - 1;
294 std::rotate(actions.rbegin() + rindex,
295 actions.rbegin() + rindex + 1,
296 actions.rend() - visible + 1);
300 return actions;
303 void ToolbarActionsBar::CreateActions() {
304 DCHECK(toolbar_actions_.empty());
305 // We wait for the extension system to be initialized before we add any
306 // actions, as they rely on the extension system to function.
307 if (!model_ || !model_->extensions_initialized())
308 return;
311 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
312 // is fixed.
313 tracked_objects::ScopedTracker tracking_profile1(
314 FROM_HERE_WITH_EXPLICIT_FUNCTION("ToolbarActionsBar::CreateActions1"));
315 // We don't redraw the view while creating actions.
316 base::AutoReset<bool> layout_resetter(&suppress_layout_, true);
318 // Extension actions come first.
319 extensions::ExtensionActionManager* action_manager =
320 extensions::ExtensionActionManager::Get(browser_->profile());
321 const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
322 for (const scoped_refptr<const extensions::Extension>& extension :
323 toolbar_items) {
324 toolbar_actions_.push_back(new ExtensionActionViewController(
325 extension.get(),
326 browser_,
327 action_manager->GetExtensionAction(*extension),
328 this));
331 // Component actions come second, and are suppressed if the extension
332 // actions are being highlighted.
333 if (!model_->is_highlighting()) {
334 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
335 // is fixed.
336 tracked_objects::ScopedTracker tracking_profile2(
337 FROM_HERE_WITH_EXPLICIT_FUNCTION(
338 "ToolbarActionsBar::CreateActions2"));
340 ScopedVector<ToolbarActionViewController> component_actions =
341 ComponentToolbarActionsFactory::GetInstance()->
342 GetComponentToolbarActions();
343 DCHECK(component_actions.empty() ||
344 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled());
345 toolbar_actions_.insert(toolbar_actions_.end(),
346 component_actions.begin(),
347 component_actions.end());
348 component_actions.weak_clear();
351 if (!toolbar_actions_.empty()) {
352 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
353 // is fixed.
354 tracked_objects::ScopedTracker tracking_profile3(
355 FROM_HERE_WITH_EXPLICIT_FUNCTION(
356 "ToolbarActionsBar::CreateActions3"));
357 ReorderActions();
360 tracked_objects::ScopedTracker tracking_profile4(
361 FROM_HERE_WITH_EXPLICIT_FUNCTION("ToolbarActionsBar::CreateActions4"));
363 for (size_t i = 0; i < toolbar_actions_.size(); ++i)
364 delegate_->AddViewForAction(toolbar_actions_[i], i);
367 // Once the actions are created, we should animate the changes.
368 suppress_animation_ = false;
370 // CreateActions() can be called multiple times, so we need to make sure we
371 // haven't already shown the bubble.
372 if (!checked_extension_bubble_) {
373 checked_extension_bubble_ = true;
374 // CreateActions() can be called as part of the browser window set up, which
375 // we need to let finish before showing the actions.
376 scoped_ptr<extensions::ExtensionMessageBubbleController> controller =
377 ExtensionMessageBubbleFactory(browser_->profile()).GetController();
378 if (controller) {
379 base::MessageLoop::current()->PostTask(
380 FROM_HERE,
381 base::Bind(&ToolbarActionsBar::MaybeShowExtensionBubble,
382 weak_ptr_factory_.GetWeakPtr(),
383 base::Passed(controller.Pass())));
388 void ToolbarActionsBar::DeleteActions() {
389 HideActivePopup();
390 delegate_->RemoveAllViews();
391 toolbar_actions_.clear();
394 void ToolbarActionsBar::Update() {
395 if (toolbar_actions_.empty())
396 return; // Nothing to do.
399 // Don't layout until the end.
400 base::AutoReset<bool> layout_resetter(&suppress_layout_, true);
401 for (ToolbarActionViewController* action : toolbar_actions_)
402 action->UpdateState();
405 ReorderActions(); // Also triggers a draw.
408 void ToolbarActionsBar::SetOverflowRowWidth(int width) {
409 DCHECK(in_overflow_mode());
410 platform_settings_.icons_per_overflow_menu_row =
411 std::max((width - kItemSpacing) / IconWidth(true), 1);
414 void ToolbarActionsBar::OnResizeComplete(int width) {
415 DCHECK(!in_overflow_mode()); // The user can't resize the overflow container.
416 size_t resized_count = WidthToIconCount(width);
417 // Save off the desired number of visible icons. We do this now instead of
418 // at the end of the animation so that even if the browser is shut down
419 // while animating, the right value will be restored on next run.
420 model_->SetVisibleIconCount(resized_count);
423 void ToolbarActionsBar::OnDragDrop(int dragged_index,
424 int dropped_index,
425 DragType drag_type) {
426 // All drag-and-drop commands should go to the main bar.
427 if (in_overflow_mode()) {
428 main_bar_->OnDragDrop(dragged_index, dropped_index, drag_type);
429 return;
432 int delta = 0;
433 if (drag_type == DRAG_TO_OVERFLOW)
434 delta = -1;
435 else if (drag_type == DRAG_TO_MAIN)
436 delta = 1;
437 model_->MoveExtensionIcon(toolbar_actions_[dragged_index]->GetId(),
438 dropped_index);
439 if (delta)
440 model_->SetVisibleIconCount(model_->visible_icon_count() + delta);
443 void ToolbarActionsBar::OnAnimationEnded() {
444 // Check if we were waiting for animation to complete to either show a
445 // message bubble, or to show a popup.
446 if (pending_extension_bubble_controller_) {
447 MaybeShowExtensionBubble(pending_extension_bubble_controller_.Pass());
448 } else if (!popped_out_closure_.is_null()) {
449 popped_out_closure_.Run();
450 popped_out_closure_.Reset();
454 bool ToolbarActionsBar::IsActionVisible(
455 const ToolbarActionViewController* action) const {
456 size_t index = std::find(toolbar_actions_.begin(),
457 toolbar_actions_.end(),
458 action) - toolbar_actions_.begin();
459 return index < GetIconCount() || action == popped_out_action_;
462 void ToolbarActionsBar::PopOutAction(ToolbarActionViewController* controller,
463 const base::Closure& closure) {
464 DCHECK(!popped_out_action_) << "Only one action can be popped out at a time!";
465 bool needs_redraw = !IsActionVisible(controller);
466 popped_out_action_ = controller;
467 if (needs_redraw) {
468 // We suppress animation for this draw, because we need the action to get
469 // into position immediately, since it's about to show its popup.
470 base::AutoReset<bool> layout_resetter(&suppress_animation_, false);
471 delegate_->Redraw(true);
474 ResizeDelegate(gfx::Tween::LINEAR, false);
475 if (!delegate_->IsAnimating()) {
476 // Don't call the closure re-entrantly.
477 base::MessageLoop::current()->PostTask(FROM_HERE, closure);
478 } else {
479 popped_out_closure_ = closure;
483 void ToolbarActionsBar::UndoPopOut() {
484 DCHECK(popped_out_action_);
485 ToolbarActionViewController* controller = popped_out_action_;
486 popped_out_action_ = nullptr;
487 popped_out_closure_.Reset();
488 if (!IsActionVisible(controller))
489 delegate_->Redraw(true);
490 ResizeDelegate(gfx::Tween::LINEAR, false);
493 void ToolbarActionsBar::SetPopupOwner(
494 ToolbarActionViewController* popup_owner) {
495 // We should never be setting a popup owner when one already exists, and
496 // never unsetting one when one wasn't set.
497 DCHECK((!popup_owner_ && popup_owner) ||
498 (popup_owner_ && !popup_owner));
499 popup_owner_ = popup_owner;
502 void ToolbarActionsBar::HideActivePopup() {
503 if (popup_owner_)
504 popup_owner_->HidePopup();
505 DCHECK(!popup_owner_);
508 ToolbarActionViewController* ToolbarActionsBar::GetMainControllerForAction(
509 ToolbarActionViewController* action) {
510 return in_overflow_mode() ?
511 main_bar_->GetActionForId(action->GetId()) : action;
514 void ToolbarActionsBar::MaybeShowExtensionBubble(
515 scoped_ptr<extensions::ExtensionMessageBubbleController> controller) {
516 controller->HighlightExtensionsIfNecessary(); // Safe to call multiple times.
517 if (delegate_->IsAnimating()) {
518 // If the toolbar is animating, we can't effectively anchor the bubble,
519 // so wait until animation stops.
520 pending_extension_bubble_controller_ = controller.Pass();
521 } else {
522 const extensions::ExtensionIdList& affected_extensions =
523 controller->GetExtensionIdList();
524 ToolbarActionViewController* anchor_action = nullptr;
525 for (const std::string& id : affected_extensions) {
526 anchor_action = GetActionForId(id);
527 if (anchor_action)
528 break;
530 delegate_->ShowExtensionMessageBubble(controller.Pass(), anchor_action);
534 void ToolbarActionsBar::OnToolbarExtensionAdded(
535 const extensions::Extension* extension,
536 int index) {
537 DCHECK(GetActionForId(extension->id()) == nullptr) <<
538 "Asked to add a toolbar action view for an extension that already exists";
540 toolbar_actions_.insert(
541 toolbar_actions_.begin() + index,
542 new ExtensionActionViewController(
543 extension,
544 browser_,
545 extensions::ExtensionActionManager::Get(browser_->profile())->
546 GetExtensionAction(*extension),
547 this));
549 delegate_->AddViewForAction(toolbar_actions_[index], index);
551 // If we are still initializing the container, don't bother animating.
552 if (!model_->extensions_initialized())
553 return;
555 // We may need to resize (e.g. to show the new icon, or the chevron). We don't
556 // need to check if the extension is upgrading here, because ResizeDelegate()
557 // checks to see if the container is already the proper size, and because
558 // if the action is newly incognito enabled, even though it's a reload, it's
559 // a new extension to this toolbar.
560 // We suppress the chevron during animation because, if we're expanding to
561 // show a new icon, we don't want to have the chevron visible only for the
562 // duration of the animation.
563 ResizeDelegate(gfx::Tween::LINEAR, true);
566 void ToolbarActionsBar::OnToolbarExtensionRemoved(
567 const extensions::Extension* extension) {
568 ToolbarActions::iterator iter = toolbar_actions_.begin();
569 while (iter != toolbar_actions_.end() && (*iter)->GetId() != extension->id())
570 ++iter;
572 if (iter == toolbar_actions_.end())
573 return;
575 // The action should outlive the UI element (which is owned by the delegate),
576 // so we can't delete it just yet. But we should remove it from the list of
577 // actions so that any width calculations are correct.
578 scoped_ptr<ToolbarActionViewController> removed_action(*iter);
579 toolbar_actions_.weak_erase(iter);
580 delegate_->RemoveViewForAction(removed_action.get());
581 removed_action.reset();
583 // If the extension is being upgraded we don't want the bar to shrink
584 // because the icon is just going to get re-added to the same location.
585 // There is an exception if this is an off-the-record profile, and the
586 // extension is no longer incognito-enabled.
587 if (!extensions::ExtensionSystem::Get(browser_->profile())->runtime_data()->
588 IsBeingUpgraded(extension->id()) ||
589 (browser_->profile()->IsOffTheRecord() &&
590 !extensions::util::IsIncognitoEnabled(extension->id(),
591 browser_->profile()))) {
592 if (toolbar_actions_.size() > model_->visible_icon_count()) {
593 // If we have more icons than we can show, then we must not be changing
594 // the container size (since we either removed an icon from the main
595 // area and one from the overflow list will have shifted in, or we
596 // removed an entry directly from the overflow list).
597 delegate_->Redraw(false);
598 } else {
599 delegate_->SetChevronVisibility(false);
600 // Either we went from overflow to no-overflow, or we shrunk the no-
601 // overflow container by 1. Either way the size changed, so animate.
602 ResizeDelegate(gfx::Tween::EASE_OUT, false);
607 void ToolbarActionsBar::OnToolbarExtensionMoved(
608 const extensions::Extension* extension,
609 int index) {
610 DCHECK(index >= 0 && index < static_cast<int>(toolbar_actions_.size()));
611 // Unfortunately, |index| doesn't really mean a lot to us, because this
612 // window's toolbar could be different (if actions are popped out). Just
613 // do a full reorder.
614 ReorderActions();
617 void ToolbarActionsBar::OnToolbarExtensionUpdated(
618 const extensions::Extension* extension) {
619 ToolbarActionViewController* action = GetActionForId(extension->id());
620 // There might not be a view in cases where we are highlighting or if we
621 // haven't fully initialized the actions.
622 if (action) {
623 action->UpdateState();
624 SetOverflowedActionWantsToRun();
628 bool ToolbarActionsBar::ShowExtensionActionPopup(
629 const extensions::Extension* extension,
630 bool grant_active_tab) {
631 // Don't override another popup, and only show in the active window.
632 if (popup_owner() || !browser_->window()->IsActive())
633 return false;
635 ToolbarActionViewController* action = GetActionForId(extension->id());
636 return action && action->ExecuteAction(grant_active_tab);
639 void ToolbarActionsBar::OnToolbarVisibleCountChanged() {
640 ResizeDelegate(gfx::Tween::EASE_OUT, false);
641 SetOverflowedActionWantsToRun();
644 void ToolbarActionsBar::ResizeDelegate(gfx::Tween::Type tween_type,
645 bool suppress_chevron) {
646 int desired_width = GetPreferredSize().width();
647 if (desired_width != delegate_->GetWidth()) {
648 delegate_->ResizeAndAnimate(tween_type, desired_width, suppress_chevron);
649 } else if (delegate_->IsAnimating()) {
650 // It's possible that we're right where we're supposed to be in terms of
651 // width, but that we're also currently resizing. If this is the case, end
652 // the current animation with the current width.
653 delegate_->StopAnimating();
654 } else {
655 // We may already be at the right size (this can happen frequently with
656 // overflow, where we have a fixed width, and in tests, where we skip
657 // animations). If this is the case, we still need to Redraw(), because the
658 // icons within the toolbar may have changed (e.g. if we removed one
659 // action and added a different one in quick succession).
660 delegate_->Redraw(false);
664 void ToolbarActionsBar::OnToolbarHighlightModeChanged(bool is_highlighting) {
665 // It's a bit of a pain that we delete and recreate everything here, but given
666 // everything else going on (the lack of highlight, [n] more extensions
667 // appearing, etc), it's not worth the extra complexity to create and insert
668 // only the new actions.
669 DeleteActions();
670 CreateActions();
671 // Resize the delegate. We suppress the chevron so that we don't risk showing
672 // it only for the duration of the animation.
673 ResizeDelegate(gfx::Tween::LINEAR, true);
676 void ToolbarActionsBar::OnToolbarModelInitialized() {
677 // We shouldn't have any actions before the model is initialized.
678 DCHECK(toolbar_actions_.empty());
679 CreateActions();
681 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337 is
682 // fixed.
683 tracked_objects::ScopedTracker tracking_profile(
684 FROM_HERE_WITH_EXPLICIT_FUNCTION(
685 "ToolbarActionsBar::OnToolbarModelInitialized"));
686 ResizeDelegate(gfx::Tween::EASE_OUT, false);
689 Browser* ToolbarActionsBar::GetBrowser() {
690 return browser_;
693 void ToolbarActionsBar::ReorderActions() {
694 if (toolbar_actions_.empty())
695 return;
697 // First, reset the order to that of the model.
698 auto compare = [](ToolbarActionViewController* const& action,
699 const scoped_refptr<const extensions::Extension>& ext) {
700 return action->GetId() == ext->id();
702 SortContainer(&toolbar_actions_.get(), model_->toolbar_items(), compare);
704 // Our visible browser actions may have changed - re-Layout() and check the
705 // size (if we aren't suppressing the layout).
706 if (!suppress_layout_) {
707 ResizeDelegate(gfx::Tween::EASE_OUT, false);
708 delegate_->Redraw(true);
711 SetOverflowedActionWantsToRun();
714 void ToolbarActionsBar::SetOverflowedActionWantsToRun() {
715 if (in_overflow_mode())
716 return;
717 bool overflowed_action_wants_to_run = false;
718 content::WebContents* web_contents = GetCurrentWebContents();
719 for (size_t i = GetIconCount(); i < toolbar_actions_.size(); ++i) {
720 if (toolbar_actions_[i]->WantsToRun(web_contents)) {
721 overflowed_action_wants_to_run = true;
722 break;
726 if (overflowed_action_wants_to_run_ != overflowed_action_wants_to_run) {
727 overflowed_action_wants_to_run_ = overflowed_action_wants_to_run;
728 if (send_overflowed_action_changes_)
729 delegate_->OnOverflowedActionWantsToRunChanged(
730 overflowed_action_wants_to_run_);
734 ToolbarActionViewController* ToolbarActionsBar::GetActionForId(
735 const std::string& id) {
736 for (ToolbarActionViewController* action : toolbar_actions_) {
737 if (action->GetId() == id)
738 return action;
740 return nullptr;
743 content::WebContents* ToolbarActionsBar::GetCurrentWebContents() {
744 return browser_->tab_strip_model()->GetActiveWebContents();