Add ability to gather metrics to BubbleManager.
[chromium-blink-merge.git] / chrome / browser / ui / bookmarks / bookmark_utils.cc
blobe178b9f2f294f0634022c3be3ef56d20d2cc4a8d
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.
5 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/search/search.h"
14 #include "chrome/browser/ui/app_list/app_list_util.h"
15 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_navigator.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/simple_message_box.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/grit/chromium_strings.h"
24 #include "chrome/grit/generated_resources.h"
25 #include "components/bookmarks/browser/bookmark_model.h"
26 #include "components/bookmarks/browser/bookmark_node_data.h"
27 #include "components/search/search.h"
28 #include "components/url_formatter/url_formatter.h"
29 #include "components/user_prefs/user_prefs.h"
30 #include "content/public/browser/web_contents.h"
31 #include "ui/base/dragdrop/drag_drop_types.h"
32 #include "ui/base/dragdrop/drop_target_event.h"
33 #include "ui/base/l10n/l10n_util.h"
35 #if defined(ENABLE_EXTENSIONS)
36 #include "chrome/browser/extensions/api/commands/command_service.h"
37 #include "extensions/browser/extension_registry.h"
38 #include "extensions/common/extension_set.h"
39 #endif
41 #if defined(TOOLKIT_VIEWS)
42 #include "ui/gfx/paint_vector_icon.h"
43 #include "ui/gfx/vector_icons_public.h"
44 #include "ui/native_theme/common_theme.h"
45 #include "ui/native_theme/native_theme.h"
46 #endif
48 #if defined(OS_WIN)
49 #include "chrome/grit/theme_resources.h"
50 #include "ui/base/resource/material_design/material_design_controller.h"
51 #include "ui/base/resource/resource_bundle.h"
52 #endif
54 using bookmarks::BookmarkModel;
55 using bookmarks::BookmarkNode;
57 namespace chrome {
59 int num_bookmark_urls_before_prompting = 15;
61 namespace {
63 // The ways in which extensions may customize the bookmark shortcut.
64 enum BookmarkShortcutDisposition {
65 BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED,
66 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED,
67 BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED
70 // Iterator that iterates through a set of BookmarkNodes returning the URLs
71 // for nodes that are urls, or the URLs for the children of non-url urls.
72 // This does not recurse through all descendants, only immediate children.
73 // The following illustrates
74 // typical usage:
75 // OpenURLIterator iterator(nodes);
76 // while (iterator.has_next()) {
77 // const GURL* url = iterator.NextURL();
78 // // do something with |urll|.
79 // }
80 class OpenURLIterator {
81 public:
82 explicit OpenURLIterator(const std::vector<const BookmarkNode*>& nodes)
83 : child_index_(0),
84 next_(NULL),
85 parent_(nodes.begin()),
86 end_(nodes.end()) {
87 FindNext();
90 bool has_next() { return next_ != NULL;}
92 const GURL* NextURL() {
93 if (!has_next()) {
94 NOTREACHED();
95 return NULL;
98 const GURL* next = next_;
99 FindNext();
100 return next;
103 private:
104 // Seach next node which has URL.
105 void FindNext() {
106 for (; parent_ < end_; ++parent_, child_index_ = 0) {
107 if ((*parent_)->is_url()) {
108 next_ = &(*parent_)->url();
109 ++parent_;
110 child_index_ = 0;
111 return;
112 } else {
113 for (; child_index_ < (*parent_)->child_count(); ++child_index_) {
114 const BookmarkNode* child = (*parent_)->GetChild(child_index_);
115 if (child->is_url()) {
116 next_ = &child->url();
117 ++child_index_;
118 return;
123 next_ = NULL;
126 int child_index_;
127 const GURL* next_;
128 std::vector<const BookmarkNode*>::const_iterator parent_;
129 const std::vector<const BookmarkNode*>::const_iterator end_;
131 DISALLOW_COPY_AND_ASSIGN(OpenURLIterator);
134 bool ShouldOpenAll(gfx::NativeWindow parent,
135 const std::vector<const BookmarkNode*>& nodes) {
136 int child_count = 0;
137 OpenURLIterator iterator(nodes);
138 while (iterator.has_next()) {
139 iterator.NextURL();
140 child_count++;
143 if (child_count < num_bookmark_urls_before_prompting)
144 return true;
146 return ShowMessageBox(parent,
147 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
148 l10n_util::GetStringFUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
149 base::IntToString16(child_count)),
150 MESSAGE_BOX_TYPE_QUESTION) == MESSAGE_BOX_RESULT_YES;
153 // Returns the total number of descendants nodes.
154 int ChildURLCountTotal(const BookmarkNode* node) {
155 int result = 0;
156 for (int i = 0; i < node->child_count(); ++i) {
157 const BookmarkNode* child = node->GetChild(i);
158 result++;
159 if (child->is_folder())
160 result += ChildURLCountTotal(child);
162 return result;
165 // Returns in |urls|, the url and title pairs for each open tab in browser.
166 void GetURLsForOpenTabs(Browser* browser,
167 std::vector<std::pair<GURL, base::string16> >* urls) {
168 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
169 std::pair<GURL, base::string16> entry;
170 GetURLAndTitleToBookmark(browser->tab_strip_model()->GetWebContentsAt(i),
171 &(entry.first), &(entry.second));
172 urls->push_back(entry);
176 // Indicates how the bookmark shortcut has been changed by extensions associated
177 // with |profile|, if at all.
178 BookmarkShortcutDisposition GetBookmarkShortcutDisposition(Profile* profile) {
179 #if defined(ENABLE_EXTENSIONS)
180 extensions::CommandService* command_service =
181 extensions::CommandService::Get(profile);
183 extensions::ExtensionRegistry* registry =
184 extensions::ExtensionRegistry::Get(profile);
185 if (!registry)
186 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
188 const extensions::ExtensionSet& extension_set =
189 registry->enabled_extensions();
191 // This flag tracks whether any extension wants the disposition to be
192 // removed.
193 bool removed = false;
194 for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
195 i != extension_set.end();
196 ++i) {
197 // Use the overridden disposition if any extension wants it.
198 if (command_service->RequestsBookmarkShortcutOverride(i->get()))
199 return BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED;
201 if (!removed &&
202 extensions::CommandService::RemovesBookmarkShortcut(i->get())) {
203 removed = true;
207 if (removed)
208 return BOOKMARK_SHORTCUT_DISPOSITION_REMOVED;
209 #endif
210 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
213 #if defined(TOOLKIT_VIEWS)
214 gfx::ImageSkia GetFolderIcon(gfx::VectorIconId id) {
215 SkColor icon_color;
216 ui::CommonThemeGetSystemColor(ui::NativeTheme::kColorId_ChromeIconGrey,
217 &icon_color);
218 return gfx::CreateVectorIcon(id, 16, icon_color);
220 #endif
222 } // namespace
224 void OpenAll(gfx::NativeWindow parent,
225 content::PageNavigator* navigator,
226 const std::vector<const BookmarkNode*>& nodes,
227 WindowOpenDisposition initial_disposition,
228 content::BrowserContext* browser_context) {
229 if (!ShouldOpenAll(parent, nodes))
230 return;
232 // Opens all |nodes| of type URL and any children of |nodes| that are of type
233 // URL. |navigator| is the PageNavigator used to open URLs. After the first
234 // url is opened |opened_first_url| is set to true and |navigator| is set to
235 // the PageNavigator of the last active tab. This is done to handle a window
236 // disposition of new window, in which case we want subsequent tabs to open in
237 // that window.
238 bool opened_first_url = false;
239 WindowOpenDisposition disposition = initial_disposition;
240 OpenURLIterator iterator(nodes);
241 while (iterator.has_next()) {
242 const GURL* url = iterator.NextURL();
243 // When |initial_disposition| is OFF_THE_RECORD, a node which can't be
244 // opened in incognito window, it is detected using |browser_context|, is
245 // not opened.
246 if (initial_disposition == OFF_THE_RECORD &&
247 !IsURLAllowedInIncognito(*url, browser_context))
248 continue;
250 content::WebContents* opened_tab = navigator->OpenURL(
251 content::OpenURLParams(*url, content::Referrer(), disposition,
252 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
254 if (!opened_first_url) {
255 opened_first_url = true;
256 disposition = NEW_BACKGROUND_TAB;
257 // We opened the first URL which may have opened a new window or clobbered
258 // the current page, reset the navigator just to be sure. |opened_tab| may
259 // be NULL in tests.
260 if (opened_tab)
261 navigator = opened_tab;
266 void OpenAll(gfx::NativeWindow parent,
267 content::PageNavigator* navigator,
268 const BookmarkNode* node,
269 WindowOpenDisposition initial_disposition,
270 content::BrowserContext* browser_context) {
271 std::vector<const BookmarkNode*> nodes;
272 nodes.push_back(node);
273 OpenAll(parent, navigator, nodes, initial_disposition, browser_context);
276 bool ConfirmDeleteBookmarkNode(const BookmarkNode* node,
277 gfx::NativeWindow window) {
278 DCHECK(node && node->is_folder() && !node->empty());
279 return ShowMessageBox(window,
280 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
281 l10n_util::GetStringFUTF16Int(IDS_BOOKMARK_EDITOR_CONFIRM_DELETE,
282 ChildURLCountTotal(node)),
283 MESSAGE_BOX_TYPE_QUESTION) == MESSAGE_BOX_RESULT_YES;
286 void ShowBookmarkAllTabsDialog(Browser* browser) {
287 Profile* profile = browser->profile();
288 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
289 DCHECK(model && model->loaded());
291 const BookmarkNode* parent = model->GetParentForNewNodes();
292 BookmarkEditor::EditDetails details =
293 BookmarkEditor::EditDetails::AddFolder(parent, parent->child_count());
294 GetURLsForOpenTabs(browser, &(details.urls));
295 DCHECK(!details.urls.empty());
297 BookmarkEditor::Show(browser->window()->GetNativeWindow(), profile, details,
298 BookmarkEditor::SHOW_TREE);
301 bool HasBookmarkURLs(const std::vector<const BookmarkNode*>& selection) {
302 OpenURLIterator iterator(selection);
303 return iterator.has_next();
306 bool HasBookmarkURLsAllowedInIncognitoMode(
307 const std::vector<const BookmarkNode*>& selection,
308 content::BrowserContext* browser_context) {
309 OpenURLIterator iterator(selection);
310 while (iterator.has_next()) {
311 const GURL* url = iterator.NextURL();
312 if (IsURLAllowedInIncognito(*url, browser_context))
313 return true;
315 return false;
318 GURL GetURLToBookmark(content::WebContents* web_contents) {
319 DCHECK(web_contents);
320 return search::IsInstantNTP(web_contents) ? GURL(kChromeUINewTabURL)
321 : web_contents->GetURL();
324 void GetURLAndTitleToBookmark(content::WebContents* web_contents,
325 GURL* url,
326 base::string16* title) {
327 *url = GetURLToBookmark(web_contents);
328 *title = web_contents->GetTitle();
331 void ToggleBookmarkBarWhenVisible(content::BrowserContext* browser_context) {
332 PrefService* prefs = user_prefs::UserPrefs::Get(browser_context);
333 const bool always_show =
334 !prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar);
336 // The user changed when the bookmark bar is shown, update the preferences.
337 prefs->SetBoolean(bookmarks::prefs::kShowBookmarkBar, always_show);
340 base::string16 FormatBookmarkURLForDisplay(const GURL& url,
341 const PrefService* prefs) {
342 std::string languages;
343 if (prefs)
344 languages = prefs->GetString(prefs::kAcceptLanguages);
346 // Because this gets re-parsed by FixupURL(), it's safe to omit the scheme
347 // and trailing slash, and unescape most characters. However, it's
348 // important not to drop any username/password, or unescape anything that
349 // changes the URL's meaning.
350 return url_formatter::FormatUrl(
351 url, languages, url_formatter::kFormatUrlOmitAll &
352 ~url_formatter::kFormatUrlOmitUsernamePassword,
353 net::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
356 bool IsAppsShortcutEnabled(Profile* profile,
357 chrome::HostDesktopType host_desktop_type) {
358 // Legacy supervised users can not have apps installed currently so there's no
359 // need to show the apps shortcut.
360 if (profile->IsLegacySupervised())
361 return false;
363 // Don't show the apps shortcut in ash since the app launcher is enabled.
364 if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
365 return false;
367 return search::IsInstantExtendedAPIEnabled() && !profile->IsOffTheRecord();
370 bool ShouldShowAppsShortcutInBookmarkBar(
371 Profile* profile,
372 chrome::HostDesktopType host_desktop_type) {
373 return IsAppsShortcutEnabled(profile, host_desktop_type) &&
374 profile->GetPrefs()->GetBoolean(
375 bookmarks::prefs::kShowAppsShortcutInBookmarkBar);
378 bool ShouldRemoveBookmarkThisPageUI(Profile* profile) {
379 return GetBookmarkShortcutDisposition(profile) ==
380 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED;
383 bool ShouldRemoveBookmarkOpenPagesUI(Profile* profile) {
384 #if defined(ENABLE_EXTENSIONS)
385 extensions::ExtensionRegistry* registry =
386 extensions::ExtensionRegistry::Get(profile);
387 if (!registry)
388 return false;
390 const extensions::ExtensionSet& extension_set =
391 registry->enabled_extensions();
393 for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
394 i != extension_set.end();
395 ++i) {
396 if (extensions::CommandService::RemovesBookmarkOpenPagesShortcut(i->get()))
397 return true;
399 #endif
401 return false;
404 int GetBookmarkDragOperation(content::BrowserContext* browser_context,
405 const BookmarkNode* node) {
406 PrefService* prefs = user_prefs::UserPrefs::Get(browser_context);
407 Profile* profile = Profile::FromBrowserContext(browser_context);
408 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
410 int move = ui::DragDropTypes::DRAG_MOVE;
411 if (!prefs->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled) ||
412 !model->client()->CanBeEditedByUser(node)) {
413 move = ui::DragDropTypes::DRAG_NONE;
415 if (node->is_url())
416 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK | move;
417 return ui::DragDropTypes::DRAG_COPY | move;
420 int GetPreferredBookmarkDropOperation(int source_operations, int operations) {
421 int common_ops = (source_operations & operations);
422 if (!common_ops)
423 return ui::DragDropTypes::DRAG_NONE;
424 if (ui::DragDropTypes::DRAG_COPY & common_ops)
425 return ui::DragDropTypes::DRAG_COPY;
426 if (ui::DragDropTypes::DRAG_LINK & common_ops)
427 return ui::DragDropTypes::DRAG_LINK;
428 if (ui::DragDropTypes::DRAG_MOVE & common_ops)
429 return ui::DragDropTypes::DRAG_MOVE;
430 return ui::DragDropTypes::DRAG_NONE;
433 int GetBookmarkDropOperation(Profile* profile,
434 const ui::DropTargetEvent& event,
435 const bookmarks::BookmarkNodeData& data,
436 const BookmarkNode* parent,
437 int index) {
438 const base::FilePath& profile_path = profile->GetPath();
440 if (data.IsFromProfilePath(profile_path) && data.size() > 1)
441 // Currently only accept one dragged node at a time.
442 return ui::DragDropTypes::DRAG_NONE;
444 if (!IsValidBookmarkDropLocation(profile, data, parent, index))
445 return ui::DragDropTypes::DRAG_NONE;
447 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
448 if (!model->client()->CanBeEditedByUser(parent))
449 return ui::DragDropTypes::DRAG_NONE;
451 const BookmarkNode* dragged_node =
452 data.GetFirstNode(model, profile->GetPath());
453 if (dragged_node) {
454 // User is dragging from this profile.
455 if (!model->client()->CanBeEditedByUser(dragged_node)) {
456 // Do a copy instead of a move when dragging bookmarks that the user can't
457 // modify.
458 return ui::DragDropTypes::DRAG_COPY;
460 return ui::DragDropTypes::DRAG_MOVE;
463 // User is dragging from another app, copy.
464 return GetPreferredBookmarkDropOperation(event.source_operations(),
465 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
468 bool IsValidBookmarkDropLocation(Profile* profile,
469 const bookmarks::BookmarkNodeData& data,
470 const BookmarkNode* drop_parent,
471 int index) {
472 if (!drop_parent->is_folder()) {
473 NOTREACHED();
474 return false;
477 if (!data.is_valid())
478 return false;
480 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
481 if (!model->client()->CanBeEditedByUser(drop_parent))
482 return false;
484 const base::FilePath& profile_path = profile->GetPath();
485 if (data.IsFromProfilePath(profile_path)) {
486 std::vector<const BookmarkNode*> nodes = data.GetNodes(model, profile_path);
487 for (size_t i = 0; i < nodes.size(); ++i) {
488 // Don't allow the drop if the user is attempting to drop on one of the
489 // nodes being dragged.
490 const BookmarkNode* node = nodes[i];
491 int node_index = (drop_parent == node->parent()) ?
492 drop_parent->GetIndexOf(nodes[i]) : -1;
493 if (node_index != -1 && (index == node_index || index == node_index + 1))
494 return false;
496 // drop_parent can't accept a child that is an ancestor.
497 if (drop_parent->HasAncestor(node))
498 return false;
500 return true;
502 // From another profile, always accept.
503 return true;
506 #if defined(TOOLKIT_VIEWS)
507 gfx::ImageSkia GetBookmarkFolderIcon() {
508 #if defined(OS_WIN)
509 if (!ui::MaterialDesignController::IsModeMaterial()) {
510 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
511 IDR_BOOKMARK_BAR_FOLDER);
513 #endif
515 return GetFolderIcon(gfx::VectorIconId::FOLDER);
518 gfx::ImageSkia GetBookmarkSupervisedFolderIcon() {
519 #if defined(OS_WIN)
520 if (!ui::MaterialDesignController::IsModeMaterial()) {
521 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
522 IDR_BOOKMARK_BAR_FOLDER_SUPERVISED);
524 #endif
526 return GetFolderIcon(gfx::VectorIconId::FOLDER_SUPERVISED);
529 gfx::ImageSkia GetBookmarkManagedFolderIcon() {
530 #if defined(OS_WIN)
531 if (!ui::MaterialDesignController::IsModeMaterial()) {
532 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
533 IDR_BOOKMARK_BAR_FOLDER_MANAGED);
535 #endif
537 return GetFolderIcon(gfx::VectorIconId::FOLDER_MANAGED);
539 #endif
541 } // namespace chrome