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