1 // Copyright 2014 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_context_menu.h"
10 #include "base/compiler_specific.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "base/values.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
18 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
19 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "components/bookmarks/browser/bookmark_model.h"
25 #include "components/bookmarks/test/bookmark_test_helpers.h"
26 #include "content/public/browser/page_navigator.h"
27 #include "content/public/test/test_browser_thread.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/base/clipboard/clipboard.h"
30 #include "ui/events/platform/platform_event_source.h"
31 #include "ui/views/controls/menu/menu_item_view.h"
34 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
37 using base::ASCIIToUTF16
;
38 using bookmarks::BookmarkModel
;
39 using bookmarks::BookmarkNode
;
40 using content::BrowserThread
;
41 using content::OpenURLParams
;
42 using content::PageNavigator
;
43 using content::WebContents
;
47 // PageNavigator implementation that records the URL.
48 class TestingPageNavigator
: public PageNavigator
{
50 WebContents
* OpenURL(const OpenURLParams
& params
) override
{
51 urls_
.push_back(params
.url
);
55 std::vector
<GURL
> urls_
;
60 class BookmarkContextMenuTest
: public testing::Test
{
62 BookmarkContextMenuTest()
63 : ui_thread_(BrowserThread::UI
, &message_loop_
),
64 file_thread_(BrowserThread::FILE, &message_loop_
),
68 void SetUp() override
{
69 event_source_
= ui::PlatformEventSource::CreateDefault();
70 profile_
.reset(new TestingProfile());
71 profile_
->CreateBookmarkModel(true);
73 model_
= BookmarkModelFactory::GetForProfile(profile_
.get());
74 bookmarks::test::WaitForBookmarkModelToLoad(model_
);
79 void TearDown() override
{
80 ui::Clipboard::DestroyClipboardForCurrentThread();
82 BrowserThread::GetBlockingPool()->FlushForTesting();
83 // Flush the message loop to make application verifiers happy.
84 message_loop_
.RunUntilIdle();
85 event_source_
.reset();
89 base::MessageLoopForUI message_loop_
;
90 content::TestBrowserThread ui_thread_
;
91 content::TestBrowserThread file_thread_
;
92 scoped_ptr
<ui::PlatformEventSource
> event_source_
;
93 scoped_ptr
<TestingProfile
> profile_
;
94 BookmarkModel
* model_
;
95 TestingPageNavigator navigator_
;
98 // Creates the following structure:
102 // -f1b as "chrome://settings"
110 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
111 std::string test_base
= "file:///c:/tmp/";
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 model_
->AddURL(f1
, 1, ASCIIToUTF16("f1b"), GURL("chrome://settings"));
116 const BookmarkNode
* f11
= model_
->AddFolder(f1
, 2, ASCIIToUTF16("F11"));
117 model_
->AddURL(f11
, 0, ASCIIToUTF16("f11a"), GURL(test_base
+ "f11a"));
118 model_
->AddFolder(bb_node
, 2, ASCIIToUTF16("F2"));
119 model_
->AddFolder(bb_node
, 3, ASCIIToUTF16("F3"));
120 const BookmarkNode
* f4
= model_
->AddFolder(bb_node
, 4, ASCIIToUTF16("F4"));
121 model_
->AddURL(f4
, 0, ASCIIToUTF16("f4a"), GURL(test_base
+ "f4a"));
125 // Tests Deleting from the menu.
126 TEST_F(BookmarkContextMenuTest
, DeleteURL
) {
127 std::vector
<const BookmarkNode
*> nodes
;
128 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(0));
129 BookmarkContextMenu
controller(
130 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
131 GURL url
= model_
->bookmark_bar_node()->GetChild(0)->url();
132 ASSERT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
134 controller
.ExecuteCommand(IDC_BOOKMARK_BAR_REMOVE
, 0);
135 // Model shouldn't have URL anymore.
136 ASSERT_FALSE(model_
->IsBookmarked(url
));
139 // Tests open all on a folder with a couple of bookmarks.
140 TEST_F(BookmarkContextMenuTest
, OpenAll
) {
141 const BookmarkNode
* folder
= model_
->bookmark_bar_node()->GetChild(1);
142 chrome::OpenAll(NULL
, &navigator_
, folder
, NEW_FOREGROUND_TAB
, NULL
);
144 // Should have navigated to F1's child but not F11's child.
145 ASSERT_EQ(static_cast<size_t>(2), navigator_
.urls_
.size());
146 ASSERT_TRUE(folder
->GetChild(0)->url() == navigator_
.urls_
[0]);
149 // Tests open all on a folder with a couple of bookmarks in incognito window.
150 TEST_F(BookmarkContextMenuTest
, OpenAllIngonito
) {
151 const BookmarkNode
* folder
= model_
->bookmark_bar_node()->GetChild(1);
152 chrome::OpenAll(NULL
, &navigator_
, folder
, OFF_THE_RECORD
, NULL
);
154 // Should have navigated to only f1a but not f2a.
155 ASSERT_EQ(static_cast<size_t>(1), navigator_
.urls_
.size());
156 ASSERT_TRUE(folder
->GetChild(0)->url() == navigator_
.urls_
[0]);
159 // Tests the enabled state of the menus when supplied an empty vector.
160 TEST_F(BookmarkContextMenuTest
, EmptyNodes
) {
161 BookmarkContextMenu
controller(
162 NULL
, NULL
, profile_
.get(), NULL
, model_
->other_node(),
163 std::vector
<const BookmarkNode
*>(), false);
164 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
166 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
168 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
169 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
171 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
173 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
176 // Tests the enabled state of the menus when supplied a vector with a single
178 TEST_F(BookmarkContextMenuTest
, SingleURL
) {
179 std::vector
<const BookmarkNode
*> nodes
;
180 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(0));
181 BookmarkContextMenu
controller(
182 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
183 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
185 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
186 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
187 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
189 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
191 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
194 // Tests the enabled state of the menus when supplied a vector with multiple
196 TEST_F(BookmarkContextMenuTest
, MultipleURLs
) {
197 std::vector
<const BookmarkNode
*> nodes
;
198 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(0));
199 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(1)->GetChild(0));
200 BookmarkContextMenu
controller(
201 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
202 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
204 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
205 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
206 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
208 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
210 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
213 // Tests the enabled state of the menus when supplied an vector with a single
215 TEST_F(BookmarkContextMenuTest
, SingleFolder
) {
216 std::vector
<const BookmarkNode
*> nodes
;
217 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(2));
218 BookmarkContextMenu
controller(
219 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
220 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
222 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
224 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
225 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
227 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
229 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
232 // Tests the enabled state of the menus when supplied a vector with multiple
233 // folders, all of which are empty.
234 TEST_F(BookmarkContextMenuTest
, MultipleEmptyFolders
) {
235 std::vector
<const BookmarkNode
*> nodes
;
236 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(2));
237 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(3));
238 BookmarkContextMenu
controller(
239 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
240 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
242 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
244 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
245 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
247 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
249 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
252 // Tests the enabled state of the menus when supplied a vector with multiple
253 // folders, some of which contain URLs.
254 TEST_F(BookmarkContextMenuTest
, MultipleFoldersWithURLs
) {
255 std::vector
<const BookmarkNode
*> nodes
;
256 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(3));
257 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(4));
258 BookmarkContextMenu
controller(
259 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false);
260 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
262 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
263 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
264 EXPECT_TRUE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
266 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
268 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
271 // Tests the enabled state of open incognito.
272 TEST_F(BookmarkContextMenuTest
, DisableIncognito
) {
273 std::vector
<const BookmarkNode
*> nodes
;
274 nodes
.push_back(model_
->bookmark_bar_node()->GetChild(0));
275 Profile
* incognito
= profile_
->GetOffTheRecordProfile();
276 BookmarkContextMenu
controller(
277 NULL
, NULL
, incognito
, NULL
, nodes
[0]->parent(), nodes
, false);
278 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_INCOGNITO
));
280 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
283 // Tests that you can't remove/edit when showing the other node.
284 TEST_F(BookmarkContextMenuTest
, DisabledItemsWithOtherNode
) {
285 std::vector
<const BookmarkNode
*> nodes
;
286 nodes
.push_back(model_
->other_node());
287 BookmarkContextMenu
controller(
288 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0], nodes
, false);
289 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_EDIT
));
290 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
293 // Tests the enabled state of the menus when supplied an empty vector and null
295 TEST_F(BookmarkContextMenuTest
, EmptyNodesNullParent
) {
296 BookmarkContextMenu
controller(
297 NULL
, NULL
, profile_
.get(), NULL
, NULL
,
298 std::vector
<const BookmarkNode
*>(), false);
299 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL
));
301 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW
));
303 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO
));
304 EXPECT_FALSE(controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE
));
306 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK
));
308 controller
.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER
));
311 TEST_F(BookmarkContextMenuTest
, CutCopyPasteNode
) {
312 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
313 std::vector
<const BookmarkNode
*> nodes
;
314 nodes
.push_back(bb_node
->GetChild(0));
315 scoped_ptr
<BookmarkContextMenu
> controller(new BookmarkContextMenu(
316 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false));
317 EXPECT_TRUE(controller
->IsCommandEnabled(IDC_COPY
));
318 EXPECT_TRUE(controller
->IsCommandEnabled(IDC_CUT
));
321 controller
->ExecuteCommand(IDC_COPY
, 0);
323 controller
.reset(new BookmarkContextMenu(
324 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false));
325 int old_count
= bb_node
->child_count();
326 controller
->ExecuteCommand(IDC_PASTE
, 0);
328 ASSERT_TRUE(bb_node
->GetChild(1)->is_url());
329 ASSERT_EQ(old_count
+ 1, bb_node
->child_count());
330 ASSERT_EQ(bb_node
->GetChild(0)->url(), bb_node
->GetChild(1)->url());
332 controller
.reset(new BookmarkContextMenu(
333 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false));
335 controller
->ExecuteCommand(IDC_CUT
, 0);
336 ASSERT_TRUE(bb_node
->GetChild(0)->is_url());
337 ASSERT_TRUE(bb_node
->GetChild(1)->is_folder());
338 ASSERT_EQ(old_count
, bb_node
->child_count());
341 // Tests that the "Show managed bookmarks" option in the context menu is only
342 // visible if the policy is set.
343 TEST_F(BookmarkContextMenuTest
, ShowManagedBookmarks
) {
344 // Create a BookmarkContextMenu for the bookmarks bar.
345 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
346 std::vector
<const BookmarkNode
*> nodes
;
347 nodes
.push_back(bb_node
->GetChild(0));
348 scoped_ptr
<BookmarkContextMenu
> controller(new BookmarkContextMenu(
349 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false));
351 // Verify that there are no managed nodes yet.
352 ChromeBookmarkClient
* client
= ChromeBookmarkClientFactory::GetForProfile(
354 EXPECT_TRUE(client
->managed_node()->empty());
356 // The context menu should not show the option to "Show managed bookmarks".
358 controller
->IsCommandVisible(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS
));
359 views::MenuItemView
* menu
= controller
->menu();
360 EXPECT_FALSE(menu
->GetMenuItemByID(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS
)
363 // Other options are not affected.
364 EXPECT_TRUE(controller
->IsCommandVisible(IDC_BOOKMARK_BAR_NEW_FOLDER
));
365 EXPECT_TRUE(menu
->GetMenuItemByID(IDC_BOOKMARK_BAR_NEW_FOLDER
)->visible());
367 // Now set the managed bookmarks policy.
368 base::DictionaryValue
* dict
= new base::DictionaryValue
;
369 dict
->SetString("name", "Google");
370 dict
->SetString("url", "http://google.com");
371 base::ListValue list
;
373 EXPECT_TRUE(client
->managed_node()->empty());
374 profile_
->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks
, list
);
375 EXPECT_FALSE(client
->managed_node()->empty());
377 // New context menus now show the "Show managed bookmarks" option.
378 controller
.reset(new BookmarkContextMenu(
379 NULL
, NULL
, profile_
.get(), NULL
, nodes
[0]->parent(), nodes
, false));
380 EXPECT_TRUE(controller
->IsCommandVisible(IDC_BOOKMARK_BAR_NEW_FOLDER
));
382 controller
->IsCommandVisible(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS
));
383 menu
= controller
->menu();
384 EXPECT_TRUE(menu
->GetMenuItemByID(IDC_BOOKMARK_BAR_NEW_FOLDER
)->visible());
385 EXPECT_TRUE(menu
->GetMenuItemByID(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS
)