ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / ui / bookmarks / bookmark_utils.cc
blob9d7fca293c94720ba513c3e37fe714354fe00438
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 using bookmarks::BookmarkModel;
42 using bookmarks::BookmarkNode;
44 namespace chrome {
46 int num_bookmark_urls_before_prompting = 15;
48 namespace {
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
61 // typical usage:
62 // OpenURLIterator iterator(nodes);
63 // while (iterator.has_next()) {
64 // const GURL* url = iterator.NextURL();
65 // // do something with |urll|.
66 // }
67 class OpenURLIterator {
68 public:
69 explicit OpenURLIterator(const std::vector<const BookmarkNode*>& nodes)
70 : child_index_(0),
71 next_(NULL),
72 parent_(nodes.begin()),
73 end_(nodes.end()) {
74 FindNext();
77 bool has_next() { return next_ != NULL;}
79 const GURL* NextURL() {
80 if (!has_next()) {
81 NOTREACHED();
82 return NULL;
85 const GURL* next = next_;
86 FindNext();
87 return next;
90 private:
91 // Seach next node which has URL.
92 void FindNext() {
93 for (; parent_ < end_; ++parent_, child_index_ = 0) {
94 if ((*parent_)->is_url()) {
95 next_ = &(*parent_)->url();
96 ++parent_;
97 child_index_ = 0;
98 return;
99 } else {
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();
104 ++child_index_;
105 return;
110 next_ = NULL;
113 int child_index_;
114 const GURL* next_;
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) {
123 int child_count = 0;
124 OpenURLIterator iterator(nodes);
125 while (iterator.has_next()) {
126 iterator.NextURL();
127 child_count++;
130 if (child_count < num_bookmark_urls_before_prompting)
131 return true;
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) {
142 int result = 0;
143 for (int i = 0; i < node->child_count(); ++i) {
144 const BookmarkNode* child = node->GetChild(i);
145 result++;
146 if (child->is_folder())
147 result += ChildURLCountTotal(child);
149 return result;
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);
172 if (!registry)
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
179 // removed.
180 bool removed = false;
181 for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
182 i != extension_set.end();
183 ++i) {
184 // Use the overridden disposition if any extension wants it.
185 if (command_service->RequestsBookmarkShortcutOverride(i->get()))
186 return BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDE_REQUESTED;
188 if (!removed &&
189 extensions::CommandService::RemovesBookmarkShortcut(i->get())) {
190 removed = true;
194 if (removed)
195 return BOOKMARK_SHORTCUT_DISPOSITION_REMOVED;
196 #endif
197 return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
200 } // namespace
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))
208 return;
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
215 // that window.
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
223 // not opened.
224 if (initial_disposition == OFF_THE_RECORD &&
225 !IsURLAllowedInIncognito(*url, browser_context))
226 continue;
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
237 // be NULL in tests.
238 if (opened_tab)
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))
291 return true;
293 return false;
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,
303 GURL* url,
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;
321 if (prefs)
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())
339 return false;
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)
343 return false;
345 return search::IsInstantExtendedAPIEnabled() && !profile->IsOffTheRecord();
348 bool ShouldShowAppsShortcutInBookmarkBar(
349 Profile* profile,
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);
365 if (!registry)
366 return false;
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();
373 ++i) {
374 if (extensions::CommandService::RemovesBookmarkOpenPagesShortcut(i->get()))
375 return true;
377 #endif
379 return false;
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;
393 if (node->is_url())
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);
400 if (!common_ops)
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,
415 int index) {
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());
431 if (dragged_node) {
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
435 // modify.
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,
449 int index) {
450 if (!drop_parent->is_folder()) {
451 NOTREACHED();
452 return false;
455 if (!data.is_valid())
456 return false;
458 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
459 if (!model->client()->CanBeEditedByUser(drop_parent))
460 return false;
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))
472 return false;
474 // drop_parent can't accept a child that is an ancestor.
475 if (drop_parent->HasAncestor(node))
476 return false;
478 return true;
480 // From another profile, always accept.
481 return true;
484 } // namespace chrome