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"
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"
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"
54 using bookmarks::BookmarkModel
;
55 using bookmarks::BookmarkNode
;
59 int num_bookmark_urls_before_prompting
= 15;
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
75 // OpenURLIterator iterator(nodes);
76 // while (iterator.has_next()) {
77 // const GURL* url = iterator.NextURL();
78 // // do something with |urll|.
80 class OpenURLIterator
{
82 explicit OpenURLIterator(const std::vector
<const BookmarkNode
*>& nodes
)
85 parent_(nodes
.begin()),
90 bool has_next() { return next_
!= NULL
;}
92 const GURL
* NextURL() {
98 const GURL
* next
= next_
;
104 // Seach next node which has URL.
106 for (; parent_
< end_
; ++parent_
, child_index_
= 0) {
107 if ((*parent_
)->is_url()) {
108 next_
= &(*parent_
)->url();
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();
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
) {
137 OpenURLIterator
iterator(nodes
);
138 while (iterator
.has_next()) {
143 if (child_count
< num_bookmark_urls_before_prompting
)
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
) {
156 for (int i
= 0; i
< node
->child_count(); ++i
) {
157 const BookmarkNode
* child
= node
->GetChild(i
);
159 if (child
->is_folder())
160 result
+= ChildURLCountTotal(child
);
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
);
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
193 bool removed
= false;
194 for (extensions::ExtensionSet::const_iterator i
= extension_set
.begin();
195 i
!= extension_set
.end();
197 // Use the overridden disposition if any extension wants it.
198 if (command_service
->RequestsBookmarkShortcutOverride(i
->get()))
199 return BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED
;
202 extensions::CommandService::RemovesBookmarkShortcut(i
->get())) {
208 return BOOKMARK_SHORTCUT_DISPOSITION_REMOVED
;
210 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED
;
213 #if defined(TOOLKIT_VIEWS)
214 gfx::ImageSkia
GetFolderIcon(gfx::VectorIconId id
) {
216 ui::CommonThemeGetSystemColor(ui::NativeTheme::kColorId_ChromeIconGrey
,
218 return gfx::CreateVectorIcon(id
, 16, icon_color
);
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
))
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
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
246 if (initial_disposition
== OFF_THE_RECORD
&&
247 !IsURLAllowedInIncognito(*url
, browser_context
))
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
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
))
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
,
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
;
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())
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
)
367 return search::IsInstantExtendedAPIEnabled() && !profile
->IsOffTheRecord();
370 bool ShouldShowAppsShortcutInBookmarkBar(
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
);
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();
396 if (extensions::CommandService::RemovesBookmarkOpenPagesShortcut(i
->get()))
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
;
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
);
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
,
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());
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
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
,
472 if (!drop_parent
->is_folder()) {
477 if (!data
.is_valid())
480 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(profile
);
481 if (!model
->client()->CanBeEditedByUser(drop_parent
))
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))
496 // drop_parent can't accept a child that is an ancestor.
497 if (drop_parent
->HasAncestor(node
))
502 // From another profile, always accept.
506 #if defined(TOOLKIT_VIEWS)
507 gfx::ImageSkia
GetBookmarkFolderIcon() {
509 if (!ui::MaterialDesignController::IsModeMaterial()) {
510 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
511 IDR_BOOKMARK_BAR_FOLDER
);
515 return GetFolderIcon(gfx::VectorIconId::FOLDER
);
518 gfx::ImageSkia
GetBookmarkSupervisedFolderIcon() {
520 if (!ui::MaterialDesignController::IsModeMaterial()) {
521 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
522 IDR_BOOKMARK_BAR_FOLDER_SUPERVISED
);
526 return GetFolderIcon(gfx::VectorIconId::FOLDER_SUPERVISED
);
529 gfx::ImageSkia
GetBookmarkManagedFolderIcon() {
531 if (!ui::MaterialDesignController::IsModeMaterial()) {
532 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
533 IDR_BOOKMARK_BAR_FOLDER_MANAGED
);
537 return GetFolderIcon(gfx::VectorIconId::FOLDER_MANAGED
);
541 } // namespace chrome