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