1 // Copyright 2013 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.
5 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
9 #include "chrome/browser/bookmarks/bookmark_stats.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/test/base/browser_with_test_window_test.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "components/bookmarks/browser/bookmark_model.h"
14 #include "components/bookmarks/test/bookmark_test_helpers.h"
15 #include "ui/views/controls/menu/menu_item_view.h"
16 #include "ui/views/controls/menu/menu_runner.h"
17 #include "ui/views/controls/menu/submenu_view.h"
19 using base::ASCIIToUTF16
;
20 using bookmarks::BookmarkModel
;
21 using bookmarks::BookmarkNode
;
23 class BookmarkMenuDelegateTest
: public BrowserWithTestWindowTest
{
25 BookmarkMenuDelegateTest() : model_(NULL
) {}
27 void SetUp() override
{
28 BrowserWithTestWindowTest::SetUp();
30 profile()->CreateBookmarkModel(true);
32 model_
= BookmarkModelFactory::GetForProfile(profile());
33 bookmarks::test::WaitForBookmarkModelToLoad(model_
);
38 void TearDown() override
{
39 if (bookmark_menu_delegate_
.get()) {
40 // Since we never show the menu we need to pass the MenuItemView to
41 // MenuRunner so that the MenuItemView is destroyed.
42 views::MenuRunner
menu_runner(bookmark_menu_delegate_
->menu(), 0);
43 bookmark_menu_delegate_
.reset();
45 BrowserWithTestWindowTest::TearDown();
50 // Destroy current menu if available, see comments in TearDown().
51 if (bookmark_menu_delegate_
.get())
52 views::MenuRunner
menu_runner(bookmark_menu_delegate_
->menu(), 0);
54 bookmark_menu_delegate_
.reset(
55 new BookmarkMenuDelegate(browser(), nullptr, nullptr));
58 void NewAndInitDelegateForPermanent() {
59 const BookmarkNode
* node
= model_
->bookmark_bar_node();
61 bookmark_menu_delegate_
->Init(&test_delegate_
, NULL
, node
, 0,
62 BookmarkMenuDelegate::SHOW_PERMANENT_FOLDERS
,
63 BOOKMARK_LAUNCH_LOCATION_NONE
);
66 const BookmarkNode
* GetNodeForMenuItem(views::MenuItemView
* menu
) {
67 const auto& node_map
= bookmark_menu_delegate_
->menu_id_to_node_map_
;
68 auto iter
= node_map
.find(menu
->GetCommand());
69 return (iter
== node_map
.end()) ? nullptr : iter
->second
;
72 int next_menu_id() { return bookmark_menu_delegate_
->next_menu_id_
; }
74 // Forces all the menus to load by way of invoking WillShowMenu() on all menu
75 // items of tyep SUBMENU.
76 void LoadAllMenus() { LoadAllMenus(bookmark_menu_delegate_
->menu()); }
78 BookmarkModel
* model_
;
80 scoped_ptr
<BookmarkMenuDelegate
> bookmark_menu_delegate_
;
83 void LoadAllMenus(views::MenuItemView
* menu
) {
84 EXPECT_EQ(views::MenuItemView::SUBMENU
, menu
->GetType());
86 for (int i
= 0; i
< menu
->GetSubmenu()->GetMenuItemCount(); ++i
) {
87 views::MenuItemView
* child
= menu
->GetSubmenu()->GetMenuItemAt(i
);
88 if (child
->GetType() == views::MenuItemView::SUBMENU
) {
89 bookmark_menu_delegate_
->WillShowMenu(child
);
95 std::string
base_path() const { return "file:///c:/tmp/"; }
97 // Creates the following structure:
110 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
111 std::string test_base
= base_path();
112 model_
->AddURL(bb_node
, 0, ASCIIToUTF16("a"), GURL(test_base
+ "a"));
113 const BookmarkNode
* f1
= model_
->AddFolder(bb_node
, 1, ASCIIToUTF16("F1"));
114 model_
->AddURL(f1
, 0, ASCIIToUTF16("f1a"), GURL(test_base
+ "f1a"));
115 const BookmarkNode
* f11
= model_
->AddFolder(f1
, 1, ASCIIToUTF16("F11"));
116 model_
->AddURL(f11
, 0, ASCIIToUTF16("f11a"), GURL(test_base
+ "f11a"));
117 model_
->AddFolder(bb_node
, 2, ASCIIToUTF16("F2"));
119 // Children of the other node.
120 model_
->AddURL(model_
->other_node(), 0, ASCIIToUTF16("oa"),
121 GURL(test_base
+ "oa"));
122 const BookmarkNode
* of1
=
123 model_
->AddFolder(model_
->other_node(), 1, ASCIIToUTF16("OF1"));
124 model_
->AddURL(of1
, 0, ASCIIToUTF16("of1a"), GURL(test_base
+ "of1a"));
127 views::MenuDelegate test_delegate_
;
129 DISALLOW_COPY_AND_ASSIGN(BookmarkMenuDelegateTest
);
132 TEST_F(BookmarkMenuDelegateTest
, VerifyLazyLoad
) {
133 NewAndInitDelegateForPermanent();
134 views::MenuItemView
* root_item
= bookmark_menu_delegate_
->menu();
135 ASSERT_TRUE(root_item
->HasSubmenu());
136 EXPECT_EQ(4, root_item
->GetSubmenu()->GetMenuItemCount());
137 EXPECT_EQ(5, root_item
->GetSubmenu()->child_count()); // Includes separator.
138 views::MenuItemView
* f1_item
= root_item
->GetSubmenu()->GetMenuItemAt(1);
139 ASSERT_TRUE(f1_item
->HasSubmenu());
140 // f1 hasn't been loaded yet.
141 EXPECT_EQ(0, f1_item
->GetSubmenu()->GetMenuItemCount());
142 // Will show triggers a load.
143 int next_id_before_load
= next_menu_id();
144 bookmark_menu_delegate_
->WillShowMenu(f1_item
);
145 // f1 should have loaded its children.
146 EXPECT_EQ(next_id_before_load
+ 2, next_menu_id());
147 ASSERT_EQ(2, f1_item
->GetSubmenu()->GetMenuItemCount());
148 const BookmarkNode
* f1_node
= model_
->bookmark_bar_node()->GetChild(1);
149 EXPECT_EQ(f1_node
->GetChild(0),
150 GetNodeForMenuItem(f1_item
->GetSubmenu()->GetMenuItemAt(0)));
151 EXPECT_EQ(f1_node
->GetChild(1),
152 GetNodeForMenuItem(f1_item
->GetSubmenu()->GetMenuItemAt(1)));
154 // F11 shouldn't have loaded yet.
155 views::MenuItemView
* f11_item
= f1_item
->GetSubmenu()->GetMenuItemAt(1);
156 ASSERT_TRUE(f11_item
->HasSubmenu());
157 EXPECT_EQ(0, f11_item
->GetSubmenu()->GetMenuItemCount());
159 next_id_before_load
= next_menu_id();
160 bookmark_menu_delegate_
->WillShowMenu(f11_item
);
161 // Invoke WillShowMenu() twice to make sure the second call doesn't cause
163 bookmark_menu_delegate_
->WillShowMenu(f11_item
);
164 // F11 should have loaded its single child (f11a).
165 EXPECT_EQ(next_id_before_load
+ 1, next_menu_id());
167 ASSERT_EQ(1, f11_item
->GetSubmenu()->GetMenuItemCount());
168 const BookmarkNode
* f11_node
= f1_node
->GetChild(1);
169 EXPECT_EQ(f11_node
->GetChild(0),
170 GetNodeForMenuItem(f11_item
->GetSubmenu()->GetMenuItemAt(0)));
173 // Verifies WillRemoveBookmarks() doesn't attempt to access MenuItemViews that
174 // have since been deleted.
175 TEST_F(BookmarkMenuDelegateTest
, RemoveBookmarks
) {
176 views::MenuDelegate test_delegate
;
177 const BookmarkNode
* node
= model_
->bookmark_bar_node()->GetChild(1);
179 bookmark_menu_delegate_
->Init(&test_delegate
, NULL
, node
, 0,
180 BookmarkMenuDelegate::HIDE_PERMANENT_FOLDERS
,
181 BOOKMARK_LAUNCH_LOCATION_NONE
);
183 std::vector
<const BookmarkNode
*> nodes_to_remove
;
184 nodes_to_remove
.push_back(node
->GetChild(1));
185 bookmark_menu_delegate_
->WillRemoveBookmarks(nodes_to_remove
);
186 nodes_to_remove
.clear();
187 bookmark_menu_delegate_
->DidRemoveBookmarks();