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