Home | History | Annotate | Download | only in bookmarks
      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.
      4 
      5 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     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"
     32 
     33 #if defined(OS_WIN)
     34 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
     35 #endif
     36 
     37 using base::ASCIIToUTF16;
     38 using content::BrowserThread;
     39 using content::OpenURLParams;
     40 using content::PageNavigator;
     41 using content::WebContents;
     42 
     43 namespace {
     44 
     45 // PageNavigator implementation that records the URL.
     46 class TestingPageNavigator : public PageNavigator {
     47  public:
     48   virtual WebContents* OpenURL(const OpenURLParams& params) OVERRIDE {
     49     urls_.push_back(params.url);
     50     return NULL;
     51   }
     52 
     53   std::vector<GURL> urls_;
     54 };
     55 
     56 }  // namespace
     57 
     58 class BookmarkContextMenuTest : public testing::Test {
     59  public:
     60   BookmarkContextMenuTest()
     61       : ui_thread_(BrowserThread::UI, &message_loop_),
     62         file_thread_(BrowserThread::FILE, &message_loop_),
     63         model_(NULL) {
     64   }
     65 
     66   virtual void SetUp() OVERRIDE {
     67     event_source_ = ui::PlatformEventSource::CreateDefault();
     68     profile_.reset(new TestingProfile());
     69     profile_->CreateBookmarkModel(true);
     70 
     71     model_ = BookmarkModelFactory::GetForProfile(profile_.get());
     72     test::WaitForBookmarkModelToLoad(model_);
     73 
     74     AddTestData();
     75   }
     76 
     77   virtual void TearDown() OVERRIDE {
     78     ui::Clipboard::DestroyClipboardForCurrentThread();
     79 
     80     BrowserThread::GetBlockingPool()->FlushForTesting();
     81     // Flush the message loop to make application verifiers happy.
     82     message_loop_.RunUntilIdle();
     83     event_source_.reset();
     84   }
     85 
     86  protected:
     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_;
     94 
     95  private:
     96   // Creates the following structure:
     97   // a
     98   // F1
     99   //  f1a
    100   // -f1b as "chrome://settings"
    101   //  F11
    102   //   f11a
    103   // F2
    104   // F3
    105   // F4
    106   //   f4a
    107   void AddTestData() {
    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"));
    120   }
    121 };
    122 
    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));
    131   // Delete the URL.
    132   controller.ExecuteCommand(IDC_BOOKMARK_BAR_REMOVE, 0);
    133   // Model shouldn't have URL anymore.
    134   ASSERT_FALSE(model_->IsBookmarked(url));
    135 }
    136 
    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);
    141 
    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]);
    145 }
    146 
    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);
    151 
    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]);
    155 }
    156 
    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));
    163   EXPECT_FALSE(
    164       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
    165   EXPECT_FALSE(
    166       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
    167   EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
    168   EXPECT_TRUE(
    169       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    170   EXPECT_TRUE(
    171       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    172 }
    173 
    174 // Tests the enabled state of the menus when supplied a vector with a single
    175 // url.
    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));
    182   EXPECT_TRUE(
    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));
    186   EXPECT_TRUE(
    187       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    188   EXPECT_TRUE(
    189       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    190 }
    191 
    192 // Tests the enabled state of the menus when supplied a vector with multiple
    193 // urls.
    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));
    201   EXPECT_TRUE(
    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));
    205   EXPECT_TRUE(
    206       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    207   EXPECT_TRUE(
    208       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    209 }
    210 
    211 // Tests the enabled state of the menus when supplied an vector with a single
    212 // folder.
    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));
    219   EXPECT_FALSE(
    220       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
    221   EXPECT_FALSE(
    222       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
    223   EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
    224   EXPECT_TRUE(
    225       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    226   EXPECT_TRUE(
    227       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    228 }
    229 
    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));
    239   EXPECT_FALSE(
    240       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
    241   EXPECT_FALSE(
    242       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
    243   EXPECT_TRUE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
    244   EXPECT_TRUE(
    245       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    246   EXPECT_TRUE(
    247       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    248 }
    249 
    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));
    259   EXPECT_TRUE(
    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));
    263   EXPECT_TRUE(
    264       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    265   EXPECT_TRUE(
    266       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    267 }
    268 
    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));
    277   EXPECT_FALSE(
    278       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
    279 }
    280 
    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));
    289 }
    290 
    291 // Tests the enabled state of the menus when supplied an empty vector and null
    292 // parent.
    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));
    298   EXPECT_FALSE(
    299       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
    300   EXPECT_FALSE(
    301       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
    302   EXPECT_FALSE(controller.IsCommandEnabled(IDC_BOOKMARK_BAR_REMOVE));
    303   EXPECT_FALSE(
    304       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
    305   EXPECT_FALSE(
    306       controller.IsCommandEnabled(IDC_BOOKMARK_BAR_NEW_FOLDER));
    307 }
    308 
    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));
    317 
    318   // Copy the URL.
    319   controller->ExecuteCommand(IDC_COPY, 0);
    320 
    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);
    325 
    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());
    329 
    330   controller.reset(new BookmarkContextMenu(
    331       NULL, NULL, profile_.get(), NULL, nodes[0]->parent(), nodes, false));
    332   // Cut the URL.
    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());
    337 }
    338 
    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));
    348 
    349   // Verify that there are no managed nodes yet.
    350   ChromeBookmarkClient* client = ChromeBookmarkClientFactory::GetForProfile(
    351       profile_.get());
    352   EXPECT_TRUE(client->managed_node()->empty());
    353 
    354   // The context menu should not show the option to "Show managed bookmarks".
    355   EXPECT_FALSE(
    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)
    359                    ->visible());
    360 
    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());
    364 
    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;
    370   list.Append(dict);
    371   EXPECT_TRUE(client->managed_node()->empty());
    372   profile_->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks, list);
    373   EXPECT_FALSE(client->managed_node()->empty());
    374 
    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));
    379   EXPECT_TRUE(
    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)
    384                   ->visible());
    385 }
    386