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 "base/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/prefs/pref_service.h"
10#include "chrome/app/chrome_command_ids.h"
11#include "chrome/browser/bookmarks/bookmark_model_factory.h"
12#include "chrome/browser/bookmarks/chrome_bookmark_client.h"
13#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
14#include "chrome/browser/prefs/incognito_mode_prefs.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/ui/bookmarks/bookmark_editor.h"
17#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/chrome_pages.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
21#include "chrome/browser/undo/bookmark_undo_service.h"
22#include "chrome/browser/undo/bookmark_undo_service_factory.h"
23#include "chrome/common/chrome_switches.h"
24#include "chrome/common/pref_names.h"
25#include "chrome/grit/generated_resources.h"
26#include "components/bookmarks/browser/bookmark_client.h"
27#include "components/bookmarks/browser/bookmark_model.h"
28#include "components/bookmarks/browser/bookmark_utils.h"
29#include "content/public/browser/page_navigator.h"
30#include "content/public/browser/user_metrics.h"
31#include "ui/base/l10n/l10n_util.h"
32
33using base::UserMetricsAction;
34using content::PageNavigator;
35
36BookmarkContextMenuController::BookmarkContextMenuController(
37    gfx::NativeWindow parent_window,
38    BookmarkContextMenuControllerDelegate* delegate,
39    Browser* browser,
40    Profile* profile,
41    PageNavigator* navigator,
42    const BookmarkNode* parent,
43    const std::vector<const BookmarkNode*>& selection)
44    : parent_window_(parent_window),
45      delegate_(delegate),
46      browser_(browser),
47      profile_(profile),
48      navigator_(navigator),
49      parent_(parent),
50      selection_(selection),
51      model_(BookmarkModelFactory::GetForProfile(profile)) {
52  DCHECK(profile_);
53  DCHECK(model_->loaded());
54  menu_model_.reset(new ui::SimpleMenuModel(this));
55  model_->AddObserver(this);
56
57  BuildMenu();
58}
59
60BookmarkContextMenuController::~BookmarkContextMenuController() {
61  if (model_)
62    model_->RemoveObserver(this);
63}
64
65void BookmarkContextMenuController::BuildMenu() {
66  if (selection_.size() == 1 && selection_[0]->is_url()) {
67    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL,
68            IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB);
69    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
70            IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW);
71    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
72            IDS_BOOKMARK_BAR_OPEN_INCOGNITO);
73  } else {
74    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL, IDS_BOOKMARK_BAR_OPEN_ALL);
75    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
76            IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
77    AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
78            IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO);
79  }
80
81  AddSeparator();
82  if (selection_.size() == 1 && selection_[0]->is_folder()) {
83    AddItem(IDC_BOOKMARK_BAR_RENAME_FOLDER, IDS_BOOKMARK_BAR_RENAME_FOLDER);
84  } else {
85    AddItem(IDC_BOOKMARK_BAR_EDIT, IDS_BOOKMARK_BAR_EDIT);
86  }
87
88  AddSeparator();
89  AddItem(IDC_CUT, IDS_CUT);
90  AddItem(IDC_COPY, IDS_COPY);
91  AddItem(IDC_PASTE, IDS_PASTE);
92
93  AddSeparator();
94  AddItem(IDC_BOOKMARK_BAR_REMOVE, IDS_BOOKMARK_BAR_REMOVE);
95  if (CommandLine::ForCurrentProcess()->HasSwitch(
96      switches::kEnableBookmarkUndo)) {
97    AddItem(IDC_BOOKMARK_BAR_UNDO, IDS_BOOKMARK_BAR_UNDO);
98    AddItem(IDC_BOOKMARK_BAR_REDO, IDS_BOOKMARK_BAR_REDO);
99  }
100
101  AddSeparator();
102  AddItem(IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK, IDS_BOOKMARK_BAR_ADD_NEW_BOOKMARK);
103  AddItem(IDC_BOOKMARK_BAR_NEW_FOLDER, IDS_BOOKMARK_BAR_NEW_FOLDER);
104
105  AddSeparator();
106  AddItem(IDC_BOOKMARK_MANAGER, IDS_BOOKMARK_MANAGER);
107  // Use the native host desktop type in tests.
108  if (chrome::IsAppsShortcutEnabled(
109          profile_,
110          browser_ ? browser_->host_desktop_type()
111                   : chrome::HOST_DESKTOP_TYPE_NATIVE)) {
112    AddCheckboxItem(IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT,
113                    IDS_BOOKMARK_BAR_SHOW_APPS_SHORTCUT);
114  }
115  AddCheckboxItem(IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS,
116                  IDS_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS_DEFAULT_NAME);
117  AddCheckboxItem(IDC_BOOKMARK_BAR_ALWAYS_SHOW, IDS_SHOW_BOOKMARK_BAR);
118}
119
120void BookmarkContextMenuController::AddItem(int id, int localization_id) {
121  menu_model_->AddItemWithStringId(id, localization_id);
122}
123
124void BookmarkContextMenuController::AddSeparator() {
125  menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
126}
127
128void BookmarkContextMenuController::AddCheckboxItem(int id,
129                                                    int localization_id) {
130  menu_model_->AddCheckItemWithStringId(id, localization_id);
131}
132
133void BookmarkContextMenuController::ExecuteCommand(int id, int event_flags) {
134  if (delegate_)
135    delegate_->WillExecuteCommand(id, selection_);
136
137  switch (id) {
138    case IDC_BOOKMARK_BAR_OPEN_ALL:
139    case IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO:
140    case IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW: {
141      WindowOpenDisposition initial_disposition;
142      if (id == IDC_BOOKMARK_BAR_OPEN_ALL) {
143        initial_disposition = NEW_BACKGROUND_TAB;
144        content::RecordAction(
145            UserMetricsAction("BookmarkBar_ContextMenu_OpenAll"));
146      } else if (id == IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW) {
147        initial_disposition = NEW_WINDOW;
148        content::RecordAction(
149            UserMetricsAction("BookmarkBar_ContextMenu_OpenAllInNewWindow"));
150      } else {
151        initial_disposition = OFF_THE_RECORD;
152        content::RecordAction(
153            UserMetricsAction("BookmarkBar_ContextMenu_OpenAllIncognito"));
154      }
155      chrome::OpenAll(parent_window_, navigator_, selection_,
156                      initial_disposition, profile_);
157      break;
158    }
159
160    case IDC_BOOKMARK_BAR_RENAME_FOLDER:
161    case IDC_BOOKMARK_BAR_EDIT:
162      content::RecordAction(
163          UserMetricsAction("BookmarkBar_ContextMenu_Edit"));
164
165      if (selection_.size() != 1) {
166        NOTREACHED();
167        break;
168      }
169
170      BookmarkEditor::Show(
171          parent_window_,
172          profile_,
173          BookmarkEditor::EditDetails::EditNode(selection_[0]),
174          selection_[0]->is_url() ? BookmarkEditor::SHOW_TREE :
175                                    BookmarkEditor::NO_TREE);
176      break;
177
178    case IDC_BOOKMARK_BAR_UNDO: {
179      content::RecordAction(
180          UserMetricsAction("BookmarkBar_ContextMenu_Undo"));
181      BookmarkUndoServiceFactory::GetForProfile(profile_)->undo_manager()->
182          Undo();
183      break;
184    }
185
186    case IDC_BOOKMARK_BAR_REDO: {
187      content::RecordAction(
188          UserMetricsAction("BookmarkBar_ContextMenu_Redo"));
189      BookmarkUndoServiceFactory::GetForProfile(profile_)->undo_manager()->
190          Redo();
191      break;
192    }
193
194    case IDC_BOOKMARK_BAR_REMOVE: {
195      content::RecordAction(
196          UserMetricsAction("BookmarkBar_ContextMenu_Remove"));
197
198      for (size_t i = 0; i < selection_.size(); ++i) {
199        int index = selection_[i]->parent()->GetIndexOf(selection_[i]);
200        if (index > -1)
201          model_->Remove(selection_[i]->parent(), index);
202      }
203      selection_.clear();
204      break;
205    }
206
207    case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK: {
208      content::RecordAction(
209          UserMetricsAction("BookmarkBar_ContextMenu_Add"));
210
211      int index;
212      const BookmarkNode* parent =
213          bookmarks::GetParentForNewNodes(parent_, selection_, &index);
214      GURL url;
215      base::string16 title;
216      chrome::GetURLAndTitleToBookmark(
217          browser_->tab_strip_model()->GetActiveWebContents(),
218          &url, &title);
219      BookmarkEditor::Show(parent_window_,
220                           profile_,
221                           BookmarkEditor::EditDetails::AddNodeInFolder(
222                               parent, index, url, title),
223                           BookmarkEditor::SHOW_TREE);
224      break;
225    }
226
227    case IDC_BOOKMARK_BAR_NEW_FOLDER: {
228      content::RecordAction(
229          UserMetricsAction("BookmarkBar_ContextMenu_NewFolder"));
230
231      int index;
232      const BookmarkNode* parent =
233          bookmarks::GetParentForNewNodes(parent_, selection_, &index);
234      BookmarkEditor::Show(
235          parent_window_,
236          profile_,
237          BookmarkEditor::EditDetails::AddFolder(parent, index),
238          BookmarkEditor::SHOW_TREE);
239      break;
240    }
241
242    case IDC_BOOKMARK_BAR_ALWAYS_SHOW:
243      chrome::ToggleBookmarkBarWhenVisible(profile_);
244      break;
245
246    case IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT: {
247      PrefService* prefs = profile_->GetPrefs();
248      prefs->SetBoolean(
249          bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
250          !prefs->GetBoolean(bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
251      break;
252    }
253
254    case IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS: {
255      PrefService* prefs = profile_->GetPrefs();
256      prefs->SetBoolean(
257          bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
258          !prefs->GetBoolean(
259              bookmarks::prefs::kShowManagedBookmarksInBookmarkBar));
260      break;
261    }
262
263    case IDC_BOOKMARK_MANAGER: {
264      content::RecordAction(UserMetricsAction("ShowBookmarkManager"));
265      if (selection_.size() != 1)
266        chrome::ShowBookmarkManager(browser_);
267      else if (selection_[0]->is_folder())
268        chrome::ShowBookmarkManagerForNode(browser_, selection_[0]->id());
269      else if (parent_)
270        chrome::ShowBookmarkManagerForNode(browser_, parent_->id());
271      else
272        chrome::ShowBookmarkManager(browser_);
273      break;
274    }
275
276    case IDC_CUT:
277      bookmarks::CopyToClipboard(model_, selection_, true);
278      break;
279
280    case IDC_COPY:
281      bookmarks::CopyToClipboard(model_, selection_, false);
282      break;
283
284    case IDC_PASTE: {
285      int index;
286      const BookmarkNode* paste_target =
287          bookmarks::GetParentForNewNodes(parent_, selection_, &index);
288      if (!paste_target)
289        return;
290
291      bookmarks::PasteFromClipboard(model_, paste_target, index);
292      break;
293    }
294
295    default:
296      NOTREACHED();
297  }
298
299  if (delegate_)
300    delegate_->DidExecuteCommand(id);
301}
302
303bool BookmarkContextMenuController::IsItemForCommandIdDynamic(int command_id)
304    const {
305  return command_id == IDC_BOOKMARK_BAR_UNDO ||
306         command_id == IDC_BOOKMARK_BAR_REDO ||
307         command_id == IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS;
308}
309
310base::string16 BookmarkContextMenuController::GetLabelForCommandId(
311    int command_id) const {
312  if (command_id == IDC_BOOKMARK_BAR_UNDO) {
313    return BookmarkUndoServiceFactory::GetForProfile(profile_)->
314        undo_manager()->GetUndoLabel();
315  }
316  if (command_id == IDC_BOOKMARK_BAR_REDO) {
317    return BookmarkUndoServiceFactory::GetForProfile(profile_)->
318        undo_manager()->GetRedoLabel();
319  }
320  if (command_id == IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS) {
321    ChromeBookmarkClient* client =
322        ChromeBookmarkClientFactory::GetForProfile(profile_);
323    return l10n_util::GetStringFUTF16(IDS_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS,
324                                      client->managed_node()->GetTitle());
325  }
326
327  NOTREACHED();
328  return base::string16();
329}
330
331bool BookmarkContextMenuController::IsCommandIdChecked(int command_id) const {
332  PrefService* prefs = profile_->GetPrefs();
333  if (command_id == IDC_BOOKMARK_BAR_ALWAYS_SHOW)
334    return prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar);
335  if (command_id == IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS)
336    return prefs->GetBoolean(
337        bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
338
339  DCHECK_EQ(IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT, command_id);
340  return prefs->GetBoolean(bookmarks::prefs::kShowAppsShortcutInBookmarkBar);
341}
342
343bool BookmarkContextMenuController::IsCommandIdEnabled(int command_id) const {
344  PrefService* prefs = profile_->GetPrefs();
345
346  bool is_root_node = selection_.size() == 1 &&
347                      selection_[0]->parent() == model_->root_node();
348  bool can_edit = prefs->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled) &&
349                  bookmarks::CanAllBeEditedByUser(model_->client(), selection_);
350  IncognitoModePrefs::Availability incognito_avail =
351      IncognitoModePrefs::GetAvailability(prefs);
352
353  switch (command_id) {
354    case IDC_BOOKMARK_BAR_OPEN_INCOGNITO:
355      return !profile_->IsOffTheRecord() &&
356             incognito_avail != IncognitoModePrefs::DISABLED;
357
358    case IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO:
359      return chrome::HasBookmarkURLsAllowedInIncognitoMode(selection_, profile_)
360             &&
361             !profile_->IsOffTheRecord() &&
362             incognito_avail != IncognitoModePrefs::DISABLED;
363
364    case IDC_BOOKMARK_BAR_OPEN_ALL:
365      return chrome::HasBookmarkURLs(selection_);
366    case IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW:
367      return chrome::HasBookmarkURLs(selection_) &&
368             incognito_avail != IncognitoModePrefs::FORCED;
369
370    case IDC_BOOKMARK_BAR_RENAME_FOLDER:
371    case IDC_BOOKMARK_BAR_EDIT:
372      return selection_.size() == 1 && !is_root_node && can_edit;
373
374    case IDC_BOOKMARK_BAR_UNDO:
375      return can_edit &&
376          BookmarkUndoServiceFactory::GetForProfile(profile_)->
377              undo_manager()->undo_count() > 0;
378
379    case IDC_BOOKMARK_BAR_REDO:
380      return can_edit &&
381          BookmarkUndoServiceFactory::GetForProfile(profile_)->
382              undo_manager()->redo_count() > 0;
383
384    case IDC_BOOKMARK_BAR_REMOVE:
385      return !selection_.empty() && !is_root_node && can_edit;
386
387    case IDC_BOOKMARK_BAR_NEW_FOLDER:
388    case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK:
389      return can_edit && model_->client()->CanBeEditedByUser(parent_) &&
390             bookmarks::GetParentForNewNodes(parent_, selection_, NULL) != NULL;
391
392    case IDC_BOOKMARK_BAR_ALWAYS_SHOW:
393      return !prefs->IsManagedPreference(bookmarks::prefs::kShowBookmarkBar);
394
395    case IDC_BOOKMARK_BAR_SHOW_APPS_SHORTCUT:
396      return !prefs->IsManagedPreference(
397          bookmarks::prefs::kShowAppsShortcutInBookmarkBar);
398
399    case IDC_COPY:
400    case IDC_CUT:
401      return !selection_.empty() && !is_root_node &&
402             (command_id == IDC_COPY || can_edit);
403
404    case IDC_PASTE:
405      // Paste to selection from the Bookmark Bar, to parent_ everywhere else
406      return can_edit &&
407             ((!selection_.empty() &&
408               bookmarks::CanPasteFromClipboard(model_, selection_[0])) ||
409              bookmarks::CanPasteFromClipboard(model_, parent_));
410  }
411  return true;
412}
413
414bool BookmarkContextMenuController::IsCommandIdVisible(int command_id) const {
415  if (command_id == IDC_BOOKMARK_BAR_SHOW_MANAGED_BOOKMARKS) {
416    // The option to hide the Managed Bookmarks folder is only available if
417    // there are any managed bookmarks configured at all.
418    ChromeBookmarkClient* client =
419        ChromeBookmarkClientFactory::GetForProfile(profile_);
420    return !client->managed_node()->empty();
421  }
422
423  return true;
424}
425
426bool BookmarkContextMenuController::GetAcceleratorForCommandId(
427    int command_id,
428    ui::Accelerator* accelerator) {
429  return false;
430}
431
432void BookmarkContextMenuController::BookmarkModelChanged() {
433  if (delegate_)
434    delegate_->CloseMenu();
435}
436