Home | History | Annotate | Download | only in bookmarks
      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