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 38namespace chrome { 39 40int num_bookmark_urls_before_prompting = 15; 41 42namespace { 43 44// The ways in which extensions may customize the bookmark shortcut. 45enum 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// } 61class OpenURLIterator { 62 public: 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 71 bool has_next() { return next_ != NULL;} 72 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. 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 115bool 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. 135int 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. 147void 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. 159BookmarkShortcutDisposition 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 196void 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 238void 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 248bool 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 258void 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 273bool HasBookmarkURLs(const std::vector<const BookmarkNode*>& selection) { 274 OpenURLIterator iterator(selection); 275 return iterator.has_next(); 276} 277 278bool 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 290GURL GetURLToBookmark(content::WebContents* web_contents) { 291 DCHECK(web_contents); 292 return IsInstantNTP(web_contents) ? 293 GURL(kChromeUINewTabURL) : web_contents->GetURL(); 294} 295 296void GetURLAndTitleToBookmark(content::WebContents* web_contents, 297 GURL* url, 298 base::string16* title) { 299 *url = GetURLToBookmark(web_contents); 300 *title = web_contents->GetTitle(); 301} 302 303void 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 312base::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 328bool 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 342bool 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 350bool ShouldRemoveBookmarkThisPageUI(Profile* profile) { 351 return GetBookmarkShortcutDisposition(profile) == 352 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED; 353} 354 355bool 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