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 "components/bookmarks/browser/bookmark_model.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 34 namespace chrome { 35 36 int num_bookmark_urls_before_prompting = 15; 37 38 namespace { 39 40 // The ways in which extensions may customize the bookmark shortcut. 41 enum 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 // } 57 class 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 111 bool 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. 131 int 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. 143 void 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. 155 BookmarkShortcutDisposition 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 188 void 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 230 void 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 240 bool 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 250 void 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 265 bool HasBookmarkURLs(const std::vector<const BookmarkNode*>& selection) { 266 OpenURLIterator iterator(selection); 267 return iterator.has_next(); 268 } 269 270 bool 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 282 GURL GetURLToBookmark(content::WebContents* web_contents) { 283 DCHECK(web_contents); 284 return IsInstantNTP(web_contents) ? 285 GURL(kChromeUINewTabURL) : web_contents->GetURL(); 286 } 287 288 void GetURLAndTitleToBookmark(content::WebContents* web_contents, 289 GURL* url, 290 base::string16* title) { 291 *url = GetURLToBookmark(web_contents); 292 *title = web_contents->GetTitle(); 293 } 294 295 void 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 303 base::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 319 bool IsAppsShortcutEnabled(Profile* profile, 320 chrome::HostDesktopType host_desktop_type) { 321 // Supervised users can not have apps installed currently so there's no need 322 // to show the apps shortcut. 323 if (profile->IsSupervised()) 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 333 bool 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 340 bool ShouldRemoveBookmarkThisPageUI(Profile* profile) { 341 return GetBookmarkShortcutDisposition(profile) == 342 BOOKMARK_SHORTCUT_DISPOSITION_REMOVED; 343 } 344 345 bool 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