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