Home | History | Annotate | Download | only in bookmarks
      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 }
   1984