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