Home | History | Annotate | Download | only in bookmarks
      1 // Copyright (c) 2011 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/bookmarks/bookmark_utils.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/file_path.h"
     11 #include "base/string16.h"
     12 #include "base/string_number_conversions.h"
     13 #include "base/time.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "chrome/browser/bookmarks/bookmark_model.h"
     16 #include "chrome/browser/bookmarks/bookmark_node_data.h"
     17 #include "chrome/browser/history/query_parser.h"
     18 #include "chrome/browser/platform_util.h"
     19 #include "chrome/browser/prefs/pref_service.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/ui/browser.h"
     22 #include "chrome/browser/ui/browser_list.h"
     23 #include "chrome/browser/ui/browser_window.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "content/browser/tab_contents/page_navigator.h"
     26 #include "content/browser/tab_contents/tab_contents.h"
     27 #include "content/common/notification_service.h"
     28 #include "grit/app_strings.h"
     29 #include "grit/chromium_strings.h"
     30 #include "grit/generated_resources.h"
     31 #include "net/base/net_util.h"
     32 #include "ui/base/dragdrop/drag_drop_types.h"
     33 #include "ui/base/l10n/l10n_util.h"
     34 #include "ui/base/models/tree_node_iterator.h"
     35 
     36 #if defined(OS_MACOSX)
     37 #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
     38 #endif
     39 
     40 #if defined(TOOLKIT_VIEWS)
     41 #include "ui/base/dragdrop/os_exchange_data.h"
     42 #include "views/drag_utils.h"
     43 #include "views/events/event.h"
     44 #include "views/widget/native_widget.h"
     45 #include "views/widget/widget.h"
     46 #endif
     47 
     48 #if defined(TOOLKIT_GTK)
     49 #include "chrome/browser/ui/gtk/custom_drag.h"
     50 #endif
     51 
     52 using base::Time;
     53 
     54 namespace {
     55 
     56 // A PageNavigator implementation that creates a new Browser. This is used when
     57 // opening a url and there is no Browser open. The Browser is created the first
     58 // time the PageNavigator method is invoked.
     59 class NewBrowserPageNavigator : public PageNavigator {
     60  public:
     61   explicit NewBrowserPageNavigator(Profile* profile)
     62       : profile_(profile),
     63         browser_(NULL) {}
     64 
     65   virtual ~NewBrowserPageNavigator() {
     66     if (browser_)
     67       browser_->window()->Show();
     68   }
     69 
     70   Browser* browser() const { return browser_; }
     71 
     72   virtual void OpenURL(const GURL& url,
     73                        const GURL& referrer,
     74                        WindowOpenDisposition disposition,
     75                        PageTransition::Type transition) OVERRIDE {
     76     if (!browser_) {
     77       Profile* profile = (disposition == OFF_THE_RECORD) ?
     78           profile_->GetOffTheRecordProfile() : profile_;
     79       browser_ = Browser::Create(profile);
     80       // Always open the first tab in the foreground.
     81       disposition = NEW_FOREGROUND_TAB;
     82     }
     83     browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB, transition);
     84   }
     85 
     86  private:
     87   Profile* profile_;
     88   Browser* browser_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
     91 };
     92 
     93 void CloneBookmarkNodeImpl(BookmarkModel* model,
     94                            const BookmarkNodeData::Element& element,
     95                            const BookmarkNode* parent,
     96                            int index_to_add_at) {
     97   if (element.is_url) {
     98     model->AddURL(parent, index_to_add_at, element.title, element.url);
     99   } else {
    100     const BookmarkNode* new_folder = model->AddFolder(parent,
    101                                                       index_to_add_at,
    102                                                       element.title);
    103     for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
    104       CloneBookmarkNodeImpl(model, element.children[i], new_folder, i);
    105   }
    106 }
    107 
    108 // Returns the number of children of |node| that are of type url.
    109 int ChildURLCount(const BookmarkNode* node) {
    110   int result = 0;
    111   for (int i = 0; i < node->child_count(); ++i) {
    112     const BookmarkNode* child = node->GetChild(i);
    113     if (child->is_url())
    114       result++;
    115   }
    116   return result;
    117 }
    118 
    119 // Implementation of OpenAll. Opens all nodes of type URL and any children of
    120 // |node| that are of type URL. |navigator| is the PageNavigator used to open
    121 // URLs. After the first url is opened |opened_url| is set to true and
    122 // |navigator| is set to the PageNavigator of the last active tab. This is done
    123 // to handle a window disposition of new window, in which case we want
    124 // subsequent tabs to open in that window.
    125 void OpenAllImpl(const BookmarkNode* node,
    126                  WindowOpenDisposition initial_disposition,
    127                  PageNavigator** navigator,
    128                  bool* opened_url) {
    129   if (node->is_url()) {
    130     WindowOpenDisposition disposition;
    131     if (*opened_url)
    132       disposition = NEW_BACKGROUND_TAB;
    133     else
    134       disposition = initial_disposition;
    135     (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
    136                           PageTransition::AUTO_BOOKMARK);
    137     if (!*opened_url) {
    138       *opened_url = true;
    139       // We opened the first URL which may have opened a new window or clobbered
    140       // the current page, reset the navigator just to be sure.
    141       Browser* new_browser = BrowserList::GetLastActive();
    142       if (new_browser) {
    143         TabContents* current_tab = new_browser->GetSelectedTabContents();
    144         DCHECK(new_browser && current_tab);
    145         if (new_browser && current_tab)
    146           *navigator = current_tab;
    147       }  // else, new_browser == NULL, which happens during testing.
    148     }
    149   } else {
    150     // For folders only open direct children.
    151     for (int i = 0; i < node->child_count(); ++i) {
    152       const BookmarkNode* child_node = node->GetChild(i);
    153       if (child_node->is_url())
    154         OpenAllImpl(child_node, initial_disposition, navigator, opened_url);
    155     }
    156   }
    157 }
    158 
    159 bool ShouldOpenAll(gfx::NativeWindow parent,
    160                    const std::vector<const BookmarkNode*>& nodes) {
    161   int child_count = 0;
    162   for (size_t i = 0; i < nodes.size(); ++i)
    163     child_count += ChildURLCount(nodes[i]);
    164   if (child_count < bookmark_utils::num_urls_before_prompting)
    165     return true;
    166 
    167   string16 message = l10n_util::GetStringFUTF16(
    168       IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
    169       base::IntToString16(child_count));
    170   string16 title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
    171   return platform_util::SimpleYesNoBox(parent, title, message);
    172 }
    173 
    174 // Comparison function that compares based on date modified of the two nodes.
    175 bool MoreRecentlyModified(const BookmarkNode* n1, const BookmarkNode* n2) {
    176   return n1->date_folder_modified() > n2->date_folder_modified();
    177 }
    178 
    179 // Returns true if |text| contains each string in |words|. This is used when
    180 // searching for bookmarks.
    181 bool DoesBookmarkTextContainWords(const string16& text,
    182                                   const std::vector<string16>& words) {
    183   for (size_t i = 0; i < words.size(); ++i) {
    184     if (text.find(words[i]) == string16::npos)
    185       return false;
    186   }
    187   return true;
    188 }
    189 
    190 // Returns true if |node|s title or url contains the strings in |words|.
    191 // |languages| argument is user's accept-language setting to decode IDN.
    192 bool DoesBookmarkContainWords(const BookmarkNode* node,
    193                               const std::vector<string16>& words,
    194                               const std::string& languages) {
    195   return
    196       DoesBookmarkTextContainWords(
    197           l10n_util::ToLower(node->GetTitle()), words) ||
    198       DoesBookmarkTextContainWords(
    199           l10n_util::ToLower(UTF8ToUTF16(node->GetURL().spec())), words) ||
    200       DoesBookmarkTextContainWords(l10n_util::ToLower(
    201           net::FormatUrl(node->GetURL(), languages, net::kFormatUrlOmitNothing,
    202                          UnescapeRule::NORMAL, NULL, NULL, NULL)), words);
    203 }
    204 
    205 }  // namespace
    206 
    207 namespace bookmark_utils {
    208 
    209 int num_urls_before_prompting = 15;
    210 
    211 int PreferredDropOperation(int source_operations, int operations) {
    212   int common_ops = (source_operations & operations);
    213   if (!common_ops)
    214     return 0;
    215   if (ui::DragDropTypes::DRAG_COPY & common_ops)
    216     return ui::DragDropTypes::DRAG_COPY;
    217   if (ui::DragDropTypes::DRAG_LINK & common_ops)
    218     return ui::DragDropTypes::DRAG_LINK;
    219   if (ui::DragDropTypes::DRAG_MOVE & common_ops)
    220     return ui::DragDropTypes::DRAG_MOVE;
    221   return ui::DragDropTypes::DRAG_NONE;
    222 }
    223 
    224 int BookmarkDragOperation(Profile* profile, const BookmarkNode* node) {
    225   int move = ui::DragDropTypes::DRAG_MOVE;
    226   if (!profile->GetPrefs()->GetBoolean(prefs::kEditBookmarksEnabled))
    227     move = 0;
    228   if (node->is_url()) {
    229     return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK | move;
    230   }
    231   return ui::DragDropTypes::DRAG_COPY | move;
    232 }
    233 
    234 #if defined(TOOLKIT_VIEWS)
    235 int BookmarkDropOperation(Profile* profile,
    236                           const views::DropTargetEvent& event,
    237                           const BookmarkNodeData& data,
    238                           const BookmarkNode* parent,
    239                           int index) {
    240   if (data.IsFromProfile(profile) && data.size() > 1)
    241     // Currently only accept one dragged node at a time.
    242     return ui::DragDropTypes::DRAG_NONE;
    243 
    244   if (!bookmark_utils::IsValidDropLocation(profile, data, parent, index))
    245     return ui::DragDropTypes::DRAG_NONE;
    246 
    247   if (data.GetFirstNode(profile)) {
    248     // User is dragging from this profile: move.
    249     return ui::DragDropTypes::DRAG_MOVE;
    250   }
    251   // User is dragging from another app, copy.
    252   return PreferredDropOperation(event.source_operations(),
    253       ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
    254 }
    255 #endif  // defined(TOOLKIT_VIEWS)
    256 
    257 int PerformBookmarkDrop(Profile* profile,
    258                         const BookmarkNodeData& data,
    259                         const BookmarkNode* parent_node,
    260                         int index) {
    261   BookmarkModel* model = profile->GetBookmarkModel();
    262   if (data.IsFromProfile(profile)) {
    263     const std::vector<const BookmarkNode*> dragged_nodes =
    264         data.GetNodes(profile);
    265     if (!dragged_nodes.empty()) {
    266       // Drag from same profile. Move nodes.
    267       for (size_t i = 0; i < dragged_nodes.size(); ++i) {
    268         model->Move(dragged_nodes[i], parent_node, index);
    269         index = parent_node->GetIndexOf(dragged_nodes[i]) + 1;
    270       }
    271       return ui::DragDropTypes::DRAG_MOVE;
    272     }
    273     return ui::DragDropTypes::DRAG_NONE;
    274   }
    275   // Dropping a folder from different profile. Always accept.
    276   bookmark_utils::CloneBookmarkNode(model, data.elements, parent_node, index);
    277   return ui::DragDropTypes::DRAG_COPY;
    278 }
    279 
    280 bool IsValidDropLocation(Profile* profile,
    281                          const BookmarkNodeData& data,
    282                          const BookmarkNode* drop_parent,
    283                          int index) {
    284   if (!drop_parent->is_folder()) {
    285     NOTREACHED();
    286     return false;
    287   }
    288 
    289   if (!data.is_valid())
    290     return false;
    291 
    292   if (data.IsFromProfile(profile)) {
    293     std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
    294     for (size_t i = 0; i < nodes.size(); ++i) {
    295       // Don't allow the drop if the user is attempting to drop on one of the
    296       // nodes being dragged.
    297       const BookmarkNode* node = nodes[i];
    298       int node_index = (drop_parent == node->parent()) ?
    299           drop_parent->GetIndexOf(nodes[i]) : -1;
    300       if (node_index != -1 && (index == node_index || index == node_index + 1))
    301         return false;
    302 
    303       // drop_parent can't accept a child that is an ancestor.
    304       if (drop_parent->HasAncestor(node))
    305         return false;
    306     }
    307     return true;
    308   }
    309   // From the same profile, always accept.
    310   return true;
    311 }
    312 
    313 void CloneBookmarkNode(BookmarkModel* model,
    314                        const std::vector<BookmarkNodeData::Element>& elements,
    315                        const BookmarkNode* parent,
    316                        int index_to_add_at) {
    317   if (!parent->is_folder() || !model) {
    318     NOTREACHED();
    319     return;
    320   }
    321   for (size_t i = 0; i < elements.size(); ++i)
    322     CloneBookmarkNodeImpl(model, elements[i], parent, index_to_add_at + i);
    323 }
    324 
    325 
    326 // Bookmark dragging
    327 void DragBookmarks(Profile* profile,
    328                    const std::vector<const BookmarkNode*>& nodes,
    329                    gfx::NativeView view) {
    330   DCHECK(!nodes.empty());
    331 
    332 #if defined(TOOLKIT_VIEWS)
    333   // Set up our OLE machinery
    334   ui::OSExchangeData data;
    335   BookmarkNodeData drag_data(nodes);
    336   drag_data.Write(profile, &data);
    337 
    338   // Allow nested message loop so we get DnD events as we drag this around.
    339   bool was_nested = MessageLoop::current()->IsNested();
    340   MessageLoop::current()->SetNestableTasksAllowed(true);
    341 
    342   views::NativeWidget* native_widget =
    343       views::NativeWidget::GetNativeWidgetForNativeView(view);
    344   if (native_widget) {
    345     native_widget->GetWidget()->RunShellDrag(NULL, data,
    346         ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE |
    347         ui::DragDropTypes::DRAG_LINK);
    348   }
    349 
    350   MessageLoop::current()->SetNestableTasksAllowed(was_nested);
    351 #elif defined(OS_MACOSX)
    352   // Allow nested message loop so we get DnD events as we drag this around.
    353   bool was_nested = MessageLoop::current()->IsNested();
    354   MessageLoop::current()->SetNestableTasksAllowed(true);
    355   bookmark_pasteboard_helper_mac::StartDrag(profile, nodes, view);
    356   MessageLoop::current()->SetNestableTasksAllowed(was_nested);
    357 #elif defined(TOOLKIT_GTK)
    358   BookmarkDrag::BeginDrag(profile, nodes);
    359 #endif
    360 }
    361 
    362 void OpenAll(gfx::NativeWindow parent,
    363              Profile* profile,
    364              PageNavigator* navigator,
    365              const std::vector<const BookmarkNode*>& nodes,
    366              WindowOpenDisposition initial_disposition) {
    367   if (!ShouldOpenAll(parent, nodes))
    368     return;
    369 
    370   NewBrowserPageNavigator navigator_impl(profile);
    371   if (!navigator) {
    372     Browser* browser =
    373         BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL, false);
    374     if (!browser || !browser->GetSelectedTabContents()) {
    375       navigator = &navigator_impl;
    376     } else {
    377       if (initial_disposition != NEW_WINDOW &&
    378           initial_disposition != OFF_THE_RECORD) {
    379         browser->window()->Activate();
    380       }
    381       navigator = browser->GetSelectedTabContents();
    382     }
    383   }
    384 
    385   bool opened_url = false;
    386   for (size_t i = 0; i < nodes.size(); ++i)
    387     OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
    388 }
    389 
    390 void OpenAll(gfx::NativeWindow parent,
    391              Profile* profile,
    392              PageNavigator* navigator,
    393              const BookmarkNode* node,
    394              WindowOpenDisposition initial_disposition) {
    395   std::vector<const BookmarkNode*> nodes;
    396   nodes.push_back(node);
    397   OpenAll(parent, profile, navigator, nodes, initial_disposition);
    398 }
    399 
    400 void CopyToClipboard(BookmarkModel* model,
    401                      const std::vector<const BookmarkNode*>& nodes,
    402                      bool remove_nodes) {
    403   if (nodes.empty())
    404     return;
    405 
    406   BookmarkNodeData(nodes).WriteToClipboard(NULL);
    407 
    408   if (remove_nodes) {
    409     for (size_t i = 0; i < nodes.size(); ++i) {
    410       int index = nodes[i]->parent()->GetIndexOf(nodes[i]);
    411       if (index > -1)
    412         model->Remove(nodes[i]->parent(), index);
    413     }
    414   }
    415 }
    416 
    417 void PasteFromClipboard(BookmarkModel* model,
    418                         const BookmarkNode* parent,
    419                         int index) {
    420   if (!parent)
    421     return;
    422 
    423   BookmarkNodeData bookmark_data;
    424   if (!bookmark_data.ReadFromClipboard())
    425     return;
    426 
    427   if (index == -1)
    428     index = parent->child_count();
    429   bookmark_utils::CloneBookmarkNode(
    430       model, bookmark_data.elements, parent, index);
    431 }
    432 
    433 bool CanPasteFromClipboard(const BookmarkNode* node) {
    434   if (!node)
    435     return false;
    436   return BookmarkNodeData::ClipboardContainsBookmarks();
    437 }
    438 
    439 string16 GetNameForURL(const GURL& url) {
    440   if (url.is_valid()) {
    441     return net::GetSuggestedFilename(url, "", "", string16());
    442   } else {
    443     return l10n_util::GetStringUTF16(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
    444   }
    445 }
    446 
    447 std::vector<const BookmarkNode*> GetMostRecentlyModifiedFolders(
    448     BookmarkModel* model,
    449     size_t max_count) {
    450   std::vector<const BookmarkNode*> nodes;
    451   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
    452   while (iterator.has_next()) {
    453     const BookmarkNode* parent = iterator.Next();
    454     if (parent->is_folder() && parent->date_folder_modified() > base::Time()) {
    455       if (max_count == 0) {
    456         nodes.push_back(parent);
    457       } else {
    458         std::vector<const BookmarkNode*>::iterator i =
    459             std::upper_bound(nodes.begin(), nodes.end(), parent,
    460                              &MoreRecentlyModified);
    461         if (nodes.size() < max_count || i != nodes.end()) {
    462           nodes.insert(i, parent);
    463           while (nodes.size() > max_count)
    464             nodes.pop_back();
    465         }
    466       }
    467     }  // else case, the root node, which we don't care about or imported nodes
    468        // (which have a time of 0).
    469   }
    470 
    471   if (nodes.size() < max_count) {
    472     // Add the bookmark bar and other nodes if there is space.
    473     if (find(nodes.begin(), nodes.end(), model->GetBookmarkBarNode()) ==
    474         nodes.end()) {
    475       nodes.push_back(model->GetBookmarkBarNode());
    476     }
    477 
    478     if (nodes.size() < max_count &&
    479         find(nodes.begin(), nodes.end(), model->other_node()) == nodes.end()) {
    480       nodes.push_back(model->other_node());
    481     }
    482   }
    483   return nodes;
    484 }
    485 
    486 void GetMostRecentlyAddedEntries(BookmarkModel* model,
    487                                  size_t count,
    488                                  std::vector<const BookmarkNode*>* nodes) {
    489   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
    490   while (iterator.has_next()) {
    491     const BookmarkNode* node = iterator.Next();
    492     if (node->is_url()) {
    493       std::vector<const BookmarkNode*>::iterator insert_position =
    494           std::upper_bound(nodes->begin(), nodes->end(), node,
    495                            &MoreRecentlyAdded);
    496       if (nodes->size() < count || insert_position != nodes->end()) {
    497         nodes->insert(insert_position, node);
    498         while (nodes->size() > count)
    499           nodes->pop_back();
    500       }
    501     }
    502   }
    503 }
    504 
    505 TitleMatch::TitleMatch()
    506     : node(NULL) {
    507 }
    508 
    509 TitleMatch::~TitleMatch() {}
    510 
    511 bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
    512   return n1->date_added() > n2->date_added();
    513 }
    514 
    515 void GetBookmarksContainingText(BookmarkModel* model,
    516                                 const string16& text,
    517                                 size_t max_count,
    518                                 const std::string& languages,
    519                                 std::vector<const BookmarkNode*>* nodes) {
    520   std::vector<string16> words;
    521   QueryParser parser;
    522   parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
    523   if (words.empty())
    524     return;
    525 
    526   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
    527   while (iterator.has_next()) {
    528     const BookmarkNode* node = iterator.Next();
    529     if (node->is_url() && DoesBookmarkContainWords(node, words, languages)) {
    530       nodes->push_back(node);
    531       if (nodes->size() == max_count)
    532         return;
    533     }
    534   }
    535 }
    536 
    537 bool DoesBookmarkContainText(const BookmarkNode* node,
    538                              const string16& text,
    539                              const std::string& languages) {
    540   std::vector<string16> words;
    541   QueryParser parser;
    542   parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
    543   if (words.empty())
    544     return false;
    545 
    546   return (node->is_url() && DoesBookmarkContainWords(node, words, languages));
    547 }
    548 
    549 static const BookmarkNode* CreateNewNode(BookmarkModel* model,
    550     const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
    551     const string16& new_title, const GURL& new_url) {
    552   const BookmarkNode* node;
    553   if (details.type == BookmarkEditor::EditDetails::NEW_URL) {
    554     node = model->AddURL(parent, parent->child_count(), new_title, new_url);
    555   } else if (details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
    556     node = model->AddFolder(parent, parent->child_count(), new_title);
    557     for (size_t i = 0; i < details.urls.size(); ++i) {
    558       model->AddURL(node, node->child_count(), details.urls[i].second,
    559                     details.urls[i].first);
    560     }
    561     model->SetDateFolderModified(parent, Time::Now());
    562   } else {
    563     NOTREACHED();
    564     return NULL;
    565   }
    566 
    567   return node;
    568 }
    569 
    570 const BookmarkNode* ApplyEditsWithNoFolderChange(BookmarkModel* model,
    571     const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
    572     const string16& new_title, const GURL& new_url) {
    573   if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
    574       details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
    575     return CreateNewNode(model, parent, details, new_title, new_url);
    576   }
    577 
    578   const BookmarkNode* node = details.existing_node;
    579   DCHECK(node);
    580 
    581   if (node->is_url())
    582     model->SetURL(node, new_url);
    583   model->SetTitle(node, new_title);
    584 
    585   return node;
    586 }
    587 
    588 const BookmarkNode* ApplyEditsWithPossibleFolderChange(BookmarkModel* model,
    589     const BookmarkNode* new_parent, const BookmarkEditor::EditDetails& details,
    590     const string16& new_title, const GURL& new_url) {
    591   if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
    592       details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
    593     return CreateNewNode(model, new_parent, details, new_title, new_url);
    594   }
    595 
    596   const BookmarkNode* node = details.existing_node;
    597   DCHECK(node);
    598 
    599   if (new_parent != node->parent())
    600     model->Move(node, new_parent, new_parent->child_count());
    601   if (node->is_url())
    602     model->SetURL(node, new_url);
    603   model->SetTitle(node, new_title);
    604 
    605   return node;
    606 }
    607 
    608 // Formerly in BookmarkBarView
    609 void ToggleWhenVisible(Profile* profile) {
    610   PrefService* prefs = profile->GetPrefs();
    611   const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
    612 
    613   // The user changed when the bookmark bar is shown, update the preferences.
    614   prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
    615   prefs->ScheduleSavePersistentPrefs();
    616 
    617   // And notify the notification service.
    618   Source<Profile> source(profile);
    619   NotificationService::current()->Notify(
    620       NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
    621       source,
    622       NotificationService::NoDetails());
    623 }
    624 
    625 void RegisterUserPrefs(PrefService* prefs) {
    626   prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
    627   prefs->RegisterBooleanPref(prefs::kEditBookmarksEnabled, true);
    628 }
    629 
    630 void GetURLAndTitleToBookmark(TabContents* tab_contents,
    631                               GURL* url,
    632                               string16* title) {
    633   *url = tab_contents->GetURL();
    634   *title = tab_contents->GetTitle();
    635 }
    636 
    637 void GetURLsForOpenTabs(Browser* browser,
    638     std::vector<std::pair<GURL, string16> >* urls) {
    639   for (int i = 0; i < browser->tab_count(); ++i) {
    640     std::pair<GURL, string16> entry;
    641     GetURLAndTitleToBookmark(browser->GetTabContentsAt(i), &(entry.first),
    642                              &(entry.second));
    643     urls->push_back(entry);
    644   }
    645 }
    646 
    647 const BookmarkNode* GetParentForNewNodes(
    648     const BookmarkNode* parent,
    649     const std::vector<const BookmarkNode*>& selection,
    650     int* index) {
    651   const BookmarkNode* real_parent = parent;
    652 
    653   if (selection.size() == 1 && selection[0]->is_folder())
    654     real_parent = selection[0];
    655 
    656   if (index) {
    657     if (selection.size() == 1 && selection[0]->is_url()) {
    658       *index = real_parent->GetIndexOf(selection[0]) + 1;
    659       if (*index == 0) {
    660         // Node doesn't exist in parent, add to end.
    661         NOTREACHED();
    662         *index = real_parent->child_count();
    663       }
    664     } else {
    665       *index = real_parent->child_count();
    666     }
    667   }
    668 
    669   return real_parent;
    670 }
    671 
    672 bool NodeHasURLs(const BookmarkNode* node) {
    673   DCHECK(node);
    674 
    675   if (node->is_url())
    676     return true;
    677 
    678   for (int i = 0; i < node->child_count(); ++i) {
    679     if (NodeHasURLs(node->GetChild(i)))
    680       return true;
    681   }
    682   return false;
    683 }
    684 
    685 }  // namespace bookmark_utils
    686