• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
BookmarkButton(views::ButtonListener * listener,const GURL & url,const std::wstring & title,Profile * profile)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 
GetTooltipText(const gfx::Point & p,std::wstring * tooltip)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 
IsTriggerableEvent(const views::MouseEvent & e)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:
BookmarkFolderButton(views::ButtonListener * listener,const std::wstring & title,views::ViewMenuDelegate * menu_delegate,bool show_menu_marker)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 
IsTriggerableEvent(const views::MouseEvent & e)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 
OnPaint(gfx::Canvas * canvas)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:
OverFlowButton(BookmarkBarView * owner)216   explicit OverFlowButton(BookmarkBarView* owner)
217       : MenuButton(NULL, std::wstring(), owner, false),
218         owner_(owner) {}
219 
OnMousePressed(const views::MouseEvent & e)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 
RecordAppLaunch(Profile * profile,GURL url)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 {
DropInfoBookmarkBarView::DropInfo248   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:
ButtonSeparatorView()294   ButtonSeparatorView() {}
~ButtonSeparatorView()295   virtual ~ButtonSeparatorView() {}
296 
OnPaint(gfx::Canvas * canvas)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 
GetPreferredSize()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 
GetAccessibleState(ui::AccessibleViewState * state)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.
GetFolderIcon()331 static const SkBitmap& GetFolderIcon() {
332   if (!kFolderIcon) {
333     kFolderIcon = ResourceBundle::GetSharedInstance().
334         GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER);
335   }
336   return *kFolderIcon;
337 }
338 
BookmarkBarView(Profile * profile,Browser * browser)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 
~BookmarkBarView()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 
SetProfile(Profile * profile)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 
SetPageNavigator(PageNavigator * navigator)426 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
427   page_navigator_ = navigator;
428 }
429 
GetPreferredSize()430 gfx::Size BookmarkBarView::GetPreferredSize() {
431   return LayoutItems(true);
432 }
433 
GetMinimumSize()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 
Layout()463 void BookmarkBarView::Layout() {
464   LayoutItems(false);
465 }
466 
ViewHierarchyChanged(bool is_add,View * parent,View * child)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 
PaintChildren(gfx::Canvas * canvas)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 
GetDropFormats(int * formats,std::set<ui::OSExchangeData::CustomFormat> * custom_formats)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 
AreDropTypesRequired()536 bool BookmarkBarView::AreDropTypesRequired() {
537   return true;
538 }
539 
CanDrop(const ui::OSExchangeData & data)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 
OnDragEntered(const DropTargetEvent & event)553 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
554 }
555 
OnDragUpdated(const DropTargetEvent & event)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 
OnDragExited()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 
OnPerformDrop(const DropTargetEvent & event)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 
ShowContextMenu(const gfx::Point & p,bool is_mouse_gesture)671 void BookmarkBarView::ShowContextMenu(const gfx::Point& p,
672                                       bool is_mouse_gesture) {
673   ShowContextMenuForView(this, p, is_mouse_gesture);
674 }
675 
GetAccessibleState(ui::AccessibleViewState * state)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 
OnStateChanged()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 
OnFullscreenToggled(bool fullscreen)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 
IsDetached() const702 bool BookmarkBarView::IsDetached() const {
703   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4))
704     return false;
705 
706   return OnNewTabPage() && (size_animation_->GetCurrentValue() != 1);
707 }
708 
IsOnTop() const709 bool BookmarkBarView::IsOnTop() const {
710   return true;
711 }
712 
GetAnimationValue() const713 double BookmarkBarView::GetAnimationValue() const {
714   return size_animation_->GetCurrentValue();
715 }
716 
GetToolbarOverlap() const717 int BookmarkBarView::GetToolbarOverlap() const {
718   return GetToolbarOverlap(false);
719 }
720 
IsAlwaysShown() const721 bool BookmarkBarView::IsAlwaysShown() const {
722   return (profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) &&
723           profile_->GetPrefs()->GetBoolean(prefs::kEnableBookmarkBar));
724 }
725 
OnNewTabPage() const726 bool BookmarkBarView::OnNewTabPage() const {
727   return (browser_ && browser_->GetSelectedTabContents() &&
728           browser_->GetSelectedTabContents()->ShouldShowBookmarkBar());
729 }
730 
GetToolbarOverlap(bool return_max) const731 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 
is_animating()745 bool BookmarkBarView::is_animating() {
746   return size_animation_->is_animating();
747 }
748 
AnimationProgressed(const ui::Animation * animation)749 void BookmarkBarView::AnimationProgressed(const ui::Animation* animation) {
750   if (browser_)
751     browser_->BookmarkBarSizeChanged(true);
752 }
753 
AnimationEnded(const ui::Animation * animation)754 void BookmarkBarView::AnimationEnded(const ui::Animation* animation) {
755   if (browser_)
756     browser_->BookmarkBarSizeChanged(false);
757 
758   SchedulePaint();
759 }
760 
BookmarkMenuDeleted(BookmarkMenuController * controller)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 
GetBookmarkButton(int index)768 views::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
769   DCHECK(index >= 0 && index < GetBookmarkButtonCount());
770   return static_cast<views::TextButton*>(GetChildViewAt(index));
771 }
772 
GetMenu()773 views::MenuItemView* BookmarkBarView::GetMenu() {
774   return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
775 }
776 
GetContextMenu()777 views::MenuItemView* BookmarkBarView::GetContextMenu() {
778   return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
779 }
780 
GetDropMenu()781 views::MenuItemView* BookmarkBarView::GetDropMenu() {
782   return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
783 }
784 
GetNodeForButtonAt(const gfx::Point & loc,int * start_index)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 
GetMenuButtonForNode(const BookmarkNode * node)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 
GetAnchorPositionAndStartIndexForButton(views::MenuButton * button,MenuItemView::AnchorPosition * anchor,int * start_index)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 
ShowImportDialog()854 void BookmarkBarView::ShowImportDialog() {
855   browser_->OpenImportSettingsDialog();
856 }
857 
Init()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 
CreateOtherBookmarkedButton()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 
CreateOverflowButton()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 
Loaded(BookmarkModel * model)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 
BookmarkModelBeingDeleted(BookmarkModel * model)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 
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)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 
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)974 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
975                                         const BookmarkNode* parent,
976                                         int index) {
977   BookmarkNodeAddedImpl(model, parent, index);
978 }
979 
BookmarkNodeAddedImpl(BookmarkModel * model,const BookmarkNode * parent,int index)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 
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int old_index,const BookmarkNode * node)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 
BookmarkNodeRemovedImpl(BookmarkModel * model,const BookmarkNode * parent,int index)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 
BookmarkNodeChanged(BookmarkModel * model,const BookmarkNode * node)1029 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1030                                           const BookmarkNode* node) {
1031   NotifyModelChanged();
1032   BookmarkNodeChangedImpl(model, node);
1033 }
1034 
BookmarkNodeChangedImpl(BookmarkModel * model,const BookmarkNode * node)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 
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)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 
BookmarkNodeFaviconLoaded(BookmarkModel * model,const BookmarkNode * node)1077 void BookmarkBarView::BookmarkNodeFaviconLoaded(BookmarkModel* model,
1078                                                 const BookmarkNode* node) {
1079   BookmarkNodeChangedImpl(model, node);
1080 }
1081 
WriteDragDataForView(View * sender,const gfx::Point & press_pt,ui::OSExchangeData * data)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 
GetDragOperationsForView(View * sender,const gfx::Point & p)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 
CanStartDragForView(views::View * sender,const gfx::Point & press_pt,const gfx::Point & p)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 
WriteBookmarkDragData(const BookmarkNode * node,ui::OSExchangeData * data)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 
RunMenu(views::View * view,const gfx::Point & pt)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 
ButtonPressed(views::Button * sender,const views::Event & event)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 
ShowContextMenuForView(View * source,const gfx::Point & p,bool is_mouse_gesture)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 
CreateBookmarkButton(const BookmarkNode * node)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 
ConfigureButton(const BookmarkNode * node,views::TextButton * button)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 
IsItemChecked(int id) const1285 bool BookmarkBarView::IsItemChecked(int id) const {
1286   DCHECK(id == kAlwaysShowCommandID);
1287   return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
1288 }
1289 
ExecuteCommand(int id)1290 void BookmarkBarView::ExecuteCommand(int id) {
1291   bookmark_utils::ToggleWhenVisible(profile_);
1292 }
1293 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
OnThemeChanged()1326 void BookmarkBarView::OnThemeChanged() {
1327   UpdateColors();
1328 }
1329 
NotifyModelChanged()1330 void BookmarkBarView::NotifyModelChanged() {
1331   if (model_changed_listener_)
1332     model_changed_listener_->ModelChanged();
1333 }
1334 
ShowDropFolderForNode(const BookmarkNode * node)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 
StopShowFolderDropMenuTimer()1359 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1360   if (show_folder_drop_menu_task_)
1361     show_folder_drop_menu_task_->Cancel();
1362 }
1363 
StartShowFolderDropMenuTimer(const BookmarkNode * node)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 
CalculateDropOperation(const DropTargetEvent & event,const BookmarkNodeData & data,int * index,bool * drop_on,bool * is_over_overflow,bool * is_over_other)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 
GetFirstHiddenNodeIndex()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 
StartThrobbing(const BookmarkNode * node,bool overflow_only)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 
DetermineViewToThrobFromRemove(const BookmarkNode * parent,int old_index)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 
GetBookmarkButtonCount()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 
StopThrobbing(bool immediate)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
CreateToolTipForURLAndTitle(const gfx::Point & screen_loc,const GURL & url,const std::wstring & title,Profile * profile)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 
UpdateColors()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 
UpdateOtherBookmarksVisibility()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 
LayoutItems(bool compute_bounds_only)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 
CreateSyncErrorButton()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