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 using bookmarks::BookmarkModel
;
42 using bookmarks::BookmarkNode
;
46 int num_bookmark_urls_before_prompting
= 15;
50 // The ways in which extensions may customize the bookmark shortcut.
51 enum BookmarkShortcutDisposition
{
52 BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED
,
53 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED
,
54 BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED
57 // Iterator that iterates through a set of BookmarkNodes returning the URLs
58 // for nodes that are urls, or the URLs for the children of non-url urls.
59 // This does not recurse through all descendants, only immediate children.
60 // The following illustrates
62 // OpenURLIterator iterator(nodes);
63 // while (iterator.has_next()) {
64 // const GURL* url = iterator.NextURL();
65 // // do something with |urll|.
67 class OpenURLIterator
{
69 explicit OpenURLIterator(const std::vector
<const BookmarkNode
*>& nodes
)
72 parent_(nodes
.begin()),
77 bool has_next() { return next_
!= NULL
;}
79 const GURL
* NextURL() {
85 const GURL
* next
= next_
;
91 // Seach next node which has URL.
93 for (; parent_
< end_
; ++parent_
, child_index_
= 0) {
94 if ((*parent_
)->is_url()) {
95 next_
= &(*parent_
)->url();
100 for (; child_index_
< (*parent_
)->child_count(); ++child_index_
) {
101 const BookmarkNode
* child
= (*parent_
)->GetChild(child_index_
);
102 if (child
->is_url()) {
103 next_
= &child
->url();
115 std::vector
<const BookmarkNode
*>::const_iterator parent_
;
116 const std::vector
<const BookmarkNode
*>::const_iterator end_
;
118 DISALLOW_COPY_AND_ASSIGN(OpenURLIterator
);
121 bool ShouldOpenAll(gfx::NativeWindow parent
,
122 const std::vector
<const BookmarkNode
*>& nodes
) {
124 OpenURLIterator
iterator(nodes
);
125 while (iterator
.has_next()) {
130 if (child_count
< num_bookmark_urls_before_prompting
)
133 return ShowMessageBox(parent
,
134 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
135 l10n_util::GetStringFUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL
,
136 base::IntToString16(child_count
)),
137 MESSAGE_BOX_TYPE_QUESTION
) == MESSAGE_BOX_RESULT_YES
;
140 // Returns the total number of descendants nodes.
141 int ChildURLCountTotal(const BookmarkNode
* node
) {
143 for (int i
= 0; i
< node
->child_count(); ++i
) {
144 const BookmarkNode
* child
= node
->GetChild(i
);
146 if (child
->is_folder())
147 result
+= ChildURLCountTotal(child
);
152 // Returns in |urls|, the url and title pairs for each open tab in browser.
153 void GetURLsForOpenTabs(Browser
* browser
,
154 std::vector
<std::pair
<GURL
, base::string16
> >* urls
) {
155 for (int i
= 0; i
< browser
->tab_strip_model()->count(); ++i
) {
156 std::pair
<GURL
, base::string16
> entry
;
157 GetURLAndTitleToBookmark(browser
->tab_strip_model()->GetWebContentsAt(i
),
158 &(entry
.first
), &(entry
.second
));
159 urls
->push_back(entry
);
163 // Indicates how the bookmark shortcut has been changed by extensions associated
164 // with |profile|, if at all.
165 BookmarkShortcutDisposition
GetBookmarkShortcutDisposition(Profile
* profile
) {
166 #if defined(ENABLE_EXTENSIONS)
167 extensions::CommandService
* command_service
=
168 extensions::CommandService::Get(profile
);
170 extensions::ExtensionRegistry
* registry
=
171 extensions::ExtensionRegistry::Get(profile
);
173 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED
;
175 const extensions::ExtensionSet
& extension_set
=
176 registry
->enabled_extensions();
178 // This flag tracks whether any extension wants the disposition to be
180 bool removed
= false;
181 for (extensions::ExtensionSet::const_iterator i
= extension_set
.begin();
182 i
!= extension_set
.end();
184 // Use the overridden disposition if any extension wants it.
185 if (command_service
->RequestsBookmarkShortcutOverride(i
->get()))
186 return BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED
;
189 extensions::CommandService::RemovesBookmarkShortcut(i
->get())) {
195 return BOOKMARK_SHORTCUT_DISPOSITION_REMOVED
;
197 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED
;
202 void OpenAll(gfx::NativeWindow parent
,
203 content::PageNavigator
* navigator
,
204 const std::vector
<const BookmarkNode
*>& nodes
,
205 WindowOpenDisposition initial_disposition
,
206 content::BrowserContext
* browser_context
) {
207 if (!ShouldOpenAll(parent
, nodes
))
210 // Opens all |nodes| of type URL and any children of |nodes| that are of type
211 // URL. |navigator| is the PageNavigator used to open URLs. After the first
212 // url is opened |opened_first_url| is set to true and |navigator| is set to
213 // the PageNavigator of the last active tab. This is done to handle a window
214 // disposition of new window, in which case we want subsequent tabs to open in
216 bool opened_first_url
= false;
217 WindowOpenDisposition disposition
= initial_disposition
;
218 OpenURLIterator
iterator(nodes
);
219 while (iterator
.has_next()) {
220 const GURL
* url
= iterator
.NextURL();
221 // When |initial_disposition| is OFF_THE_RECORD, a node which can't be
222 // opened in incognito window, it is detected using |browser_context|, is
224 if (initial_disposition
== OFF_THE_RECORD
&&
225 !IsURLAllowedInIncognito(*url
, browser_context
))
228 content::WebContents
* opened_tab
= navigator
->OpenURL(
229 content::OpenURLParams(*url
, content::Referrer(), disposition
,
230 ui::PAGE_TRANSITION_AUTO_BOOKMARK
, false));
232 if (!opened_first_url
) {
233 opened_first_url
= true;
234 disposition
= NEW_BACKGROUND_TAB
;
235 // We opened the first URL which may have opened a new window or clobbered
236 // the current page, reset the navigator just to be sure. |opened_tab| may
239 navigator
= opened_tab
;
244 void OpenAll(gfx::NativeWindow parent
,
245 content::PageNavigator
* navigator
,
246 const BookmarkNode
* node
,
247 WindowOpenDisposition initial_disposition
,
248 content::BrowserContext
* browser_context
) {
249 std::vector
<const BookmarkNode
*> nodes
;
250 nodes
.push_back(node
);
251 OpenAll(parent
, navigator
, nodes
, initial_disposition
, browser_context
);
254 bool ConfirmDeleteBookmarkNode(const BookmarkNode
* node
,
255 gfx::NativeWindow window
) {
256 DCHECK(node
&& node
->is_folder() && !node
->empty());
257 return ShowMessageBox(window
,
258 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
259 l10n_util::GetStringFUTF16Int(IDS_BOOKMARK_EDITOR_CONFIRM_DELETE
,
260 ChildURLCountTotal(node
)),
261 MESSAGE_BOX_TYPE_QUESTION
) == MESSAGE_BOX_RESULT_YES
;
264 void ShowBookmarkAllTabsDialog(Browser
* browser
) {
265 Profile
* profile
= browser
->profile();
266 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(profile
);
267 DCHECK(model
&& model
->loaded());
269 const BookmarkNode
* parent
= model
->GetParentForNewNodes();
270 BookmarkEditor::EditDetails details
=
271 BookmarkEditor::EditDetails::AddFolder(parent
, parent
->child_count());
272 GetURLsForOpenTabs(browser
, &(details
.urls
));
273 DCHECK(!details
.urls
.empty());
275 BookmarkEditor::Show(browser
->window()->GetNativeWindow(), profile
, details
,
276 BookmarkEditor::SHOW_TREE
);
279 bool HasBookmarkURLs(const std::vector
<const BookmarkNode
*>& selection
) {
280 OpenURLIterator
iterator(selection
);
281 return iterator
.has_next();
284 bool HasBookmarkURLsAllowedInIncognitoMode(
285 const std::vector
<const BookmarkNode
*>& selection
,
286 content::BrowserContext
* browser_context
) {
287 OpenURLIterator
iterator(selection
);
288 while (iterator
.has_next()) {
289 const GURL
* url
= iterator
.NextURL();
290 if (IsURLAllowedInIncognito(*url
, browser_context
))
296 GURL
GetURLToBookmark(content::WebContents
* web_contents
) {
297 DCHECK(web_contents
);
298 return search::IsInstantNTP(web_contents
) ? GURL(kChromeUINewTabURL
)
299 : web_contents
->GetURL();
302 void GetURLAndTitleToBookmark(content::WebContents
* web_contents
,
304 base::string16
* title
) {
305 *url
= GetURLToBookmark(web_contents
);
306 *title
= web_contents
->GetTitle();
309 void ToggleBookmarkBarWhenVisible(content::BrowserContext
* browser_context
) {
310 PrefService
* prefs
= user_prefs::UserPrefs::Get(browser_context
);
311 const bool always_show
=
312 !prefs
->GetBoolean(bookmarks::prefs::kShowBookmarkBar
);
314 // The user changed when the bookmark bar is shown, update the preferences.
315 prefs
->SetBoolean(bookmarks::prefs::kShowBookmarkBar
, always_show
);
318 base::string16
FormatBookmarkURLForDisplay(const GURL
& url
,
319 const PrefService
* prefs
) {
320 std::string languages
;
322 languages
= prefs
->GetString(prefs::kAcceptLanguages
);
324 // Because this gets re-parsed by FixupURL(), it's safe to omit the scheme
325 // and trailing slash, and unescape most characters. However, it's
326 // important not to drop any username/password, or unescape anything that
327 // changes the URL's meaning.
328 return url_formatter::FormatUrl(
329 url
, languages
, url_formatter::kFormatUrlOmitAll
&
330 ~url_formatter::kFormatUrlOmitUsernamePassword
,
331 net::UnescapeRule::SPACES
, nullptr, nullptr, nullptr);
334 bool IsAppsShortcutEnabled(Profile
* profile
,
335 chrome::HostDesktopType host_desktop_type
) {
336 // Legacy supervised users can not have apps installed currently so there's no
337 // need to show the apps shortcut.
338 if (profile
->IsLegacySupervised())
341 // Don't show the apps shortcut in ash since the app launcher is enabled.
342 if (host_desktop_type
== chrome::HOST_DESKTOP_TYPE_ASH
)
345 return search::IsInstantExtendedAPIEnabled() && !profile
->IsOffTheRecord();
348 bool ShouldShowAppsShortcutInBookmarkBar(
350 chrome::HostDesktopType host_desktop_type
) {
351 return IsAppsShortcutEnabled(profile
, host_desktop_type
) &&
352 profile
->GetPrefs()->GetBoolean(
353 bookmarks::prefs::kShowAppsShortcutInBookmarkBar
);
356 bool ShouldRemoveBookmarkThisPageUI(Profile
* profile
) {
357 return GetBookmarkShortcutDisposition(profile
) ==
358 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED
;
361 bool ShouldRemoveBookmarkOpenPagesUI(Profile
* profile
) {
362 #if defined(ENABLE_EXTENSIONS)
363 extensions::ExtensionRegistry
* registry
=
364 extensions::ExtensionRegistry::Get(profile
);
368 const extensions::ExtensionSet
& extension_set
=
369 registry
->enabled_extensions();
371 for (extensions::ExtensionSet::const_iterator i
= extension_set
.begin();
372 i
!= extension_set
.end();
374 if (extensions::CommandService::RemovesBookmarkOpenPagesShortcut(i
->get()))
382 int GetBookmarkDragOperation(content::BrowserContext
* browser_context
,
383 const BookmarkNode
* node
) {
384 PrefService
* prefs
= user_prefs::UserPrefs::Get(browser_context
);
385 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
386 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(profile
);
388 int move
= ui::DragDropTypes::DRAG_MOVE
;
389 if (!prefs
->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled
) ||
390 !model
->client()->CanBeEditedByUser(node
)) {
391 move
= ui::DragDropTypes::DRAG_NONE
;
394 return ui::DragDropTypes::DRAG_COPY
| ui::DragDropTypes::DRAG_LINK
| move
;
395 return ui::DragDropTypes::DRAG_COPY
| move
;
398 int GetPreferredBookmarkDropOperation(int source_operations
, int operations
) {
399 int common_ops
= (source_operations
& operations
);
401 return ui::DragDropTypes::DRAG_NONE
;
402 if (ui::DragDropTypes::DRAG_COPY
& common_ops
)
403 return ui::DragDropTypes::DRAG_COPY
;
404 if (ui::DragDropTypes::DRAG_LINK
& common_ops
)
405 return ui::DragDropTypes::DRAG_LINK
;
406 if (ui::DragDropTypes::DRAG_MOVE
& common_ops
)
407 return ui::DragDropTypes::DRAG_MOVE
;
408 return ui::DragDropTypes::DRAG_NONE
;
411 int GetBookmarkDropOperation(Profile
* profile
,
412 const ui::DropTargetEvent
& event
,
413 const bookmarks::BookmarkNodeData
& data
,
414 const BookmarkNode
* parent
,
416 const base::FilePath
& profile_path
= profile
->GetPath();
418 if (data
.IsFromProfilePath(profile_path
) && data
.size() > 1)
419 // Currently only accept one dragged node at a time.
420 return ui::DragDropTypes::DRAG_NONE
;
422 if (!IsValidBookmarkDropLocation(profile
, data
, parent
, index
))
423 return ui::DragDropTypes::DRAG_NONE
;
425 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(profile
);
426 if (!model
->client()->CanBeEditedByUser(parent
))
427 return ui::DragDropTypes::DRAG_NONE
;
429 const BookmarkNode
* dragged_node
=
430 data
.GetFirstNode(model
, profile
->GetPath());
432 // User is dragging from this profile.
433 if (!model
->client()->CanBeEditedByUser(dragged_node
)) {
434 // Do a copy instead of a move when dragging bookmarks that the user can't
436 return ui::DragDropTypes::DRAG_COPY
;
438 return ui::DragDropTypes::DRAG_MOVE
;
441 // User is dragging from another app, copy.
442 return GetPreferredBookmarkDropOperation(event
.source_operations(),
443 ui::DragDropTypes::DRAG_COPY
| ui::DragDropTypes::DRAG_LINK
);
446 bool IsValidBookmarkDropLocation(Profile
* profile
,
447 const bookmarks::BookmarkNodeData
& data
,
448 const BookmarkNode
* drop_parent
,
450 if (!drop_parent
->is_folder()) {
455 if (!data
.is_valid())
458 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(profile
);
459 if (!model
->client()->CanBeEditedByUser(drop_parent
))
462 const base::FilePath
& profile_path
= profile
->GetPath();
463 if (data
.IsFromProfilePath(profile_path
)) {
464 std::vector
<const BookmarkNode
*> nodes
= data
.GetNodes(model
, profile_path
);
465 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
466 // Don't allow the drop if the user is attempting to drop on one of the
467 // nodes being dragged.
468 const BookmarkNode
* node
= nodes
[i
];
469 int node_index
= (drop_parent
== node
->parent()) ?
470 drop_parent
->GetIndexOf(nodes
[i
]) : -1;
471 if (node_index
!= -1 && (index
== node_index
|| index
== node_index
+ 1))
474 // drop_parent can't accept a child that is an ancestor.
475 if (drop_parent
->HasAncestor(node
))
480 // From another profile, always accept.
484 } // namespace chrome