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 content::BrowserThread;
39 using content::OpenURLParams;
40 using content::PageNavigator;
41 using content::WebContents;
45 // PageNavigator implementation that records the URL.
46 class TestingPageNavigator : public PageNavigator {
48 WebContents* OpenURL(const OpenURLParams& params) override {
49 urls_.push_back(params.url);
53 std::vector<GURL> urls_;
58 class BookmarkContextMenuTest : public testing::Test {
60 BookmarkContextMenuTest()
61 : ui_thread_(BrowserThread::UI, &message_loop_),
62 file_thread_(BrowserThread::FILE, &message_loop_),
66 void SetUp() override {
67 event_source_ = ui::PlatformEventSource::CreateDefault();
68 profile_.reset(new TestingProfile());
69 profile_->CreateBookmarkModel(true);
71 model_ = BookmarkModelFactory::GetForProfile(profile_.get());
72 bookmarks::test::WaitForBookmarkModelToLoad(model_);
77 void TearDown() override {
78 ui::Clipboard::DestroyClipboardForCurrentThread();
80 BrowserThread::GetBlockingPool()->FlushForTesting();
81 // Flush the message loop to make application verifiers happy.
82 message_loop_.RunUntilIdle();
83 event_source_.reset();
87 base::MessageLoopForUI message_loop_;
88 content::TestBrowserThread ui_thread_;
89 content::TestBrowserThread file_thread_;
90 scoped_ptr<ui::PlatformEventSource> event_source_;
91 scoped_ptr<TestingProfile> profile_;
92 BookmarkModel* model_;
93 TestingPageNavigator navigator_;
96 // Creates the following structure:
100 // -f1b as "chrome://settings"
108 const BookmarkNode* bb_node = model_->bookmark_bar_node();
109 std::string test_base = "file:///c:/tmp/";
110 model_->AddURL(bb_node, 0, ASCIIToUTF16("a"), GURL(test_base + "a"));
111 const BookmarkNode* f1 = model_->AddFolder(bb_node, 1, ASCIIToUTF16("F1"));
112 model_->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a"));
113 model_->AddURL(f1, 1, ASCIIToUTF16("f1b"), GURL("chrome://settings"));
114 const BookmarkNode* f11 = model_->AddFolder(f1, 2, ASCIIToUTF16("F11"));
115 model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a"));
116 model_->AddFolder(bb_node, 2, ASCIIToUTF16("F2"));
117 model_->AddFolder(bb_node, 3, ASCIIToUTF16("F3"));
118 const BookmarkNode* f4 = model_->AddFolder(bb_node, 4, ASCIIToUTF16("F4"));
119 model_->AddURL(f4, 0, ASCIIToUTF16("f4a"), GURL(test_base + "f4a"));
123 // Tests Deleting from the menu.
124 TEST_F(BookmarkContextMenuTest, DeleteURL) {
125 std::vector<const BookmarkNode*> nodes;
126 nodes.push_back(model_->bookmark_bar_node()->GetChild(0));
127 BookmarkContextMenu controller(
128 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
129 GURL url = model_->bookmark_bar_node()->GetChild(0)->url();
130 ASSERT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
132 controller.ExecuteCommand(IDC_BOOKMARK_BAR_REMOVE, 0);
133 // Model shouldn't have URL anymore.
134 ASSERT_FALSE(model_->IsBookmarked(url));
137 // Tests open all on a folder with a couple of bookmarks.
138 TEST_F(BookmarkContextMenuTest, OpenAll) {
139 const BookmarkNode* folder = model_->bookmark_bar_node()->GetChild(1);
140 chrome::OpenAll(NULL, &navigator_, folder, NEW_FOREGROUND_TAB, NULL);
142 // Should have navigated to F1's child but not F11's child.
143 ASSERT_EQ(static_cast<size_t>(2), navigator_.urls_.size());
144 ASSERT_TRUE(folder->GetChild(0)->url() == navigator_.urls_[0]);
147 // Tests open all on a folder with a couple of bookmarks in incognito window.
148 TEST_F(BookmarkContextMenuTest, OpenAllIngonito) {
149 const BookmarkNode* folder = model_->bookmark_bar_node()->GetChild(1);
150 chrome::OpenAll(NULL, &navigator_, folder, OFF_THE_RECORD, NULL);
152 // Should have navigated to only f1a but not f2a.
153 ASSERT_EQ(static_cast<size_t>(1), navigator_.urls_.size());
154 ASSERT_TRUE(folder->GetChild(0)->url() == navigator_.urls_[0]);
157 // Tests the enabled state of the menus when supplied an empty vector.
158 TEST_F(BookmarkContextMenuTest, EmptyNodes) {
159 BookmarkContextMenu controller(
160 NULL, NULL, profile_.get(), NULL, model_->other_node(),
161 std::vector<const BookmarkNode*>(), false);
162 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
164 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
166 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
167 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
169 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
171 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
174 // Tests the enabled state of the menus when supplied a vector with a single
176 TEST_F(BookmarkContextMenuTest, SingleURL) {
177 std::vector<const BookmarkNode*> nodes;
178 nodes.push_back(model_->bookmark_bar_node()->GetChild(0));
179 BookmarkContextMenu controller(
180 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
181 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
183 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
184 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
185 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
187 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
189 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
192 // Tests the enabled state of the menus when supplied a vector with multiple
194 TEST_F(BookmarkContextMenuTest, MultipleURLs) {
195 std::vector<const BookmarkNode*> nodes;
196 nodes.push_back(model_->bookmark_bar_node()->GetChild(0));
197 nodes.push_back(model_->bookmark_bar_node()->GetChild(1)->GetChild(0));
198 BookmarkContextMenu controller(
199 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
200 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
202 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
203 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
204 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
206 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
208 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
211 // Tests the enabled state of the menus when supplied an vector with a single
213 TEST_F(BookmarkContextMenuTest, SingleFolder) {
214 std::vector<const BookmarkNode*> nodes;
215 nodes.push_back(model_->bookmark_bar_node()->GetChild(2));
216 BookmarkContextMenu controller(
217 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
218 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
220 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
222 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
223 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
225 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
227 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
230 // Tests the enabled state of the menus when supplied a vector with multiple
231 // folders, all of which are empty.
232 TEST_F(BookmarkContextMenuTest, MultipleEmptyFolders) {
233 std::vector<const BookmarkNode*> nodes;
234 nodes.push_back(model_->bookmark_bar_node()->GetChild(2));
235 nodes.push_back(model_->bookmark_bar_node()->GetChild(3));
236 BookmarkContextMenu controller(
237 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
238 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
240 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
242 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
243 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
245 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
247 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
250 // Tests the enabled state of the menus when supplied a vector with multiple
251 // folders, some of which contain URLs.
252 TEST_F(BookmarkContextMenuTest, MultipleFoldersWithURLs) {
253 std::vector<const BookmarkNode*> nodes;
254 nodes.push_back(model_->bookmark_bar_node()->GetChild(3));
255 nodes.push_back(model_->bookmark_bar_node()->GetChild(4));
256 BookmarkContextMenu controller(
257 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false);
258 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
260 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
261 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
262 EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
264 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
266 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
269 // Tests the enabled state of open incognito.
270 TEST_F(BookmarkContextMenuTest, DisableIncognito) {
271 std::vector<const BookmarkNode*> nodes;
272 nodes.push_back(model_->bookmark_bar_node()->GetChild(0));
273 Profile* incognito = profile_->GetOffTheRecordProfile();
274 BookmarkContextMenu controller(
275 NULL, NULL, incognito, NULL, nodes[0]->parent(), nodes, false);
276 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_INCOGNITO));
278 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
281 // Tests that you can't remove/edit when showing the other node.
282 TEST_F(BookmarkContextMenuTest, DisabledItemsWithOtherNode) {
283 std::vector<const BookmarkNode*> nodes;
284 nodes.push_back(model_->other_node());
285 BookmarkContextMenu controller(
286 NULL, NULL, profile_.get(), NULL, nodes[0], nodes, false);
287 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_EDIT));
288 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
291 // Tests the enabled state of the menus when supplied an empty vector and null
293 TEST_F(BookmarkContextMenuTest, EmptyNodesNullParent) {
294 BookmarkContextMenu controller(
295 NULL, NULL, profile_.get(), NULL, NULL,
296 std::vector<const BookmarkNode*>(), false);
297 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL));
299 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
301 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
302 EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
304 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
306 controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
309 TEST_F(BookmarkContextMenuTest, CutCopyPasteNode) {
310 const BookmarkNode* bb_node = model_->bookmark_bar_node();
311 std::vector<const BookmarkNode*> nodes;
312 nodes.push_back(bb_node->GetChild(0));
313 scoped_ptr<BookmarkContextMenu> controller(new BookmarkContextMenu(
314 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
315 EXPECT_TRUE(controller->IsCommandEnabled(IDC_COPY));
316 EXPECT_TRUE(controller->IsCommandEnabled(IDC_CUT));
319 controller->ExecuteCommand(IDC_COPY, 0);
321 controller.reset(new BookmarkContextMenu(
322 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
323 int old_count = bb_node->child_count();
324 controller->ExecuteCommand(IDC_PASTE, 0);
326 ASSERT_TRUE(bb_node->GetChild(1)->is_url());
327 ASSERT_EQ(old_count + 1, bb_node->child_count());
328 ASSERT_EQ(bb_node->GetChild(0)->url(), bb_node->GetChild(1)->url());
330 controller.reset(new BookmarkContextMenu(
331 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
333 controller->ExecuteCommand(IDC_CUT, 0);
334 ASSERT_TRUE(bb_node->GetChild(0)->is_url());
335 ASSERT_TRUE(bb_node->GetChild(1)->is_folder());
336 ASSERT_EQ(old_count, bb_node->child_count());
339 // Tests that the "Show managed bookmarks" option in the context menu is only
340 // visible if the policy is set.
341 TEST_F(BookmarkContextMenuTest, ShowManagedBookmarks) {
342 // Create a BookmarkContextMenu for the bookmarks bar.
343 const BookmarkNode* bb_node = model_->bookmark_bar_node();
344 std::vector<const BookmarkNode*> nodes;
345 nodes.push_back(bb_node->GetChild(0));
346 scoped_ptr<BookmarkContextMenu> controller(new BookmarkContextMenu(
347 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
349 // Verify that there are no managed nodes yet.
350 ChromeBookmarkClient* client = ChromeBookmarkClientFactory::GetForProfile(
352 EXPECT_TRUE(client->managed_node()->empty());
354 // The context menu should not show the option to "Show managed bookmarks".
356 controller->IsCommandVisible(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS));
357 views::MenuItemView* menu = controller->menu();
358 EXPECT_FALSE(menu->GetMenuItemByID(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS)
361 // Other options are not affected.
362 EXPECT_TRUE(controller->IsCommandVisible(IDC_BOOKMARK_BAR_NEW_FOLDER));
363 EXPECT_TRUE(menu->GetMenuItemByID(IDC_BOOKMARK_BAR_NEW_FOLDER)->visible());
365 // Now set the managed bookmarks policy.
366 base::DictionaryValue* dict = new base::DictionaryValue;
367 dict->SetString("name", "Google");
368 dict->SetString("url", "http://google.com");
369 base::ListValue list;
371 EXPECT_TRUE(client->managed_node()->empty());
372 profile_->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks, list);
373 EXPECT_FALSE(client->managed_node()->empty());
375 // New context menus now show the "Show managed bookmarks" option.
376 controller.reset(new BookmarkContextMenu(
377 NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
378 EXPECT_TRUE(controller->IsCommandVisible(IDC_BOOKMARK_BAR_NEW_FOLDER));
380 controller->IsCommandVisible(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS));
381 menu = controller->menu();
382 EXPECT_TRUE(menu->GetMenuItemByID(IDC_BOOKMARK_BAR_NEW_FOLDER)->visible());
383 EXPECT_TRUE(menu->GetMenuItemByID(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS)