bookmark_utils.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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_utils.h"
6
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.h"
12#include "chrome/browser/bookmarks/bookmark_model_factory.h"
13#include "chrome/browser/extensions/api/commands/command_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/search/search.h"
16#include "chrome/browser/ui/app_list/app_list_util.h"
17#include "chrome/browser/ui/bookmarks/bookmark_editor.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_navigator.h"
20#include "chrome/browser/ui/browser_window.h"
21#include "chrome/browser/ui/simple_message_box.h"
22#include "chrome/browser/ui/tabs/tab_strip_model.h"
23#include "chrome/common/pref_names.h"
24#include "chrome/common/url_constants.h"
25#include "components/user_prefs/user_prefs.h"
26#include "content/public/browser/web_contents.h"
27#include "extensions/browser/extension_registry.h"
28#include "extensions/common/extension_set.h"
29#include "grit/chromium_strings.h"
30#include "grit/generated_resources.h"
31#include "net/base/net_util.h"
32#include "ui/base/l10n/l10n_util.h"
33
34namespace chrome {
35
36int num_bookmark_urls_before_prompting = 15;
37
38namespace {
39
40// The ways in which extensions may customize the bookmark shortcut.
41enum BookmarkShortcutDisposition {
42  BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED,
43  BOOKMARK_SHORTCUT_DISPOSITION_REMOVED,
44  BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDDEN
45};
46
47// Iterator that iterates through a set of BookmarkNodes returning the URLs
48// for nodes that are urls, or the URLs for the children of non-url urls.
49// This does not recurse through all descendants, only immediate children.
50// The following illustrates
51// typical usage:
52// OpenURLIterator iterator(nodes);
53// while (iterator.has_next()) {
54//   const GURL* url = iterator.NextURL();
55//   // do something with |urll|.
56// }
57class OpenURLIterator {
58 public:
59  explicit OpenURLIterator(const std::vector<const BookmarkNode*>& nodes)
60      : child_index_(0),
61        next_(NULL),
62        parent_(nodes.begin()),
63        end_(nodes.end()) {
64    FindNext();
65  }
66
67  bool has_next() { return next_ != NULL;}
68
69  const GURL* NextURL() {
70    if (!has_next()) {
71      NOTREACHED();
72      return NULL;
73    }
74
75    const GURL* next = next_;
76    FindNext();
77    return next;
78  }
79
80 private:
81  // Seach next node which has URL.
82  void FindNext() {
83    for (; parent_ < end_; ++parent_, child_index_ = 0) {
84      if ((*parent_)->is_url()) {
85        next_ = &(*parent_)->url();
86        ++parent_;
87        child_index_ = 0;
88        return;
89      } else {
90        for (; child_index_ < (*parent_)->child_count(); ++child_index_) {
91          const BookmarkNode* child = (*parent_)->GetChild(child_index_);
92          if (child->is_url()) {
93            next_ = &child->url();
94            ++child_index_;
95            return;
96          }
97        }
98      }
99    }
100    next_ = NULL;
101  }
102
103  int child_index_;
104  const GURL* next_;
105  std::vector<const BookmarkNode*>::const_iterator parent_;
106  const std::vector<const BookmarkNode*>::const_iterator end_;
107
108  DISALLOW_COPY_AND_ASSIGN(OpenURLIterator);
109};
110
111bool ShouldOpenAll(gfx::NativeWindow parent,
112                   const std::vector<const BookmarkNode*>& nodes) {
113  int child_count = 0;
114  OpenURLIterator iterator(nodes);
115  while (iterator.has_next()) {
116    iterator.NextURL();
117    child_count++;
118  }
119
120  if (child_count < num_bookmark_urls_before_prompting)
121    return true;
122
123  return ShowMessageBox(parent,
124      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
125      l10n_util::GetStringFUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
126                                 base::IntToString16(child_count)),
127      MESSAGE_BOX_TYPE_QUESTION) == MESSAGE_BOX_RESULT_YES;
128}
129
130// Returns the total number of descendants nodes.
131int ChildURLCountTotal(const BookmarkNode* node) {
132  int result = 0;
133  for (int i = 0; i < node->child_count(); ++i) {
134    const BookmarkNode* child = node->GetChild(i);
135    result++;
136    if (child->is_folder())
137      result += ChildURLCountTotal(child);
138  }
139  return result;
140}
141
142// Returns in |urls|, the url and title pairs for each open tab in browser.
143void GetURLsForOpenTabs(Browser* browser,
144                        std::vector<std::pair<GURL, base::string16> >* urls) {
145  for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
146    std::pair<GURL, base::string16> entry;
147    GetURLAndTitleToBookmark(browser->tab_strip_model()->GetWebContentsAt(i),
148                             &(entry.first), &(entry.second));
149    urls->push_back(entry);
150  }
151}
152
153// Indicates how the bookmark shortcut has been changed by extensions associated
154// with |profile|, if at all.
155BookmarkShortcutDisposition GetBookmarkShortcutDisposition(Profile* profile) {
156  extensions::CommandService* command_service =
157      extensions::CommandService::Get(profile);
158
159  extensions::ExtensionRegistry* registry =
160      extensions::ExtensionRegistry::Get(profile);
161  if (!registry)
162    return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
163
164  const extensions::ExtensionSet& extension_set =
165      registry->enabled_extensions();
166
167  // This flag tracks whether any extension wants the disposition to be
168  // removed.
169  bool removed = false;
170  for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
171       i != extension_set.end();
172       ++i) {
173    // Use the overridden disposition if any extension wants it.
174    if (command_service->OverridesBookmarkShortcut(*i))
175      return BOOKMARK_SHORTCUT_DISPOSITION_OVERRIDDEN;
176
177    if (!removed && extensions::CommandService::RemovesBookmarkShortcut(*i))
178      removed = true;
179  }
180
181  if (removed)
182    return BOOKMARK_SHORTCUT_DISPOSITION_REMOVED;
183  return BOOKMARK_SHORTCUT_DISPOSITION_UNCHANGED;
184}
185
186}  // namespace
187
188void OpenAll(gfx::NativeWindow parent,
189             content::PageNavigator* navigator,
190             const std::vector<const BookmarkNode*>& nodes,
191             WindowOpenDisposition initial_disposition,
192             content::BrowserContext* browser_context) {
193  if (!ShouldOpenAll(parent, nodes))
194    return;
195
196  // Opens all |nodes| of type URL and any children of |nodes| that are of type
197  // URL. |navigator| is the PageNavigator used to open URLs. After the first
198  // url is opened |opened_first_url| is set to true and |navigator| is set to
199  // the PageNavigator of the last active tab. This is done to handle a window
200  // disposition of new window, in which case we want subsequent tabs to open in
201  // that window.
202  bool opened_first_url = false;
203  WindowOpenDisposition disposition = initial_disposition;
204  OpenURLIterator iterator(nodes);
205  while (iterator.has_next()) {
206    const GURL* url = iterator.NextURL();
207    // When |initial_disposition| is OFF_THE_RECORD, a node which can't be
208    // opened in incognito window, it is detected using |browser_context|, is
209    // not opened.
210    if (initial_disposition == OFF_THE_RECORD &&
211        !IsURLAllowedInIncognito(*url, browser_context))
212      continue;
213
214    content::WebContents* opened_tab = navigator->OpenURL(
215        content::OpenURLParams(*url, content::Referrer(), disposition,
216                               content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
217
218    if (!opened_first_url) {
219      opened_first_url = true;
220      disposition = NEW_BACKGROUND_TAB;
221      // We opened the first URL which may have opened a new window or clobbered
222      // the current page, reset the navigator just to be sure. |opened_tab| may
223      // be NULL in tests.
224      if (opened_tab)
225        navigator = opened_tab;
226    }
227  }
228}
229
230void OpenAll(gfx::NativeWindow parent,
231             content::PageNavigator* navigator,
232             const BookmarkNode* node,
233             WindowOpenDisposition initial_disposition,
234             content::BrowserContext* browser_context) {
235  std::vector<const BookmarkNode*> nodes;
236  nodes.push_back(node);
237  OpenAll(parent, navigator, nodes, initial_disposition, browser_context);
238}
239
240bool ConfirmDeleteBookmarkNode(const BookmarkNode* node,
241                               gfx::NativeWindow window) {
242  DCHECK(node && node->is_folder() && !node->empty());
243  return ShowMessageBox(window,
244      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
245      l10n_util::GetStringFUTF16Int(IDS_BOOKMARK_EDITOR_CONFIRM_DELETE,
246                                    ChildURLCountTotal(node)),
247      MESSAGE_BOX_TYPE_QUESTION) == MESSAGE_BOX_RESULT_YES;
248}
249
250void ShowBookmarkAllTabsDialog(Browser* browser) {
251  Profile* profile = browser->profile();
252  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
253  DCHECK(model && model->loaded());
254
255  const BookmarkNode* parent = model->GetParentForNewNodes();
256  BookmarkEditor::EditDetails details =
257      BookmarkEditor::EditDetails::AddFolder(parent, parent->child_count());
258  GetURLsForOpenTabs(browser, &(details.urls));
259  DCHECK(!details.urls.empty());
260
261  BookmarkEditor::Show(browser->window()->GetNativeWindow(), profile, details,
262                       BookmarkEditor::SHOW_TREE);
263}
264
265bool HasBookmarkURLs(const std::vector<const BookmarkNode*>& selection) {
266  OpenURLIterator iterator(selection);
267  return iterator.has_next();
268}
269
270bool HasBookmarkURLsAllowedInIncognitoMode(
271    const std::vector<const BookmarkNode*>& selection,
272    content::BrowserContext* browser_context) {
273  OpenURLIterator iterator(selection);
274  while (iterator.has_next()) {
275    const GURL* url = iterator.NextURL();
276    if (IsURLAllowedInIncognito(*url, browser_context))
277      return true;
278  }
279  return false;
280}
281
282GURL GetURLToBookmark(content::WebContents* web_contents) {
283  DCHECK(web_contents);
284  return IsInstantNTP(web_contents) ?
285      GURL(kChromeUINewTabURL) : web_contents->GetURL();
286}
287
288void GetURLAndTitleToBookmark(content::WebContents* web_contents,
289                              GURL* url,
290                              base::string16* title) {
291  *url = GetURLToBookmark(web_contents);
292  *title = web_contents->GetTitle();
293}
294
295void ToggleBookmarkBarWhenVisible(content::BrowserContext* browser_context) {
296  PrefService* prefs = user_prefs::UserPrefs::Get(browser_context);
297  const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
298
299  // The user changed when the bookmark bar is shown, update the preferences.
300  prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
301}
302
303base::string16 FormatBookmarkURLForDisplay(const GURL& url,
304                                           const PrefService* prefs) {
305  std::string languages;
306  if (prefs)
307    languages = prefs->GetString(prefs::kAcceptLanguages);
308
309  // Because this gets re-parsed by FixupURL(), it's safe to omit the scheme
310  // and trailing slash, and unescape most characters.  However, it's
311  // important not to drop any username/password, or unescape anything that
312  // changes the URL's meaning.
313  return net::FormatUrl(
314      url, languages,
315      net::kFormatUrlOmitAll & ~net::kFormatUrlOmitUsernamePassword,
316      net::UnescapeRule::SPACES, NULL, NULL, NULL);
317}
318
319bool IsAppsShortcutEnabled(Profile* profile,
320                           chrome::HostDesktopType host_desktop_type) {
321  // Managed users can not have apps installed currently so there's no need to
322  // show the apps shortcut.
323  if (profile->IsManaged())
324    return false;
325
326  // Don't show the apps shortcut in ash since the app launcher is enabled.
327  if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
328    return false;
329
330  return chrome::IsInstantExtendedAPIEnabled() && !profile->IsOffTheRecord();
331}
332
333bool ShouldShowAppsShortcutInBookmarkBar(
334  Profile* profile,
335  chrome::HostDesktopType host_desktop_type) {
336  return IsAppsShortcutEnabled(profile, host_desktop_type) &&
337      profile->GetPrefs()->GetBoolean(prefs::kShowAppsShortcutInBookmarkBar);
338}
339
340bool ShouldRemoveBookmarkThisPageUI(Profile* profile) {
341  return GetBookmarkShortcutDisposition(profile) ==
342         BOOKMARK_SHORTCUT_DISPOSITION_REMOVED;
343}
344
345bool ShouldRemoveBookmarkOpenPagesUI(Profile* profile) {
346  extensions::ExtensionRegistry* registry =
347      extensions::ExtensionRegistry::Get(profile);
348  if (!registry)
349    return false;
350
351  const extensions::ExtensionSet& extension_set =
352      registry->enabled_extensions();
353
354  for (extensions::ExtensionSet::const_iterator i = extension_set.begin();
355       i != extension_set.end();
356       ++i) {
357    if (extensions::CommandService::RemovesBookmarkOpenPagesShortcut(*i))
358      return true;
359  }
360
361  return false;
362}
363
364}  // namespace chrome
365