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