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/bookmarks/bookmark_context_menu_controller.h" 6 7#include <string> 8 9#include "base/memory/scoped_ptr.h" 10#include "base/message_loop/message_loop.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/values.h" 13#include "chrome/app/chrome_command_ids.h" 14#include "chrome/browser/bookmarks/bookmark_model_factory.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/ui/bookmarks/bookmark_utils.h" 17#include "chrome/common/pref_names.h" 18#include "chrome/test/base/testing_pref_service_syncable.h" 19#include "chrome/test/base/testing_profile.h" 20#include "components/bookmarks/browser/bookmark_model.h" 21#include "components/bookmarks/browser/bookmark_node.h" 22#include "components/bookmarks/test/bookmark_test_helpers.h" 23#include "content/public/browser/page_navigator.h" 24#include "content/public/test/test_browser_thread.h" 25#include "grit/generated_resources.h" 26#include "testing/gtest/include/gtest/gtest.h" 27#include "ui/base/clipboard/clipboard.h" 28 29using base::ASCIIToUTF16; 30using content::BrowserThread; 31using content::OpenURLParams; 32using content::PageNavigator; 33using content::WebContents; 34 35// PageNavigator implementation that records the URL. 36class TestingPageNavigator : public PageNavigator { 37 public: 38 virtual WebContents* OpenURL(const OpenURLParams& params) OVERRIDE { 39 urls_.push_back(params.url); 40 return NULL; 41 } 42 43 std::vector<GURL> urls_; 44}; 45 46class BookmarkContextMenuControllerTest : public testing::Test { 47 public: 48 BookmarkContextMenuControllerTest() 49 : ui_thread_(BrowserThread::UI, &message_loop_), 50 file_thread_(BrowserThread::FILE, &message_loop_), 51 model_(NULL) { 52 } 53 54 virtual void SetUp() OVERRIDE { 55 TestingProfile::Builder builder; 56 profile_ = builder.Build(); 57 profile_->CreateBookmarkModel(true); 58 model_ = BookmarkModelFactory::GetForProfile(profile_.get()); 59 test::WaitForBookmarkModelToLoad(model_); 60 AddTestData(model_); 61 } 62 63 virtual void TearDown() OVERRIDE { 64 ui::Clipboard::DestroyClipboardForCurrentThread(); 65 66 // Flush the message loop to make application verifiers happy. 67 message_loop_.RunUntilIdle(); 68 } 69 70 // Creates the following structure: 71 // a 72 // F1 73 // f1a 74 // F11 75 // f11a 76 // F2 77 // F3 78 // F4 79 // f4a 80 static void AddTestData(BookmarkModel* model) { 81 const BookmarkNode* bb_node = model->bookmark_bar_node(); 82 std::string test_base = "file:///c:/tmp/"; 83 model->AddURL(bb_node, 0, ASCIIToUTF16("a"), GURL(test_base + "a")); 84 const BookmarkNode* f1 = model->AddFolder(bb_node, 1, ASCIIToUTF16("F1")); 85 model->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a")); 86 const BookmarkNode* f11 = model->AddFolder(f1, 1, ASCIIToUTF16("F11")); 87 model->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a")); 88 model->AddFolder(bb_node, 2, ASCIIToUTF16("F2")); 89 model->AddFolder(bb_node, 3, ASCIIToUTF16("F3")); 90 const BookmarkNode* f4 = model->AddFolder(bb_node, 4, ASCIIToUTF16("F4")); 91 model->AddURL(f4, 0, ASCIIToUTF16("f4a"), GURL(test_base + "f4a")); 92 } 93 94 protected: 95 base::MessageLoopForUI message_loop_; 96 content::TestBrowserThread ui_thread_; 97 content::TestBrowserThread file_thread_; 98 scoped_ptr<TestingProfile> profile_; 99 BookmarkModel* model_; 100 TestingPageNavigator navigator_; 101}; 102 103// Tests Deleting from the menu. 104TEST_F(BookmarkContextMenuControllerTest, DeleteURL) { 105 std::vector<const BookmarkNode*> nodes; 106 nodes.push_back(model_->bookmark_bar_node()->GetChild(0)); 107 BookmarkContextMenuController controller( 108 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 109 GURL url = model_->bookmark_bar_node()->GetChild(0)->url(); 110 ASSERT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 111 // Delete the URL. 112 controller.ExecuteCommand(IDC_BOOKMARK_BAR_REMOVE, 0); 113 // Model shouldn't have URL anymore. 114 ASSERT_FALSE(model_->IsBookmarked(url)); 115} 116 117// Tests open all on a folder with a couple of bookmarks. 118TEST_F(BookmarkContextMenuControllerTest, OpenAll) { 119 const BookmarkNode* folder = model_->bookmark_bar_node()->GetChild(1); 120 chrome::OpenAll(NULL, &navigator_, folder, NEW_FOREGROUND_TAB, NULL); 121 122 // Should have navigated to F1's child, but not F11's child. 123 ASSERT_EQ(static_cast<size_t>(1), navigator_.urls_.size()); 124 ASSERT_TRUE(folder->GetChild(0)->url() == navigator_.urls_[0]); 125} 126 127// Tests the enabled state of the menus when supplied an empty vector. 128TEST_F(BookmarkContextMenuControllerTest, EmptyNodes) { 129 BookmarkContextMenuController controller( 130 NULL, NULL, NULL, profile_.get(), NULL, model_->other_node(), 131 std::vector<const BookmarkNode*>()); 132 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 133 EXPECT_FALSE( 134 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 135 EXPECT_FALSE( 136 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 137 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 138 EXPECT_TRUE( 139 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 140 EXPECT_TRUE( 141 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 142} 143 144// Tests the enabled state of the menus when supplied a vector with a single 145// url. 146TEST_F(BookmarkContextMenuControllerTest, SingleURL) { 147 std::vector<const BookmarkNode*> nodes; 148 nodes.push_back(model_->bookmark_bar_node()->GetChild(0)); 149 BookmarkContextMenuController controller( 150 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 151 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 152 EXPECT_TRUE( 153 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 154 EXPECT_TRUE( 155 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 156 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 157 EXPECT_TRUE( 158 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 159 EXPECT_TRUE( 160 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 161} 162 163// Tests the enabled state of the menus when supplied a vector with multiple 164// urls. 165TEST_F(BookmarkContextMenuControllerTest, MultipleURLs) { 166 std::vector<const BookmarkNode*> nodes; 167 nodes.push_back(model_->bookmark_bar_node()->GetChild(0)); 168 nodes.push_back(model_->bookmark_bar_node()->GetChild(1)->GetChild(0)); 169 BookmarkContextMenuController controller( 170 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 171 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 172 EXPECT_TRUE( 173 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 174 EXPECT_TRUE( 175 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 176 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 177 EXPECT_TRUE( 178 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 179 EXPECT_TRUE( 180 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 181} 182 183// Tests the enabled state of the menus when supplied an vector with a single 184// folder. 185TEST_F(BookmarkContextMenuControllerTest, SingleFolder) { 186 std::vector<const BookmarkNode*> nodes; 187 nodes.push_back(model_->bookmark_bar_node()->GetChild(2)); 188 BookmarkContextMenuController controller( 189 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 190 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 191 EXPECT_FALSE( 192 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 193 EXPECT_FALSE( 194 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 195 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 196 EXPECT_TRUE( 197 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 198 EXPECT_TRUE( 199 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 200} 201 202// Tests the enabled state of the menus when supplied a vector with multiple 203// folders, all of which are empty. 204TEST_F(BookmarkContextMenuControllerTest, MultipleEmptyFolders) { 205 std::vector<const BookmarkNode*> nodes; 206 nodes.push_back(model_->bookmark_bar_node()->GetChild(2)); 207 nodes.push_back(model_->bookmark_bar_node()->GetChild(3)); 208 BookmarkContextMenuController controller( 209 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 210 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 211 EXPECT_FALSE( 212 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 213 EXPECT_FALSE( 214 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 215 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 216 EXPECT_TRUE( 217 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 218 EXPECT_TRUE( 219 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 220} 221 222// Tests the enabled state of the menus when supplied a vector with multiple 223// folders, some of which contain URLs. 224TEST_F(BookmarkContextMenuControllerTest, MultipleFoldersWithURLs) { 225 std::vector<const BookmarkNode*> nodes; 226 nodes.push_back(model_->bookmark_bar_node()->GetChild(3)); 227 nodes.push_back(model_->bookmark_bar_node()->GetChild(4)); 228 BookmarkContextMenuController controller( 229 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 230 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 231 EXPECT_TRUE( 232 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 233 EXPECT_TRUE( 234 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 235 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 236 EXPECT_TRUE( 237 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 238 EXPECT_TRUE( 239 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 240} 241 242// Tests the enabled state of open incognito. 243TEST_F(BookmarkContextMenuControllerTest, DisableIncognito) { 244 // Create an incognito Profile. It must be associated with the original 245 // Profile, so that GetOriginalProfile() works as expected. 246 TestingProfile::Builder builder; 247 builder.SetIncognito(); 248 scoped_ptr<TestingProfile> testing_incognito = builder.Build(); 249 testing_incognito->SetOriginalProfile(profile_.get()); 250 TestingProfile* incognito = testing_incognito.get(); 251 profile_->SetOffTheRecordProfile(testing_incognito.PassAs<Profile>()); 252 253 incognito->CreateBookmarkModel(true); 254 BookmarkModel* model = BookmarkModelFactory::GetForProfile(incognito); 255 test::WaitForBookmarkModelToLoad(model); 256 AddTestData(model); 257 258 std::vector<const BookmarkNode*> nodes; 259 nodes.push_back(model->bookmark_bar_node()->GetChild(0)); 260 BookmarkContextMenuController controller( 261 NULL, NULL, NULL, incognito, NULL, nodes[0]->parent(), nodes); 262 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_INCOGNITO)); 263 EXPECT_FALSE( 264 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 265} 266 267// Tests that you can't remove/edit when showing the other node. 268TEST_F(BookmarkContextMenuControllerTest, DisabledItemsWithOtherNode) { 269 std::vector<const BookmarkNode*> nodes; 270 nodes.push_back(model_->other_node()); 271 BookmarkContextMenuController controller( 272 NULL, NULL, NULL, profile_.get(), NULL, nodes[0], nodes); 273 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_EDIT)); 274 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 275} 276 277// Tests the enabled state of the menus when supplied an empty vector and null 278// parent. 279TEST_F(BookmarkContextMenuControllerTest, EmptyNodesNullParent) { 280 BookmarkContextMenuController controller( 281 NULL, NULL, NULL, profile_.get(), NULL, NULL, 282 std::vector<const BookmarkNode*>()); 283 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 284 EXPECT_FALSE( 285 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 286 EXPECT_FALSE( 287 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 288 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 289 EXPECT_FALSE( 290 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 291 EXPECT_FALSE( 292 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 293} 294 295// Tests the enabled state of the menus when supplied a vector containing just 296// the top-level bookmark bar node. 297TEST_F(BookmarkContextMenuControllerTest, BookmarkBar) { 298 std::vector<const BookmarkNode*> nodes; 299 nodes.push_back(model_->bookmark_bar_node()); 300 BookmarkContextMenuController controller( 301 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes); 302 EXPECT_TRUE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL)); 303 EXPECT_TRUE( 304 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW)); 305 EXPECT_TRUE( 306 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO)); 307 EXPECT_FALSE(controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_REMOVE)); 308 EXPECT_TRUE( 309 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK)); 310 EXPECT_TRUE( 311 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER)); 312} 313 314TEST_F(BookmarkContextMenuControllerTest, CutCopyPasteNode) { 315 const BookmarkNode* bb_node = model_->bookmark_bar_node(); 316 std::vector<const BookmarkNode*> nodes; 317 nodes.push_back(bb_node->GetChild(0)); 318 scoped_ptr<BookmarkContextMenuController> controller( 319 new BookmarkContextMenuController( 320 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes)); 321 EXPECT_TRUE(controller->IsCommandIdEnabled(IDC_COPY)); 322 EXPECT_TRUE(controller->IsCommandIdEnabled(IDC_CUT)); 323 324 // Copy the URL. 325 controller->ExecuteCommand(IDC_COPY, 0); 326 327 controller.reset(new BookmarkContextMenuController( 328 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes)); 329 int old_count = bb_node->child_count(); 330 controller->ExecuteCommand(IDC_PASTE, 0); 331 332 ASSERT_TRUE(bb_node->GetChild(1)->is_url()); 333 ASSERT_EQ(old_count + 1, bb_node->child_count()); 334 ASSERT_EQ(bb_node->GetChild(0)->url(), bb_node->GetChild(1)->url()); 335 336 controller.reset(new BookmarkContextMenuController( 337 NULL, NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes)); 338 // Cut the URL. 339 controller->ExecuteCommand(IDC_CUT, 0); 340 ASSERT_TRUE(bb_node->GetChild(0)->is_url()); 341 ASSERT_TRUE(bb_node->GetChild(1)->is_folder()); 342 ASSERT_EQ(old_count, bb_node->child_count()); 343} 344 345TEST_F(BookmarkContextMenuControllerTest, 346 ManagedShowAppsShortcutInBookmarksBar) { 347 BookmarkContextMenuController controller( 348 NULL, NULL, NULL, profile_.get(), NULL, model_->bookmark_bar_node(), 349 std::vector<const BookmarkNode*>()); 350 351 // By default, the pref is not managed and the command is enabled. 352 TestingPrefServiceSyncable* prefs = profile_->GetTestingPrefService(); 353 EXPECT_FALSE( 354 prefs->IsManagedPreference(prefs::kShowAppsShortcutInBookmarkBar)); 355 EXPECT_TRUE( 356 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT)); 357 358 // Disabling the shorcut by policy disables the command. 359 prefs->SetManagedPref(prefs::kShowAppsShortcutInBookmarkBar, 360 new base::FundamentalValue(false)); 361 EXPECT_FALSE( 362 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT)); 363 364 // And enabling the shortcut by policy disables the command too. 365 prefs->SetManagedPref(prefs::kShowAppsShortcutInBookmarkBar, 366 new base::FundamentalValue(true)); 367 EXPECT_FALSE( 368 controller.IsCommandIdEnabled(IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT)); 369} 370