Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / bookmarks / bookmark_bar_view.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <set>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/i18n/rtl.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/defaults.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/sync/profile_sync_service.h"
25 #include "chrome/browser/sync/profile_sync_service_factory.h"
26 #include "chrome/browser/themes/theme_properties.h"
27 #include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
28 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
29 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
30 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/chrome_pages.h"
33 #include "chrome/browser/ui/elide_url.h"
34 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
35 #include "chrome/browser/ui/omnibox/omnibox_view.h"
36 #include "chrome/browser/ui/tabs/tab_strip_model.h"
37 #include "chrome/browser/ui/view_ids.h"
38 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h"
39 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
40 #include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h"
41 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
42 #include "chrome/browser/ui/views/event_utils.h"
43 #include "chrome/browser/ui/views/frame/browser_view.h"
44 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
45 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
46 #include "chrome/common/chrome_switches.h"
47 #include "chrome/common/extensions/extension_constants.h"
48 #include "chrome/common/pref_names.h"
49 #include "chrome/common/url_constants.h"
50 #include "components/bookmarks/core/browser/bookmark_model.h"
51 #include "content/public/browser/notification_details.h"
52 #include "content/public/browser/notification_source.h"
53 #include "content/public/browser/page_navigator.h"
54 #include "content/public/browser/render_view_host.h"
55 #include "content/public/browser/render_widget_host_view.h"
56 #include "content/public/browser/user_metrics.h"
57 #include "content/public/browser/web_contents.h"
58 #include "content/public/common/page_transition_types.h"
59 #include "extensions/browser/extension_registry.h"
60 #include "extensions/common/extension.h"
61 #include "extensions/common/extension_set.h"
62 #include "grit/generated_resources.h"
63 #include "grit/theme_resources.h"
64 #include "grit/ui_resources.h"
65 #include "ui/accessibility/ax_view_state.h"
66 #include "ui/base/dragdrop/drag_utils.h"
67 #include "ui/base/dragdrop/os_exchange_data.h"
68 #include "ui/base/l10n/l10n_util.h"
69 #include "ui/base/resource/resource_bundle.h"
70 #include "ui/base/theme_provider.h"
71 #include "ui/base/window_open_disposition.h"
72 #include "ui/gfx/animation/slide_animation.h"
73 #include "ui/gfx/canvas.h"
74 #include "ui/gfx/text_elider.h"
75 #include "ui/views/button_drag_utils.h"
76 #include "ui/views/controls/button/menu_button.h"
77 #include "ui/views/controls/label.h"
78 #include "ui/views/drag_utils.h"
79 #include "ui/views/metrics.h"
80 #include "ui/views/view_constants.h"
81 #include "ui/views/widget/tooltip_manager.h"
82 #include "ui/views/widget/widget.h"
83 #include "ui/views/window/non_client_view.h"
84
85 using base::UserMetricsAction;
86 using content::OpenURLParams;
87 using content::PageNavigator;
88 using content::Referrer;
89 using ui::DropTargetEvent;
90 using views::CustomButton;
91 using views::MenuButton;
92 using views::View;
93
94 // Margins around the content.
95 static const int kDetachedTopMargin = 1;  // When attached, we use 0 and let the
96                                           // toolbar above serve as the margin.
97 static const int kBottomMargin = 2;
98 static const int kLeftMargin = 1;
99 static const int kRightMargin = 1;
100
101 // static
102 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView";
103
104 // Padding between buttons.
105 static const int kButtonPadding = 0;
106
107 // Icon to display when one isn't found for the page.
108 static gfx::ImageSkia* kDefaultFavicon = NULL;
109
110 // Icon used for folders.
111 static gfx::ImageSkia* kFolderIcon = NULL;
112
113 // Color of the drop indicator.
114 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
115
116 // Width of the drop indicator.
117 static const int kDropIndicatorWidth = 2;
118
119 // Distance between the bottom of the bar and the separator.
120 static const int kSeparatorMargin = 1;
121
122 // Width of the separator between the recently bookmarked button and the
123 // overflow indicator.
124 static const int kSeparatorWidth = 4;
125
126 // Starting x-coordinate of the separator line within a separator.
127 static const int kSeparatorStartX = 2;
128
129 // Left-padding for the instructional text.
130 static const int kInstructionsPadding = 6;
131
132 // Tag for the 'Other bookmarks' button.
133 static const int kOtherFolderButtonTag = 1;
134
135 // Tag for the 'Apps Shortcut' button.
136 static const int kAppsShortcutButtonTag = 2;
137
138 namespace {
139
140 // To enable/disable BookmarkBar animations during testing. In production
141 // animations are enabled by default.
142 bool animations_enabled = true;
143
144 // BookmarkButtonBase -----------------------------------------------
145
146 // Base class for text buttons used on the bookmark bar.
147
148 class BookmarkButtonBase : public views::TextButton {
149  public:
150   BookmarkButtonBase(views::ButtonListener* listener,
151                      const base::string16& title)
152       : TextButton(listener, title) {
153     show_animation_.reset(new gfx::SlideAnimation(this));
154     if (!animations_enabled) {
155       // For some reason during testing the events generated by animating
156       // throw off the test. So, don't animate while testing.
157       show_animation_->Reset(1);
158     } else {
159       show_animation_->Show();
160     }
161   }
162
163   virtual bool IsTriggerableEvent(const ui::Event& e) OVERRIDE {
164     return e.type() == ui::ET_GESTURE_TAP ||
165            e.type() == ui::ET_GESTURE_TAP_DOWN ||
166            event_utils::IsPossibleDispositionEvent(e);
167   }
168
169  private:
170   scoped_ptr<gfx::SlideAnimation> show_animation_;
171
172   DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase);
173 };
174
175 // BookmarkButton -------------------------------------------------------------
176
177 // Buttons used for the bookmarks on the bookmark bar.
178
179 class BookmarkButton : public BookmarkButtonBase {
180  public:
181   // The internal view class name.
182   static const char kViewClassName[];
183
184   BookmarkButton(views::ButtonListener* listener,
185                  const GURL& url,
186                  const base::string16& title,
187                  Profile* profile)
188       : BookmarkButtonBase(listener, title),
189         url_(url),
190         profile_(profile) {
191   }
192
193   virtual bool GetTooltipText(const gfx::Point& p,
194                               base::string16* tooltip) const OVERRIDE {
195     gfx::Point location(p);
196     ConvertPointToScreen(this, &location);
197     *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle(
198         GetWidget(), location, url_, text(), profile_);
199     return !tooltip->empty();
200   }
201
202   virtual const char* GetClassName() const OVERRIDE {
203     return kViewClassName;
204   }
205
206  private:
207   const GURL& url_;
208   Profile* profile_;
209
210   DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
211 };
212
213 // static
214 const char BookmarkButton::kViewClassName[] = "BookmarkButton";
215
216 // ShortcutButton -------------------------------------------------------------
217
218 // Buttons used for the shortcuts on the bookmark bar.
219
220 class ShortcutButton : public BookmarkButtonBase {
221  public:
222   // The internal view class name.
223   static const char kViewClassName[];
224
225   ShortcutButton(views::ButtonListener* listener,
226                  const base::string16& title)
227       : BookmarkButtonBase(listener, title) {
228   }
229
230   virtual const char* GetClassName() const OVERRIDE {
231     return kViewClassName;
232   }
233
234  private:
235
236   DISALLOW_COPY_AND_ASSIGN(ShortcutButton);
237 };
238
239 // static
240 const char ShortcutButton::kViewClassName[] = "ShortcutButton";
241
242 // BookmarkFolderButton -------------------------------------------------------
243
244 // Buttons used for folders on the bookmark bar, including the 'other folders'
245 // button.
246 class BookmarkFolderButton : public views::MenuButton {
247  public:
248   BookmarkFolderButton(views::ButtonListener* listener,
249                        const base::string16& title,
250                        views::MenuButtonListener* menu_button_listener,
251                        bool show_menu_marker)
252       : MenuButton(listener, title, menu_button_listener, show_menu_marker) {
253     show_animation_.reset(new gfx::SlideAnimation(this));
254     if (!animations_enabled) {
255       // For some reason during testing the events generated by animating
256       // throw off the test. So, don't animate while testing.
257       show_animation_->Reset(1);
258     } else {
259       show_animation_->Show();
260     }
261   }
262
263   virtual bool GetTooltipText(const gfx::Point& p,
264                               base::string16* tooltip) const OVERRIDE {
265     if (text_size_.width() > GetTextBounds().width())
266       *tooltip = text_;
267     return !tooltip->empty();
268   }
269
270   virtual bool IsTriggerableEvent(const ui::Event& e) OVERRIDE {
271     // Left clicks and taps should show the menu contents and right clicks
272     // should show the context menu. They should not trigger the opening of
273     // underlying urls.
274     if (e.type() == ui::ET_GESTURE_TAP ||
275         (e.IsMouseEvent() && (e.flags() &
276              (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON))))
277       return false;
278
279     if (e.IsMouseEvent())
280       return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB;
281     return false;
282   }
283
284   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
285     views::MenuButton::PaintButton(canvas, views::MenuButton::PB_NORMAL);
286   }
287
288  private:
289   scoped_ptr<gfx::SlideAnimation> show_animation_;
290
291   DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
292 };
293
294 // OverFlowButton (chevron) --------------------------------------------------
295
296 class OverFlowButton : public views::MenuButton {
297  public:
298   explicit OverFlowButton(BookmarkBarView* owner)
299       : MenuButton(NULL, base::string16(), owner, false),
300         owner_(owner) {}
301
302   virtual bool OnMousePressed(const ui::MouseEvent& e) OVERRIDE {
303     owner_->StopThrobbing(true);
304     return views::MenuButton::OnMousePressed(e);
305   }
306
307  private:
308   BookmarkBarView* owner_;
309
310   DISALLOW_COPY_AND_ASSIGN(OverFlowButton);
311 };
312
313 void RecordAppLaunch(Profile* profile, GURL url) {
314   const extensions::Extension* extension =
315       extensions::ExtensionRegistry::Get(profile)
316           ->enabled_extensions().GetAppByURL(url);
317   if (!extension)
318     return;
319
320   CoreAppLauncherHandler::RecordAppLaunchType(
321       extension_misc::APP_LAUNCH_BOOKMARK_BAR,
322       extension->GetType());
323 }
324
325 }  // namespace
326
327 // DropLocation ---------------------------------------------------------------
328
329 struct BookmarkBarView::DropLocation {
330   DropLocation()
331       : index(-1),
332         operation(ui::DragDropTypes::DRAG_NONE),
333         on(false),
334         button_type(DROP_BOOKMARK) {
335   }
336
337   bool Equals(const DropLocation& other) {
338     return ((other.index == index) && (other.on == on) &&
339             (other.button_type == button_type));
340   }
341
342   // Index into the model the drop is over. This is relative to the root node.
343   int index;
344
345   // Drop constants.
346   int operation;
347
348   // If true, the user is dropping on a folder.
349   bool on;
350
351   // Type of button.
352   DropButtonType button_type;
353 };
354
355 // DropInfo -------------------------------------------------------------------
356
357 // Tracks drops on the BookmarkBarView.
358
359 struct BookmarkBarView::DropInfo {
360   DropInfo()
361       : valid(false),
362         is_menu_showing(false),
363         x(0),
364         y(0) {
365   }
366
367   // Whether the data is valid.
368   bool valid;
369
370   // If true, the menu is being shown.
371   bool is_menu_showing;
372
373   // Coordinates of the drag (in terms of the BookmarkBarView).
374   int x;
375   int y;
376
377   // DropData for the drop.
378   BookmarkNodeData data;
379
380   DropLocation location;
381 };
382
383 // ButtonSeparatorView  --------------------------------------------------------
384
385 class BookmarkBarView::ButtonSeparatorView : public views::View {
386  public:
387   ButtonSeparatorView() {}
388   virtual ~ButtonSeparatorView() {}
389
390   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
391     DetachableToolbarView::PaintVerticalDivider(
392         canvas, kSeparatorStartX, height(), 1,
393         DetachableToolbarView::kEdgeDividerColor,
394         DetachableToolbarView::kMiddleDividerColor,
395         GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR));
396   }
397
398   virtual gfx::Size GetPreferredSize() OVERRIDE {
399     // We get the full height of the bookmark bar, so that the height returned
400     // here doesn't matter.
401     return gfx::Size(kSeparatorWidth, 1);
402   }
403
404   virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
405     state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR);
406     state->role = ui::AX_ROLE_SPLITTER;
407   }
408
409  private:
410   DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
411 };
412
413 // BookmarkBarView ------------------------------------------------------------
414
415 // static
416 const int BookmarkBarView::kMaxButtonWidth = 150;
417 const int BookmarkBarView::kNewtabHorizontalPadding = 2;
418 const int BookmarkBarView::kToolbarAttachedBookmarkBarOverlap = 3;
419
420 static const gfx::ImageSkia& GetDefaultFavicon() {
421   if (!kDefaultFavicon) {
422     ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
423     kDefaultFavicon = rb->GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
424   }
425   return *kDefaultFavicon;
426 }
427
428 static const gfx::ImageSkia& GetFolderIcon() {
429   if (!kFolderIcon) {
430     ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
431     kFolderIcon = rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER);
432   }
433   return *kFolderIcon;
434 }
435
436 BookmarkBarView::BookmarkBarView(Browser* browser, BrowserView* browser_view)
437     : page_navigator_(NULL),
438       model_(NULL),
439       bookmark_menu_(NULL),
440       bookmark_drop_menu_(NULL),
441       other_bookmarked_button_(NULL),
442       apps_page_shortcut_(NULL),
443       show_folder_method_factory_(this),
444       overflow_button_(NULL),
445       instructions_(NULL),
446       bookmarks_separator_view_(NULL),
447       browser_(browser),
448       browser_view_(browser_view),
449       infobar_visible_(false),
450       throbbing_view_(NULL),
451       bookmark_bar_state_(BookmarkBar::SHOW),
452       animating_detached_(false) {
453   set_id(VIEW_ID_BOOKMARK_BAR);
454   Init();
455
456   size_animation_->Reset(1);
457 }
458
459 BookmarkBarView::~BookmarkBarView() {
460   if (model_)
461     model_->RemoveObserver(this);
462
463   // It's possible for the menu to outlive us, reset the observer to make sure
464   // it doesn't have a reference to us.
465   if (bookmark_menu_) {
466     bookmark_menu_->set_observer(NULL);
467     bookmark_menu_->SetPageNavigator(NULL);
468     bookmark_menu_->clear_bookmark_bar();
469   }
470   if (context_menu_.get())
471     context_menu_->SetPageNavigator(NULL);
472
473   StopShowFolderDropMenuTimer();
474 }
475
476 // static
477 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) {
478   animations_enabled = !disabled;
479 }
480
481 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
482   page_navigator_ = navigator;
483   if (bookmark_menu_)
484     bookmark_menu_->SetPageNavigator(navigator);
485   if (context_menu_.get())
486     context_menu_->SetPageNavigator(navigator);
487 }
488
489 void BookmarkBarView::SetBookmarkBarState(
490     BookmarkBar::State state,
491     BookmarkBar::AnimateChangeType animate_type) {
492   if (animate_type == BookmarkBar::ANIMATE_STATE_CHANGE &&
493       animations_enabled) {
494     animating_detached_ = (state == BookmarkBar::DETACHED ||
495                            bookmark_bar_state_ == BookmarkBar::DETACHED);
496     if (state == BookmarkBar::SHOW)
497       size_animation_->Show();
498     else
499       size_animation_->Hide();
500   } else {
501     size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0);
502   }
503   bookmark_bar_state_ = state;
504 }
505
506 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const {
507   if (!infobar_visible_ && browser_->window()->IsFullscreen()) {
508     // There is no client edge to overlap when detached in fullscreen with no
509     // infobars visible.
510     return 0;
511   }
512   return views::NonClientFrameView::kClientEdgeThickness;
513 }
514
515 bool BookmarkBarView::is_animating() {
516   return size_animation_->is_animating();
517 }
518
519 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
520     const gfx::Point& loc,
521     int* model_start_index) {
522   *model_start_index = 0;
523
524   if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
525     return NULL;
526
527   gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
528
529   // Check the buttons first.
530   for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
531     views::View* child = child_at(i);
532     if (!child->visible())
533       break;
534     if (child->bounds().Contains(adjusted_loc))
535       return model_->bookmark_bar_node()->GetChild(i);
536   }
537
538   // Then the overflow button.
539   if (overflow_button_->visible() &&
540       overflow_button_->bounds().Contains(adjusted_loc)) {
541     *model_start_index = GetFirstHiddenNodeIndex();
542     return model_->bookmark_bar_node();
543   }
544
545   // And finally the other folder.
546   if (other_bookmarked_button_->visible() &&
547       other_bookmarked_button_->bounds().Contains(adjusted_loc)) {
548     return model_->other_node();
549   }
550
551   return NULL;
552 }
553
554 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
555     const BookmarkNode* node) {
556   if (node == model_->other_node())
557     return other_bookmarked_button_;
558   if (node == model_->bookmark_bar_node())
559     return overflow_button_;
560   int index = model_->bookmark_bar_node()->GetIndexOf(node);
561   if (index == -1 || !node->is_folder())
562     return NULL;
563   return static_cast<views::MenuButton*>(child_at(index));
564 }
565
566 void BookmarkBarView::GetAnchorPositionForButton(
567     views::MenuButton* button,
568     views::MenuAnchorPosition* anchor) {
569   if (button == other_bookmarked_button_ || button == overflow_button_)
570     *anchor = views::MENU_ANCHOR_TOPRIGHT;
571   else
572     *anchor = views::MENU_ANCHOR_TOPLEFT;
573 }
574
575 views::MenuItemView* BookmarkBarView::GetMenu() {
576   return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
577 }
578
579 views::MenuItemView* BookmarkBarView::GetContextMenu() {
580   return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
581 }
582
583 views::MenuItemView* BookmarkBarView::GetDropMenu() {
584   return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
585 }
586
587 void BookmarkBarView::StopThrobbing(bool immediate) {
588   if (!throbbing_view_)
589     return;
590
591   // If not immediate, cycle through 2 more complete cycles.
592   throbbing_view_->StartThrobbing(immediate ? 0 : 4);
593   throbbing_view_ = NULL;
594 }
595
596 // static
597 base::string16 BookmarkBarView::CreateToolTipForURLAndTitle(
598     const views::Widget* widget,
599     const gfx::Point& screen_loc,
600     const GURL& url,
601     const base::string16& title,
602     Profile* profile) {
603   int max_width = views::TooltipManager::GetMaxWidth(
604       screen_loc.x(),
605       screen_loc.y(),
606       widget->GetNativeView());
607   const gfx::FontList tt_fonts = widget->GetTooltipManager()->GetFontList();
608   base::string16 result;
609
610   // First the title.
611   if (!title.empty()) {
612     base::string16 localized_title = title;
613     base::i18n::AdjustStringForLocaleDirection(&localized_title);
614     result.append(ElideText(localized_title, tt_fonts, max_width,
615                             gfx::ELIDE_AT_END));
616   }
617
618   // Only show the URL if the url and title differ.
619   if (title != base::UTF8ToUTF16(url.spec())) {
620     if (!result.empty())
621       result.push_back('\n');
622
623     // We need to explicitly specify the directionality of the URL's text to
624     // make sure it is treated as an LTR string when the context is RTL. For
625     // example, the URL "http://www.yahoo.com/" appears as
626     // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
627     // the Unicode BiDi algorithm puts certain characters on the left by
628     // default.
629     std::string languages = profile->GetPrefs()->GetString(
630         prefs::kAcceptLanguages);
631     base::string16 elided_url(ElideUrl(url, tt_fonts, max_width, languages));
632     elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
633     result.append(elided_url);
634   }
635   return result;
636 }
637
638 bool BookmarkBarView::IsDetached() const {
639   return (bookmark_bar_state_ == BookmarkBar::DETACHED) ||
640       (animating_detached_ && size_animation_->is_animating());
641 }
642
643 double BookmarkBarView::GetAnimationValue() const {
644   return size_animation_->GetCurrentValue();
645 }
646
647 int BookmarkBarView::GetToolbarOverlap() const {
648   int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
649       views::NonClientFrameView::kClientEdgeThickness;
650   if (!IsDetached())
651     return attached_overlap;
652
653   int detached_overlap = GetFullyDetachedToolbarOverlap();
654
655   // Do not animate the overlap when the infobar is above us (i.e. when we're
656   // detached), since drawing over the infobar looks weird.
657   if (infobar_visible_)
658     return detached_overlap;
659
660   // When detached with no infobar, animate the overlap between the attached and
661   // detached states.
662   return detached_overlap + static_cast<int>(
663       (attached_overlap - detached_overlap) *
664           size_animation_->GetCurrentValue());
665 }
666
667 gfx::Size BookmarkBarView::GetPreferredSize() {
668   return LayoutItems(true);
669 }
670
671 bool BookmarkBarView::HitTestRect(const gfx::Rect& rect) const {
672   // If bookmark bar is attached and omnibox popup is open (on top of the bar),
673   // force hit-testing to fail.  This prevents hovers/clicks just above the
674   // omnibox popup from activating the top few pixels of items on the bookmark
675   // bar.
676   if (!IsDetached() && browser_view_ &&
677       browser_view_->GetLocationBar()->GetOmniboxView()->model()->
678           popup_model()->IsOpen()) {
679     return false;
680   }
681   return DetachableToolbarView::HitTestRect(rect);
682 }
683
684 gfx::Size BookmarkBarView::GetMinimumSize() {
685   // The minimum width of the bookmark bar should at least contain the overflow
686   // button, by which one can access all the Bookmark Bar items, and the "Other
687   // Bookmarks" folder, along with appropriate margins and button padding.
688   int width = kLeftMargin;
689
690   int height = chrome::kBookmarkBarHeight;
691   if (IsDetached()) {
692     double current_state = 1 - size_animation_->GetCurrentValue();
693     width += 2 * static_cast<int>(kNewtabHorizontalPadding * current_state);
694     height += static_cast<int>(
695         (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
696             current_state);
697   }
698
699   gfx::Size other_bookmarked_pref;
700   if (other_bookmarked_button_->visible())
701     other_bookmarked_pref = other_bookmarked_button_->GetPreferredSize();
702   gfx::Size overflow_pref;
703   if (overflow_button_->visible())
704     overflow_pref = overflow_button_->GetPreferredSize();
705   gfx::Size bookmarks_separator_pref;
706   if (bookmarks_separator_view_->visible())
707     bookmarks_separator_pref = bookmarks_separator_view_->GetPreferredSize();
708
709   gfx::Size apps_page_shortcut_pref;
710   if (apps_page_shortcut_->visible())
711     apps_page_shortcut_pref = apps_page_shortcut_->GetPreferredSize();
712   width += other_bookmarked_pref.width() + kButtonPadding +
713       apps_page_shortcut_pref.width() + kButtonPadding +
714       overflow_pref.width() + kButtonPadding +
715       bookmarks_separator_pref.width();
716
717   return gfx::Size(width, height);
718 }
719
720 void BookmarkBarView::Layout() {
721   LayoutItems(false);
722 }
723
724 void BookmarkBarView::ViewHierarchyChanged(
725     const ViewHierarchyChangedDetails& details) {
726   if (details.is_add && details.child == this) {
727     // We may get inserted into a hierarchy with a profile - this typically
728     // occurs when the bar's contents get populated fast enough that the
729     // buttons are created before the bar is attached to a frame.
730     UpdateColors();
731
732     if (height() > 0) {
733       // We only layout while parented. When we become parented, if our bounds
734       // haven't changed, OnBoundsChanged() won't get invoked and we won't
735       // layout. Therefore we always force a layout when added.
736       Layout();
737     }
738   }
739 }
740
741 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas) {
742   View::PaintChildren(canvas);
743
744   if (drop_info_.get() && drop_info_->valid &&
745       drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
746       drop_info_->location.button_type != DROP_OVERFLOW &&
747       !drop_info_->location.on) {
748     int index = drop_info_->location.index;
749     DCHECK(index <= GetBookmarkButtonCount());
750     int x = 0;
751     int y = 0;
752     int h = height();
753     if (index == GetBookmarkButtonCount()) {
754       if (index == 0) {
755         x = kLeftMargin;
756       } else {
757         x = GetBookmarkButton(index - 1)->x() +
758             GetBookmarkButton(index - 1)->width();
759       }
760     } else {
761       x = GetBookmarkButton(index)->x();
762     }
763     if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
764       y = GetBookmarkButton(0)->y();
765       h = GetBookmarkButton(0)->height();
766     }
767
768     // Since the drop indicator is painted directly onto the canvas, we must
769     // make sure it is painted in the right location if the locale is RTL.
770     gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
771                                y,
772                                kDropIndicatorWidth,
773                                h);
774     indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
775
776     // TODO(sky/glen): make me pretty!
777     canvas->FillRect(indicator_bounds, kDropIndicatorColor);
778   }
779 }
780
781 bool BookmarkBarView::GetDropFormats(
782       int* formats,
783       std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
784   if (!model_ || !model_->loaded())
785     return false;
786   *formats = ui::OSExchangeData::URL;
787   custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat());
788   return true;
789 }
790
791 bool BookmarkBarView::AreDropTypesRequired() {
792   return true;
793 }
794
795 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
796   if (!model_ || !model_->loaded() ||
797       !browser_->profile()->GetPrefs()->GetBoolean(
798           prefs::kEditBookmarksEnabled))
799     return false;
800
801   if (!drop_info_.get())
802     drop_info_.reset(new DropInfo());
803
804   // Only accept drops of 1 node, which is the case for all data dragged from
805   // bookmark bar and menus.
806   return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
807 }
808
809 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
810 }
811
812 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
813   if (!drop_info_.get())
814     return 0;
815
816   if (drop_info_->valid &&
817       (drop_info_->x == event.x() && drop_info_->y == event.y())) {
818     // The location of the mouse didn't change, return the last operation.
819     return drop_info_->location.operation;
820   }
821
822   drop_info_->x = event.x();
823   drop_info_->y = event.y();
824
825   DropLocation location;
826   CalculateDropLocation(event, drop_info_->data, &location);
827
828   if (drop_info_->valid && drop_info_->location.Equals(location)) {
829     // The position we're going to drop didn't change, return the last drag
830     // operation we calculated. Copy of the operation in case it changed.
831     drop_info_->location.operation = location.operation;
832     return drop_info_->location.operation;
833   }
834
835   StopShowFolderDropMenuTimer();
836
837   // TODO(sky): Optimize paint region.
838   SchedulePaint();
839
840   drop_info_->location = location;
841   drop_info_->valid = true;
842
843   if (drop_info_->is_menu_showing) {
844     if (bookmark_drop_menu_)
845       bookmark_drop_menu_->Cancel();
846     drop_info_->is_menu_showing = false;
847   }
848
849   if (location.on || location.button_type == DROP_OVERFLOW ||
850       location.button_type == DROP_OTHER_FOLDER) {
851     const BookmarkNode* node;
852     if (location.button_type == DROP_OTHER_FOLDER)
853       node = model_->other_node();
854     else if (location.button_type == DROP_OVERFLOW)
855       node = model_->bookmark_bar_node();
856     else
857       node = model_->bookmark_bar_node()->GetChild(location.index);
858     StartShowFolderDropMenuTimer(node);
859   }
860
861   return drop_info_->location.operation;
862 }
863
864 void BookmarkBarView::OnDragExited() {
865   StopShowFolderDropMenuTimer();
866
867   // NOTE: we don't hide the menu on exit as it's possible the user moved the
868   // mouse over the menu, which triggers an exit on us.
869
870   drop_info_->valid = false;
871
872   if (drop_info_->location.index != -1) {
873     // TODO(sky): optimize the paint region.
874     SchedulePaint();
875   }
876   drop_info_.reset();
877 }
878
879 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
880   StopShowFolderDropMenuTimer();
881
882   if (bookmark_drop_menu_)
883     bookmark_drop_menu_->Cancel();
884
885   if (!drop_info_.get() || !drop_info_->location.operation)
886     return ui::DragDropTypes::DRAG_NONE;
887
888   const BookmarkNode* root =
889       (drop_info_->location.button_type == DROP_OTHER_FOLDER) ?
890       model_->other_node() : model_->bookmark_bar_node();
891   int index = drop_info_->location.index;
892
893   if (index != -1) {
894     // TODO(sky): optimize the SchedulePaint region.
895     SchedulePaint();
896   }
897   const BookmarkNode* parent_node;
898   if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
899     parent_node = root;
900     index = parent_node->child_count();
901   } else if (drop_info_->location.on) {
902     parent_node = root->GetChild(index);
903     index = parent_node->child_count();
904   } else {
905     parent_node = root;
906   }
907   const BookmarkNodeData data = drop_info_->data;
908   DCHECK(data.is_valid());
909   drop_info_.reset();
910   return chrome::DropBookmarks(browser_->profile(), data, parent_node, index);
911 }
912
913 void BookmarkBarView::OnThemeChanged() {
914   UpdateColors();
915 }
916
917 const char* BookmarkBarView::GetClassName() const {
918   return kViewClassName;
919 }
920
921 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) {
922   state->role = ui::AX_ROLE_TOOLBAR;
923   state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
924 }
925
926 void BookmarkBarView::AnimationProgressed(const gfx::Animation* animation) {
927   // |browser_view_| can be NULL during tests.
928   if (browser_view_)
929     browser_view_->ToolbarSizeChanged(true);
930 }
931
932 void BookmarkBarView::AnimationEnded(const gfx::Animation* animation) {
933   // |browser_view_| can be NULL during tests.
934   if (browser_view_) {
935     browser_view_->ToolbarSizeChanged(false);
936     SchedulePaint();
937   }
938 }
939
940 void BookmarkBarView::BookmarkMenuControllerDeleted(
941     BookmarkMenuController* controller) {
942   if (controller == bookmark_menu_)
943     bookmark_menu_ = NULL;
944   else if (controller == bookmark_drop_menu_)
945     bookmark_drop_menu_ = NULL;
946 }
947
948 void BookmarkBarView::ShowImportDialog() {
949   int64 install_time =
950       g_browser_process->local_state()->GetInt64(prefs::kInstallDate);
951   int64 time_from_install = base::Time::Now().ToTimeT() - install_time;
952   if (bookmark_bar_state_ == BookmarkBar::SHOW) {
953     UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
954                          time_from_install);
955   } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) {
956     UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
957                          time_from_install);
958   }
959
960   chrome::ShowImportDialog(browser_);
961 }
962
963 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) {
964   StopThrobbing(true);
965   const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url);
966   if (!node)
967     return;  // Generally shouldn't happen.
968   StartThrobbing(node, false);
969 }
970
971 void BookmarkBarView::OnBookmarkBubbleHidden() {
972   StopThrobbing(false);
973 }
974
975 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model,
976                                           bool ids_reassigned) {
977   // There should be no buttons. If non-zero it means Load was invoked more than
978   // once, or we didn't properly clear things. Either of which shouldn't happen.
979   DCHECK_EQ(0, GetBookmarkButtonCount());
980   const BookmarkNode* node = model_->bookmark_bar_node();
981   DCHECK(node);
982   // Create a button for each of the children on the bookmark bar.
983   for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
984     AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
985   DCHECK(model_->other_node());
986   other_bookmarked_button_->SetAccessibleName(model_->other_node()->GetTitle());
987   other_bookmarked_button_->SetText(model_->other_node()->GetTitle());
988   UpdateColors();
989   UpdateOtherBookmarksVisibility();
990   other_bookmarked_button_->SetEnabled(true);
991
992   Layout();
993   SchedulePaint();
994 }
995
996 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
997   NOTREACHED();
998   // Do minimal cleanup, presumably we'll be deleted shortly.
999   model_->RemoveObserver(this);
1000   model_ = NULL;
1001 }
1002
1003 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
1004                                         const BookmarkNode* old_parent,
1005                                         int old_index,
1006                                         const BookmarkNode* new_parent,
1007                                         int new_index) {
1008   bool was_throbbing = throbbing_view_ &&
1009       throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
1010   if (was_throbbing)
1011     throbbing_view_->StopThrobbing();
1012   BookmarkNodeRemovedImpl(model, old_parent, old_index);
1013   BookmarkNodeAddedImpl(model, new_parent, new_index);
1014   if (was_throbbing)
1015     StartThrobbing(new_parent->GetChild(new_index), false);
1016 }
1017
1018 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
1019                                         const BookmarkNode* parent,
1020                                         int index) {
1021   BookmarkNodeAddedImpl(model, parent, index);
1022 }
1023
1024 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1025                                           const BookmarkNode* parent,
1026                                           int old_index,
1027                                           const BookmarkNode* node,
1028                                           const std::set<GURL>& removed_urls) {
1029   // Close the menu if the menu is showing for the deleted node.
1030   if (bookmark_menu_ && bookmark_menu_->node() == node)
1031     bookmark_menu_->Cancel();
1032   BookmarkNodeRemovedImpl(model, parent, old_index);
1033 }
1034
1035 void BookmarkBarView::BookmarkAllNodesRemoved(
1036     BookmarkModel* model,
1037     const std::set<GURL>& removed_urls) {
1038   UpdateOtherBookmarksVisibility();
1039
1040   StopThrobbing(true);
1041
1042   // Remove the existing buttons.
1043   while (GetBookmarkButtonCount()) {
1044     delete GetBookmarkButton(0);
1045   }
1046
1047   Layout();
1048   SchedulePaint();
1049 }
1050
1051 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1052                                           const BookmarkNode* node) {
1053   BookmarkNodeChangedImpl(model, node);
1054 }
1055
1056 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1057                                                     const BookmarkNode* node) {
1058   if (node != model_->bookmark_bar_node())
1059     return;  // We only care about reordering of the bookmark bar node.
1060
1061   // Remove the existing buttons.
1062   while (GetBookmarkButtonCount()) {
1063     views::View* button = child_at(0);
1064     RemoveChildView(button);
1065     base::MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1066   }
1067
1068   // Create the new buttons.
1069   for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1070     AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1071   UpdateColors();
1072
1073   Layout();
1074   SchedulePaint();
1075 }
1076
1077 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model,
1078                                                  const BookmarkNode* node) {
1079   BookmarkNodeChangedImpl(model, node);
1080 }
1081
1082 void BookmarkBarView::WriteDragDataForView(View* sender,
1083                                            const gfx::Point& press_pt,
1084                                            ui::OSExchangeData* data) {
1085   content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1086
1087   for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1088     if (sender == GetBookmarkButton(i)) {
1089       views::TextButton* button = GetBookmarkButton(i);
1090       scoped_ptr<gfx::Canvas> canvas(
1091           views::GetCanvasForDragImage(button->GetWidget(), button->size()));
1092       button->PaintButton(canvas.get(), views::TextButton::PB_FOR_DRAG);
1093       drag_utils::SetDragImageOnDataObject(*canvas, button->size(),
1094                                            press_pt.OffsetFromOrigin(),
1095                                            data);
1096       WriteBookmarkDragData(model_->bookmark_bar_node()->GetChild(i), data);
1097       return;
1098     }
1099   }
1100   NOTREACHED();
1101 }
1102
1103 int BookmarkBarView::GetDragOperationsForView(View* sender,
1104                                               const gfx::Point& p) {
1105   if (size_animation_->is_animating() ||
1106       (size_animation_->GetCurrentValue() == 0 &&
1107        bookmark_bar_state_ != BookmarkBar::DETACHED)) {
1108     // Don't let the user drag while animating open or we're closed (and not
1109     // detached, when detached size_animation_ is always 0). This typically is
1110     // only hit if the user does something to inadvertently trigger DnD such as
1111     // pressing the mouse and hitting control-b.
1112     return ui::DragDropTypes::DRAG_NONE;
1113   }
1114
1115   for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1116     if (sender == GetBookmarkButton(i)) {
1117       return chrome::GetBookmarkDragOperation(
1118           browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
1119     }
1120   }
1121   NOTREACHED();
1122   return ui::DragDropTypes::DRAG_NONE;
1123 }
1124
1125 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1126                                           const gfx::Point& press_pt,
1127                                           const gfx::Point& p) {
1128   // Check if we have not moved enough horizontally but we have moved downward
1129   // vertically - downward drag.
1130   gfx::Vector2d move_offset = p - press_pt;
1131   gfx::Vector2d horizontal_offset(move_offset.x(), 0);
1132   if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
1133     for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1134       if (sender == GetBookmarkButton(i)) {
1135         const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1136         // If the folder button was dragged, show the menu instead.
1137         if (node && node->is_folder()) {
1138           views::MenuButton* menu_button =
1139               static_cast<views::MenuButton*>(sender);
1140           menu_button->Activate();
1141           return false;
1142         }
1143         break;
1144       }
1145     }
1146   }
1147   return true;
1148 }
1149
1150 void BookmarkBarView::OnMenuButtonClicked(views::View* view,
1151                                           const gfx::Point& point) {
1152   const BookmarkNode* node;
1153
1154   int start_index = 0;
1155   if (view == other_bookmarked_button_) {
1156     node = model_->other_node();
1157   } else if (view == overflow_button_) {
1158     node = model_->bookmark_bar_node();
1159     start_index = GetFirstHiddenNodeIndex();
1160   } else {
1161     int button_index = GetIndexOf(view);
1162     DCHECK_NE(-1, button_index);
1163     node = model_->bookmark_bar_node()->GetChild(button_index);
1164   }
1165
1166   RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1167   bookmark_menu_ = new BookmarkMenuController(
1168       browser_, page_navigator_, GetWidget(), node, start_index);
1169   bookmark_menu_->set_observer(this);
1170   bookmark_menu_->RunMenuAt(this, false);
1171 }
1172
1173 void BookmarkBarView::ButtonPressed(views::Button* sender,
1174                                     const ui::Event& event) {
1175   WindowOpenDisposition disposition_from_event_flags =
1176       ui::DispositionFromEventFlags(event.flags());
1177
1178   if (sender->tag() == kAppsShortcutButtonTag) {
1179     OpenURLParams params(GURL(chrome::kChromeUIAppsURL),
1180                          Referrer(),
1181                          disposition_from_event_flags,
1182                          content::PAGE_TRANSITION_AUTO_BOOKMARK,
1183                          false);
1184     page_navigator_->OpenURL(params);
1185     RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1186     return;
1187   }
1188
1189   const BookmarkNode* node;
1190   if (sender->tag() == kOtherFolderButtonTag) {
1191     node = model_->other_node();
1192   } else {
1193     int index = GetIndexOf(sender);
1194     DCHECK_NE(-1, index);
1195     node = model_->bookmark_bar_node()->GetChild(index);
1196   }
1197   DCHECK(page_navigator_);
1198
1199   if (node->is_url()) {
1200     RecordAppLaunch(browser_->profile(), node->url());
1201     OpenURLParams params(
1202         node->url(), Referrer(), disposition_from_event_flags,
1203         content::PAGE_TRANSITION_AUTO_BOOKMARK, false);
1204     page_navigator_->OpenURL(params);
1205   } else {
1206     chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node,
1207                     disposition_from_event_flags, browser_->profile());
1208   }
1209
1210   RecordBookmarkLaunch(node, GetBookmarkLaunchLocation());
1211 }
1212
1213 void BookmarkBarView::ShowContextMenuForView(views::View* source,
1214                                              const gfx::Point& point,
1215                                              ui::MenuSourceType source_type) {
1216   if (!model_->loaded()) {
1217     // Don't do anything if the model isn't loaded.
1218     return;
1219   }
1220
1221   const BookmarkNode* parent = NULL;
1222   std::vector<const BookmarkNode*> nodes;
1223   if (source == other_bookmarked_button_) {
1224     parent = model_->other_node();
1225     // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1226     // sure the user can't edit/delete the node in this case.
1227     nodes.push_back(parent);
1228   } else if (source != this && source != apps_page_shortcut_) {
1229     // User clicked on one of the bookmark buttons, find which one they
1230     // clicked on, except for the apps page shortcut, which must behave as if
1231     // the user clicked on the bookmark bar background.
1232     int bookmark_button_index = GetIndexOf(source);
1233     DCHECK(bookmark_button_index != -1 &&
1234            bookmark_button_index < GetBookmarkButtonCount());
1235     const BookmarkNode* node =
1236         model_->bookmark_bar_node()->GetChild(bookmark_button_index);
1237     nodes.push_back(node);
1238     parent = node->parent();
1239   } else {
1240     parent = model_->bookmark_bar_node();
1241     nodes.push_back(parent);
1242   }
1243   bool close_on_remove =
1244       (parent == model_->other_node()) && (parent->child_count() == 1);
1245
1246   context_menu_.reset(new BookmarkContextMenu(
1247       GetWidget(), browser_, browser_->profile(),
1248       browser_->tab_strip_model()->GetActiveWebContents(),
1249       parent, nodes, close_on_remove));
1250   context_menu_->RunMenuAt(point, source_type);
1251 }
1252
1253 void BookmarkBarView::Init() {
1254   // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1255   // return NULL.  When we're inserted into a hierarchy, we'll call
1256   // UpdateColors(), which will set the appropriate colors for all the objects
1257   // added in this function.
1258
1259   // Child views are traversed in the order they are added. Make sure the order
1260   // they are added matches the visual order.
1261   overflow_button_ = CreateOverflowButton();
1262   AddChildView(overflow_button_);
1263
1264   other_bookmarked_button_ = CreateOtherBookmarkedButton();
1265   // We'll re-enable when the model is loaded.
1266   other_bookmarked_button_->SetEnabled(false);
1267   AddChildView(other_bookmarked_button_);
1268
1269   apps_page_shortcut_ = CreateAppsPageShortcutButton();
1270   AddChildView(apps_page_shortcut_);
1271   profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
1272   profile_pref_registrar_.Add(
1273       prefs::kShowAppsShortcutInBookmarkBar,
1274       base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged,
1275                  base::Unretained(this)));
1276   apps_page_shortcut_->SetVisible(
1277       chrome::ShouldShowAppsShortcutInBookmarkBar(
1278           browser_->profile(), browser_->host_desktop_type()));
1279
1280   bookmarks_separator_view_ = new ButtonSeparatorView();
1281   AddChildView(bookmarks_separator_view_);
1282   UpdateBookmarksSeparatorVisibility();
1283
1284   instructions_ = new BookmarkBarInstructionsView(this);
1285   AddChildView(instructions_);
1286
1287   set_context_menu_controller(this);
1288
1289   size_animation_.reset(new gfx::SlideAnimation(this));
1290
1291   model_ = BookmarkModelFactory::GetForProfile(browser_->profile());
1292   if (model_) {
1293     model_->AddObserver(this);
1294     if (model_->loaded())
1295       BookmarkModelLoaded(model_, false);
1296     // else case: we'll receive notification back from the BookmarkModel when
1297     // done loading, then we'll populate the bar.
1298   }
1299 }
1300
1301 int BookmarkBarView::GetBookmarkButtonCount() {
1302   // We contain four non-bookmark button views: other bookmarks, bookmarks
1303   // separator, chevrons (for overflow), apps page, and the instruction label.
1304   return child_count() - 5;
1305 }
1306
1307 views::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
1308   DCHECK(index >= 0 && index < GetBookmarkButtonCount());
1309   return static_cast<views::TextButton*>(child_at(index));
1310 }
1311
1312 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
1313   return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
1314                         BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
1315 }
1316
1317 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1318   const int bb_count = GetBookmarkButtonCount();
1319   for (int i = 0; i < bb_count; ++i) {
1320     if (!GetBookmarkButton(i)->visible())
1321       return i;
1322   }
1323   return bb_count;
1324 }
1325
1326 MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() {
1327   // Title is set in Loaded.
1328   MenuButton* button =
1329       new BookmarkFolderButton(this, base::string16(), this, false);
1330   button->set_id(VIEW_ID_OTHER_BOOKMARKS);
1331   button->SetIcon(GetFolderIcon());
1332   button->set_context_menu_controller(this);
1333   button->set_tag(kOtherFolderButtonTag);
1334   return button;
1335 }
1336
1337 MenuButton* BookmarkBarView::CreateOverflowButton() {
1338   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1339   MenuButton* button = new OverFlowButton(this);
1340   button->SetIcon(*rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS));
1341
1342   // The overflow button's image contains an arrow and therefore it is a
1343   // direction sensitive image and we need to flip it if the UI layout is
1344   // right-to-left.
1345   //
1346   // By default, menu buttons are not flipped because they generally contain
1347   // text and flipping the gfx::Canvas object will break text rendering. Since
1348   // the overflow button does not contain text, we can safely flip it.
1349   button->EnableCanvasFlippingForRTLUI(true);
1350
1351   // Make visible as necessary.
1352   button->SetVisible(false);
1353   // Set accessibility name.
1354   button->SetAccessibleName(
1355       l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
1356   return button;
1357 }
1358
1359 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1360   if (node->is_url()) {
1361     BookmarkButton* button = new BookmarkButton(
1362         this, node->url(), node->GetTitle(), browser_->profile());
1363     ConfigureButton(node, button);
1364     return button;
1365   } else {
1366     views::MenuButton* button = new BookmarkFolderButton(
1367         this, node->GetTitle(), this, false);
1368     button->SetIcon(GetFolderIcon());
1369     ConfigureButton(node, button);
1370     return button;
1371   }
1372 }
1373
1374 views::TextButton* BookmarkBarView::CreateAppsPageShortcutButton() {
1375   views::TextButton* button = new ShortcutButton(
1376       this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME));
1377   button->SetTooltipText(l10n_util::GetStringUTF16(
1378       IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP));
1379   button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1380   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1381   button->SetIcon(*rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT));
1382   button->set_context_menu_controller(this);
1383   button->set_tag(kAppsShortcutButtonTag);
1384   return button;
1385 }
1386
1387 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1388                                       views::TextButton* button) {
1389   button->SetText(node->GetTitle());
1390   button->SetAccessibleName(node->GetTitle());
1391   button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1392   // We don't always have a theme provider (ui tests, for example).
1393   if (GetThemeProvider()) {
1394     button->SetEnabledColor(GetThemeProvider()->GetColor(
1395         ThemeProperties::COLOR_BOOKMARK_TEXT));
1396   }
1397
1398   button->ClearMaxTextSize();
1399   button->set_context_menu_controller(this);
1400   button->set_drag_controller(this);
1401   if (node->is_url()) {
1402     const gfx::Image& favicon = model_->GetFavicon(node);
1403     if (!favicon.IsEmpty())
1404       button->SetIcon(*favicon.ToImageSkia());
1405     else
1406       button->SetIcon(GetDefaultFavicon());
1407   }
1408   button->set_max_width(kMaxButtonWidth);
1409 }
1410
1411 void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
1412                                             const BookmarkNode* parent,
1413                                             int index) {
1414   UpdateOtherBookmarksVisibility();
1415   if (parent != model_->bookmark_bar_node()) {
1416     // We only care about nodes on the bookmark bar.
1417     return;
1418   }
1419   DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
1420   const BookmarkNode* node = parent->GetChild(index);
1421   ProfileSyncService* sync_service(ProfileSyncServiceFactory::
1422       GetInstance()->GetForProfile(browser_->profile()));
1423   if (!throbbing_view_ && sync_service && sync_service->FirstSetupInProgress())
1424     StartThrobbing(node, true);
1425   AddChildViewAt(CreateBookmarkButton(node), index);
1426   UpdateColors();
1427   Layout();
1428   SchedulePaint();
1429 }
1430
1431 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1432                                               const BookmarkNode* parent,
1433                                               int index) {
1434   UpdateOtherBookmarksVisibility();
1435
1436   StopThrobbing(true);
1437   // No need to start throbbing again as the bookmark bubble can't be up at
1438   // the same time as the user reorders.
1439
1440   if (parent != model_->bookmark_bar_node()) {
1441     // We only care about nodes on the bookmark bar.
1442     return;
1443   }
1444   DCHECK(index >= 0 && index < GetBookmarkButtonCount());
1445   views::View* button = child_at(index);
1446   RemoveChildView(button);
1447   base::MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1448   Layout();
1449   SchedulePaint();
1450 }
1451
1452 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1453                                               const BookmarkNode* node) {
1454   if (node->parent() != model_->bookmark_bar_node()) {
1455     // We only care about nodes on the bookmark bar.
1456     return;
1457   }
1458   int index = model_->bookmark_bar_node()->GetIndexOf(node);
1459   DCHECK_NE(-1, index);
1460   views::TextButton* button = GetBookmarkButton(index);
1461   gfx::Size old_pref = button->GetPreferredSize();
1462   ConfigureButton(node, button);
1463   gfx::Size new_pref = button->GetPreferredSize();
1464   if (old_pref.width() != new_pref.width()) {
1465     Layout();
1466     SchedulePaint();
1467   } else if (button->visible()) {
1468     button->SchedulePaint();
1469   }
1470 }
1471
1472 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1473   if (bookmark_drop_menu_) {
1474     if (bookmark_drop_menu_->node() == node) {
1475       // Already showing for the specified node.
1476       return;
1477     }
1478     bookmark_drop_menu_->Cancel();
1479   }
1480
1481   views::MenuButton* menu_button = GetMenuButtonForNode(node);
1482   if (!menu_button)
1483     return;
1484
1485   int start_index = 0;
1486   if (node == model_->bookmark_bar_node())
1487     start_index = GetFirstHiddenNodeIndex();
1488
1489   drop_info_->is_menu_showing = true;
1490   bookmark_drop_menu_ = new BookmarkMenuController(browser_,
1491       page_navigator_, GetWidget(), node, start_index);
1492   bookmark_drop_menu_->set_observer(this);
1493   bookmark_drop_menu_->RunMenuAt(this, true);
1494 }
1495
1496 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1497   show_folder_method_factory_.InvalidateWeakPtrs();
1498 }
1499
1500 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1501   if (!animations_enabled) {
1502     // So that tests can run as fast as possible disable the delay during
1503     // testing.
1504     ShowDropFolderForNode(node);
1505     return;
1506   }
1507   show_folder_method_factory_.InvalidateWeakPtrs();
1508   base::MessageLoop::current()->PostDelayedTask(
1509       FROM_HERE,
1510       base::Bind(&BookmarkBarView::ShowDropFolderForNode,
1511                  show_folder_method_factory_.GetWeakPtr(),
1512                  node),
1513       base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1514 }
1515
1516 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event,
1517                                             const BookmarkNodeData& data,
1518                                             DropLocation* location) {
1519   DCHECK(model_);
1520   DCHECK(model_->loaded());
1521   DCHECK(data.is_valid());
1522
1523   *location = DropLocation();
1524
1525   // The drop event uses the screen coordinates while the child Views are
1526   // always laid out from left to right (even though they are rendered from
1527   // right-to-left on RTL locales). Thus, in order to make sure the drop
1528   // coordinates calculation works, we mirror the event's X coordinate if the
1529   // locale is RTL.
1530   int mirrored_x = GetMirroredXInView(event.x());
1531
1532   bool found = false;
1533   const int other_delta_x = mirrored_x - other_bookmarked_button_->x();
1534   Profile* profile = browser_->profile();
1535   if (other_bookmarked_button_->visible() && other_delta_x >= 0 &&
1536       other_delta_x < other_bookmarked_button_->width()) {
1537     // Mouse is over 'other' folder.
1538     location->button_type = DROP_OTHER_FOLDER;
1539     location->on = true;
1540     found = true;
1541   } else if (!GetBookmarkButtonCount()) {
1542     // No bookmarks, accept the drop.
1543     location->index = 0;
1544     int ops = data.GetFirstNode(model_, profile->GetPath()) ?
1545         ui::DragDropTypes::DRAG_MOVE :
1546         ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
1547     location->operation = chrome::GetPreferredBookmarkDropOperation(
1548         event.source_operations(), ops);
1549     return;
1550   }
1551
1552   for (int i = 0; i < GetBookmarkButtonCount() &&
1553        GetBookmarkButton(i)->visible() && !found; i++) {
1554     views::TextButton* button = GetBookmarkButton(i);
1555     int button_x = mirrored_x - button->x();
1556     int button_w = button->width();
1557     if (button_x < button_w) {
1558       found = true;
1559       const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1560       if (node->is_folder()) {
1561         if (button_x <= views::kDropBetweenPixels) {
1562           location->index = i;
1563         } else if (button_x < button_w - views::kDropBetweenPixels) {
1564           location->index = i;
1565           location->on = true;
1566         } else {
1567           location->index = i + 1;
1568         }
1569       } else if (button_x < button_w / 2) {
1570         location->index = i;
1571       } else {
1572         location->index = i + 1;
1573       }
1574       break;
1575     }
1576   }
1577
1578   if (!found) {
1579     if (overflow_button_->visible()) {
1580       // Are we over the overflow button?
1581       int overflow_delta_x = mirrored_x - overflow_button_->x();
1582       if (overflow_delta_x >= 0 &&
1583           overflow_delta_x < overflow_button_->width()) {
1584         // Mouse is over overflow button.
1585         location->index = GetFirstHiddenNodeIndex();
1586         location->button_type = DROP_OVERFLOW;
1587       } else if (overflow_delta_x < 0) {
1588         // Mouse is after the last visible button but before overflow button;
1589         // use the last visible index.
1590         location->index = GetFirstHiddenNodeIndex();
1591       } else {
1592         return;
1593       }
1594     } else if (!other_bookmarked_button_->visible() ||
1595                mirrored_x < other_bookmarked_button_->x()) {
1596       // Mouse is after the last visible button but before more recently
1597       // bookmarked; use the last visible index.
1598       location->index = GetFirstHiddenNodeIndex();
1599     } else {
1600       return;
1601     }
1602   }
1603
1604   if (location->on) {
1605     const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ?
1606         model_->other_node() :
1607         model_->bookmark_bar_node()->GetChild(location->index);
1608     location->operation = chrome::GetBookmarkDropOperation(
1609         profile, event, data, parent, parent->child_count());
1610     if (!location->operation && !data.has_single_url() &&
1611         data.GetFirstNode(model_, profile->GetPath()) == parent) {
1612       // Don't open a menu if the node being dragged is the menu to open.
1613       location->on = false;
1614     }
1615   } else {
1616     location->operation = chrome::GetBookmarkDropOperation(
1617         profile, event, data, model_->bookmark_bar_node(), location->index);
1618   }
1619 }
1620
1621 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
1622                                             ui::OSExchangeData* data) {
1623   DCHECK(node && data);
1624   BookmarkNodeData drag_data(node);
1625   drag_data.Write(browser_->profile()->GetPath(), data);
1626 }
1627
1628 void BookmarkBarView::StartThrobbing(const BookmarkNode* node,
1629                                      bool overflow_only) {
1630   DCHECK(!throbbing_view_);
1631
1632   // Determine which visible button is showing the bookmark (or is an ancestor
1633   // of the bookmark).
1634   const BookmarkNode* bbn = model_->bookmark_bar_node();
1635   const BookmarkNode* parent_on_bb = node;
1636   while (parent_on_bb) {
1637     const BookmarkNode* parent = parent_on_bb->parent();
1638     if (parent == bbn)
1639       break;
1640     parent_on_bb = parent;
1641   }
1642   if (parent_on_bb) {
1643     int index = bbn->GetIndexOf(parent_on_bb);
1644     if (index >= GetFirstHiddenNodeIndex()) {
1645       // Node is hidden, animate the overflow button.
1646       throbbing_view_ = overflow_button_;
1647     } else if (!overflow_only) {
1648       throbbing_view_ = static_cast<CustomButton*>(child_at(index));
1649     }
1650   } else if (!overflow_only) {
1651     throbbing_view_ = other_bookmarked_button_;
1652   }
1653
1654   // Use a large number so that the button continues to throb.
1655   if (throbbing_view_)
1656     throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
1657 }
1658
1659 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove(
1660     const BookmarkNode* parent,
1661     int old_index) {
1662   const BookmarkNode* bbn = model_->bookmark_bar_node();
1663   const BookmarkNode* old_node = parent;
1664   int old_index_on_bb = old_index;
1665   while (old_node && old_node != bbn) {
1666     const BookmarkNode* parent = old_node->parent();
1667     if (parent == bbn) {
1668       old_index_on_bb = bbn->GetIndexOf(old_node);
1669       break;
1670     }
1671     old_node = parent;
1672   }
1673   if (old_node) {
1674     if (old_index_on_bb >= GetFirstHiddenNodeIndex()) {
1675       // Node is hidden, animate the overflow button.
1676       return overflow_button_;
1677     }
1678     return static_cast<CustomButton*>(child_at(old_index_on_bb));
1679   }
1680   // Node wasn't on the bookmark bar, use the other bookmark button.
1681   return other_bookmarked_button_;
1682 }
1683
1684 void BookmarkBarView::UpdateColors() {
1685   // We don't always have a theme provider (ui tests, for example).
1686   const ui::ThemeProvider* theme_provider = GetThemeProvider();
1687   if (!theme_provider)
1688     return;
1689   SkColor text_color =
1690       theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
1691   for (int i = 0; i < GetBookmarkButtonCount(); ++i)
1692     GetBookmarkButton(i)->SetEnabledColor(text_color);
1693   other_bookmarked_button()->SetEnabledColor(text_color);
1694   if (apps_page_shortcut_->visible())
1695     apps_page_shortcut_->SetEnabledColor(text_color);
1696 }
1697
1698 void BookmarkBarView::UpdateOtherBookmarksVisibility() {
1699   bool has_other_children = !model_->other_node()->empty();
1700   if (has_other_children == other_bookmarked_button_->visible())
1701     return;
1702   other_bookmarked_button_->SetVisible(has_other_children);
1703   UpdateBookmarksSeparatorVisibility();
1704   Layout();
1705   SchedulePaint();
1706 }
1707
1708 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() {
1709   // Ash does not paint the bookmarks separator line because it looks odd on
1710   // the flat background.  We keep it present for layout, but don't draw it.
1711   bookmarks_separator_view_->SetVisible(
1712       browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH &&
1713       other_bookmarked_button_->visible());
1714 }
1715
1716 gfx::Size BookmarkBarView::LayoutItems(bool compute_bounds_only) {
1717   gfx::Size prefsize;
1718   if (!parent() && !compute_bounds_only)
1719     return prefsize;
1720
1721   int x = kLeftMargin;
1722   int top_margin = IsDetached() ? kDetachedTopMargin : 0;
1723   int y = top_margin;
1724   int width = View::width() - kRightMargin - kLeftMargin;
1725   int height = chrome::kBookmarkBarHeight - kBottomMargin;
1726   int separator_margin = kSeparatorMargin;
1727
1728   if (IsDetached()) {
1729     double current_state = 1 - size_animation_->GetCurrentValue();
1730     x += static_cast<int>(kNewtabHorizontalPadding * current_state);
1731     y += (View::height() - chrome::kBookmarkBarHeight) / 2;
1732     width -= static_cast<int>(kNewtabHorizontalPadding * current_state);
1733     separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
1734   } else {
1735     // For the attached appearance, pin the content to the bottom of the bar
1736     // when animating in/out, as shrinking its height instead looks weird.  This
1737     // also matches how we layout infobars.
1738     y += View::height() - chrome::kBookmarkBarHeight;
1739   }
1740
1741   gfx::Size other_bookmarked_pref = other_bookmarked_button_->visible() ?
1742       other_bookmarked_button_->GetPreferredSize() : gfx::Size();
1743   gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
1744   gfx::Size bookmarks_separator_pref =
1745       bookmarks_separator_view_->GetPreferredSize();
1746   gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ?
1747       apps_page_shortcut_->GetPreferredSize() : gfx::Size();
1748
1749   int max_x = width - overflow_pref.width() - kButtonPadding -
1750       bookmarks_separator_pref.width();
1751   if (other_bookmarked_button_->visible())
1752     max_x -= other_bookmarked_pref.width() + kButtonPadding;
1753
1754   // Next, layout out the buttons. Any buttons that are placed beyond the
1755   // visible region and made invisible.
1756
1757   // Start with the apps page shortcut button.
1758   if (apps_page_shortcut_->visible()) {
1759     if (!compute_bounds_only) {
1760       apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(),
1761                                      height);
1762     }
1763     x += apps_page_shortcut_pref.width() + kButtonPadding;
1764   }
1765
1766   // Then go through the bookmark buttons.
1767   if (GetBookmarkButtonCount() == 0 && model_ && model_->loaded()) {
1768     gfx::Size pref = instructions_->GetPreferredSize();
1769     if (!compute_bounds_only) {
1770       instructions_->SetBounds(
1771           x + kInstructionsPadding, y,
1772           std::min(static_cast<int>(pref.width()),
1773           max_x - x),
1774           height);
1775       instructions_->SetVisible(true);
1776     }
1777   } else {
1778     if (!compute_bounds_only)
1779       instructions_->SetVisible(false);
1780
1781     for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1782       views::View* child = child_at(i);
1783       gfx::Size pref = child->GetPreferredSize();
1784       int next_x = x + pref.width() + kButtonPadding;
1785       if (!compute_bounds_only) {
1786         child->SetVisible(next_x < max_x);
1787         child->SetBounds(x, y, pref.width(), height);
1788       }
1789       x = next_x;
1790     }
1791   }
1792
1793   // Layout the right side of the bar.
1794   const bool all_visible = (GetBookmarkButtonCount() == 0 ||
1795                             child_at(GetBookmarkButtonCount() - 1)->visible());
1796
1797   // Layout the right side buttons.
1798   if (!compute_bounds_only)
1799     x = max_x + kButtonPadding;
1800   else
1801     x += kButtonPadding;
1802
1803   // The overflow button.
1804   if (!compute_bounds_only) {
1805     overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
1806     overflow_button_->SetVisible(!all_visible);
1807   }
1808   x += overflow_pref.width();
1809
1810   // Separator.
1811   if (bookmarks_separator_view_->visible()) {
1812     if (!compute_bounds_only) {
1813       bookmarks_separator_view_->SetBounds(x,
1814                                            y - top_margin,
1815                                            bookmarks_separator_pref.width(),
1816                                            height + top_margin + kBottomMargin -
1817                                            separator_margin);
1818     }
1819
1820     x += bookmarks_separator_pref.width();
1821   }
1822
1823   // The other bookmarks button.
1824   if (other_bookmarked_button_->visible()) {
1825     if (!compute_bounds_only) {
1826       other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.width(),
1827                                           height);
1828     }
1829     x += other_bookmarked_pref.width() + kButtonPadding;
1830   }
1831
1832   // Set the preferred size computed so far.
1833   if (compute_bounds_only) {
1834     x += kRightMargin;
1835     prefsize.set_width(x);
1836     if (IsDetached()) {
1837       x += static_cast<int>(kNewtabHorizontalPadding *
1838           (1 - size_animation_->GetCurrentValue()));
1839       prefsize.set_height(
1840           chrome::kBookmarkBarHeight +
1841           static_cast<int>(
1842               (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
1843               (1 - size_animation_->GetCurrentValue())));
1844     } else {
1845       prefsize.set_height(static_cast<int>(chrome::kBookmarkBarHeight *
1846                                            size_animation_->GetCurrentValue()));
1847     }
1848   }
1849   return prefsize;
1850 }
1851
1852 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() {
1853   DCHECK(apps_page_shortcut_);
1854   // Only perform layout if required.
1855   bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar(
1856       browser_->profile(), browser_->host_desktop_type());
1857   if (apps_page_shortcut_->visible() == visible)
1858     return;
1859   apps_page_shortcut_->SetVisible(visible);
1860   UpdateBookmarksSeparatorVisibility();
1861   Layout();
1862 }