• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
NewBrowserPageNavigator(Profile * profile)61   explicit NewBrowserPageNavigator(Profile* profile)
62       : profile_(profile),
63         browser_(NULL) {}
64 
~NewBrowserPageNavigator()65   virtual ~NewBrowserPageNavigator() {
66     if (browser_)
67       browser_->window()->Show();
68   }
69 
browser() const70   Browser* browser() const { return browser_; }
71 
OpenURL(const GURL & url,const GURL & referrer,WindowOpenDisposition disposition,PageTransition::Type transition)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 
CloneBookmarkNodeImpl(BookmarkModel * model,const BookmarkNodeData::Element & element,const BookmarkNode * parent,int index_to_add_at)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.
ChildURLCount(const BookmarkNode * node)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.
OpenAllImpl(const BookmarkNode * node,WindowOpenDisposition initial_disposition,PageNavigator ** navigator,bool * opened_url)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 
ShouldOpenAll(gfx::NativeWindow parent,const std::vector<const BookmarkNode * > & nodes)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.
MoreRecentlyModified(const BookmarkNode * n1,const BookmarkNode * n2)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.
DoesBookmarkTextContainWords(const string16 & text,const std::vector<string16> & words)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.
DoesBookmarkContainWords(const BookmarkNode * node,const std::vector<string16> & words,const std::string & languages)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 
PreferredDropOperation(int source_operations,int operations)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 
BookmarkDragOperation(Profile * profile,const BookmarkNode * node)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)
BookmarkDropOperation(Profile * profile,const views::DropTargetEvent & event,const BookmarkNodeData & data,const BookmarkNode * parent,int index)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 
PerformBookmarkDrop(Profile * profile,const BookmarkNodeData & data,const BookmarkNode * parent_node,int index)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 
IsValidDropLocation(Profile * profile,const BookmarkNodeData & data,const BookmarkNode * drop_parent,int index)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 
CloneBookmarkNode(BookmarkModel * model,const std::vector<BookmarkNodeData::Element> & elements,const BookmarkNode * parent,int index_to_add_at)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
DragBookmarks(Profile * profile,const std::vector<const BookmarkNode * > & nodes,gfx::NativeView view)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 
OpenAll(gfx::NativeWindow parent,Profile * profile,PageNavigator * navigator,const std::vector<const BookmarkNode * > & nodes,WindowOpenDisposition initial_disposition)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 
OpenAll(gfx::NativeWindow parent,Profile * profile,PageNavigator * navigator,const BookmarkNode * node,WindowOpenDisposition initial_disposition)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 
CopyToClipboard(BookmarkModel * model,const std::vector<const BookmarkNode * > & nodes,bool remove_nodes)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 
PasteFromClipboard(BookmarkModel * model,const BookmarkNode * parent,int index)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 
CanPasteFromClipboard(const BookmarkNode * node)433 bool CanPasteFromClipboard(const BookmarkNode* node) {
434   if (!node)
435     return false;
436   return BookmarkNodeData::ClipboardContainsBookmarks();
437 }
438 
GetNameForURL(const GURL & url)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 
GetMostRecentlyModifiedFolders(BookmarkModel * model,size_t max_count)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 
GetMostRecentlyAddedEntries(BookmarkModel * model,size_t count,std::vector<const BookmarkNode * > * nodes)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 
TitleMatch()505 TitleMatch::TitleMatch()
506     : node(NULL) {
507 }
508 
~TitleMatch()509 TitleMatch::~TitleMatch() {}
510 
MoreRecentlyAdded(const BookmarkNode * n1,const BookmarkNode * n2)511 bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
512   return n1->date_added() > n2->date_added();
513 }
514 
GetBookmarksContainingText(BookmarkModel * model,const string16 & text,size_t max_count,const std::string & languages,std::vector<const BookmarkNode * > * nodes)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 
DoesBookmarkContainText(const BookmarkNode * node,const string16 & text,const std::string & languages)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 
CreateNewNode(BookmarkModel * model,const BookmarkNode * parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)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 
ApplyEditsWithNoFolderChange(BookmarkModel * model,const BookmarkNode * parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)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 
ApplyEditsWithPossibleFolderChange(BookmarkModel * model,const BookmarkNode * new_parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)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
ToggleWhenVisible(Profile * profile)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 
RegisterUserPrefs(PrefService * prefs)625 void RegisterUserPrefs(PrefService* prefs) {
626   prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
627   prefs->RegisterBooleanPref(prefs::kEditBookmarksEnabled, true);
628 }
629 
GetURLAndTitleToBookmark(TabContents * tab_contents,GURL * url,string16 * title)630 void GetURLAndTitleToBookmark(TabContents* tab_contents,
631                               GURL* url,
632                               string16* title) {
633   *url = tab_contents->GetURL();
634   *title = tab_contents->GetTitle();
635 }
636 
GetURLsForOpenTabs(Browser * browser,std::vector<std::pair<GURL,string16>> * urls)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 
GetParentForNewNodes(const BookmarkNode * parent,const std::vector<const BookmarkNode * > & selection,int * index)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 
NodeHasURLs(const BookmarkNode * node)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