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