- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / bookmarks / bookmark_utils.cc
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/bookmarks/bookmark_utils.h"
6
7 #include <utility>
8
9 #include "base/basictypes.h"
10 #include "base/files/file_path.h"
11 #include "base/i18n/case_conversion.h"
12 #include "base/i18n/string_search.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/bookmarks/bookmark_model.h"
18 #include "chrome/browser/history/query_parser.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/user_prefs/pref_registry_syncable.h"
21 #include "content/public/browser/user_metrics.h"
22 #include "net/base/net_util.h"
23 #include "ui/base/models/tree_node_iterator.h"
24
25 using base::Time;
26
27 namespace {
28
29 void CloneBookmarkNodeImpl(BookmarkModel* model,
30                            const BookmarkNodeData::Element& element,
31                            const BookmarkNode* parent,
32                            int index_to_add_at,
33                            bool reset_node_times) {
34   if (element.is_url) {
35     if (reset_node_times) {
36       model->AddURL(parent, index_to_add_at, element.title, element.url);
37     } else {
38       DCHECK(!element.date_added.is_null());
39       model->AddURLWithCreationTime(parent, index_to_add_at, element.title,
40                                     element.url, element.date_added);
41     }
42   } else {
43     const BookmarkNode* new_folder = model->AddFolder(parent,
44                                                       index_to_add_at,
45                                                       element.title);
46     if (!reset_node_times) {
47       DCHECK(!element.date_folder_modified.is_null());
48       model->SetDateFolderModified(new_folder, element.date_folder_modified);
49     }
50     for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
51       CloneBookmarkNodeImpl(model, element.children[i], new_folder, i,
52                             reset_node_times);
53   }
54 }
55
56 // Comparison function that compares based on date modified of the two nodes.
57 bool MoreRecentlyModified(const BookmarkNode* n1, const BookmarkNode* n2) {
58   return n1->date_folder_modified() > n2->date_folder_modified();
59 }
60
61 // Returns true if |text| contains each string in |words|. This is used when
62 // searching for bookmarks.
63 bool DoesBookmarkTextContainWords(const string16& text,
64                                   const std::vector<string16>& words) {
65   for (size_t i = 0; i < words.size(); ++i) {
66     if (!base::i18n::StringSearchIgnoringCaseAndAccents(
67             words[i], text, NULL, NULL)) {
68       return false;
69     }
70   }
71   return true;
72 }
73
74 // Returns true if |node|s title or url contains the strings in |words|.
75 // |languages| argument is user's accept-language setting to decode IDN.
76 bool DoesBookmarkContainWords(const BookmarkNode* node,
77                               const std::vector<string16>& words,
78                               const std::string& languages) {
79   return
80       DoesBookmarkTextContainWords(node->GetTitle(), words) ||
81       DoesBookmarkTextContainWords(UTF8ToUTF16(node->url().spec()), words) ||
82       DoesBookmarkTextContainWords(net::FormatUrl(
83           node->url(), languages, net::kFormatUrlOmitNothing,
84           net::UnescapeRule::NORMAL, NULL, NULL, NULL), words);
85 }
86
87 }  // namespace
88
89 namespace bookmark_utils {
90
91 void CloneBookmarkNode(BookmarkModel* model,
92                        const std::vector<BookmarkNodeData::Element>& elements,
93                        const BookmarkNode* parent,
94                        int index_to_add_at,
95                        bool reset_node_times) {
96   if (!parent->is_folder() || !model) {
97     NOTREACHED();
98     return;
99   }
100   for (size_t i = 0; i < elements.size(); ++i) {
101     CloneBookmarkNodeImpl(model, elements[i], parent, index_to_add_at + i,
102                           reset_node_times);
103   }
104 }
105
106 void CopyToClipboard(BookmarkModel* model,
107                      const std::vector<const BookmarkNode*>& nodes,
108                      bool remove_nodes) {
109   if (nodes.empty())
110     return;
111
112   BookmarkNodeData(nodes).WriteToClipboard(ui::CLIPBOARD_TYPE_COPY_PASTE);
113
114   if (remove_nodes) {
115     for (size_t i = 0; i < nodes.size(); ++i) {
116       int index = nodes[i]->parent()->GetIndexOf(nodes[i]);
117       if (index > -1)
118         model->Remove(nodes[i]->parent(), index);
119     }
120   }
121 }
122
123 void PasteFromClipboard(BookmarkModel* model,
124                         const BookmarkNode* parent,
125                         int index) {
126   if (!parent)
127     return;
128
129   BookmarkNodeData bookmark_data;
130   if (!bookmark_data.ReadFromClipboard(ui::CLIPBOARD_TYPE_COPY_PASTE))
131     return;
132
133   if (index == -1)
134     index = parent->child_count();
135   CloneBookmarkNode(model, bookmark_data.elements, parent, index, true);
136 }
137
138 bool CanPasteFromClipboard(const BookmarkNode* node) {
139   if (!node)
140     return false;
141   return BookmarkNodeData::ClipboardContainsBookmarks();
142 }
143
144 // This is used with a tree iterator to skip subtrees which are not visible.
145 static bool PruneInvisibleFolders(const BookmarkNode* node) {
146   return !node->IsVisible();
147 }
148
149 std::vector<const BookmarkNode*> GetMostRecentlyModifiedFolders(
150     BookmarkModel* model,
151     size_t max_count) {
152   std::vector<const BookmarkNode*> nodes;
153   ui::TreeNodeIterator<const BookmarkNode>
154       iterator(model->root_node(), PruneInvisibleFolders);
155
156   while (iterator.has_next()) {
157     const BookmarkNode* parent = iterator.Next();
158     if (parent->is_folder() && parent->date_folder_modified() > base::Time()) {
159       if (max_count == 0) {
160         nodes.push_back(parent);
161       } else {
162         std::vector<const BookmarkNode*>::iterator i =
163             std::upper_bound(nodes.begin(), nodes.end(), parent,
164                              &MoreRecentlyModified);
165         if (nodes.size() < max_count || i != nodes.end()) {
166           nodes.insert(i, parent);
167           while (nodes.size() > max_count)
168             nodes.pop_back();
169         }
170       }
171     }  // else case, the root node, which we don't care about or imported nodes
172        // (which have a time of 0).
173   }
174
175   if (nodes.size() < max_count) {
176     // Add the permanent nodes if there is space. The permanent nodes are the
177     // only children of the root_node.
178     const BookmarkNode* root_node = model->root_node();
179
180     for (int i = 0; i < root_node->child_count(); ++i) {
181       const BookmarkNode* node = root_node->GetChild(i);
182       if (node->IsVisible() &&
183           std::find(nodes.begin(), nodes.end(), node) == nodes.end()) {
184         nodes.push_back(node);
185
186         if (nodes.size() == max_count)
187           break;
188       }
189     }
190   }
191   return nodes;
192 }
193
194 void GetMostRecentlyAddedEntries(BookmarkModel* model,
195                                  size_t count,
196                                  std::vector<const BookmarkNode*>* nodes) {
197   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
198   while (iterator.has_next()) {
199     const BookmarkNode* node = iterator.Next();
200     if (node->is_url()) {
201       std::vector<const BookmarkNode*>::iterator insert_position =
202           std::upper_bound(nodes->begin(), nodes->end(), node,
203                            &MoreRecentlyAdded);
204       if (nodes->size() < count || insert_position != nodes->end()) {
205         nodes->insert(insert_position, node);
206         while (nodes->size() > count)
207           nodes->pop_back();
208       }
209     }
210   }
211 }
212
213 bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
214   return n1->date_added() > n2->date_added();
215 }
216
217 void GetBookmarksContainingText(BookmarkModel* model,
218                                 const string16& text,
219                                 size_t max_count,
220                                 const std::string& languages,
221                                 std::vector<const BookmarkNode*>* nodes) {
222   std::vector<string16> words;
223   QueryParser parser;
224   parser.ParseQueryWords(base::i18n::ToLower(text), &words);
225   if (words.empty())
226     return;
227
228   ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
229   while (iterator.has_next()) {
230     const BookmarkNode* node = iterator.Next();
231     if (node->is_url() && DoesBookmarkContainWords(node, words, languages)) {
232       nodes->push_back(node);
233       if (nodes->size() == max_count)
234         return;
235     }
236   }
237 }
238
239 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
240   registry->RegisterBooleanPref(
241       prefs::kShowBookmarkBar,
242       false,
243       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
244   registry->RegisterBooleanPref(
245       prefs::kEditBookmarksEnabled,
246       true,
247       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
248   registry->RegisterBooleanPref(
249       prefs::kShowAppsShortcutInBookmarkBar,
250       true,
251       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
252 }
253
254 const BookmarkNode* GetParentForNewNodes(
255     const BookmarkNode* parent,
256     const std::vector<const BookmarkNode*>& selection,
257     int* index) {
258   const BookmarkNode* real_parent = parent;
259
260   if (selection.size() == 1 && selection[0]->is_folder())
261     real_parent = selection[0];
262
263   if (index) {
264     if (selection.size() == 1 && selection[0]->is_url()) {
265       *index = real_parent->GetIndexOf(selection[0]) + 1;
266       if (*index == 0) {
267         // Node doesn't exist in parent, add to end.
268         NOTREACHED();
269         *index = real_parent->child_count();
270       }
271     } else {
272       *index = real_parent->child_count();
273     }
274   }
275
276   return real_parent;
277 }
278
279 void DeleteBookmarkFolders(BookmarkModel* model,
280                            const std::vector<int64>& ids) {
281   // Remove the folders that were removed. This has to be done after all the
282   // other changes have been committed.
283   for (std::vector<int64>::const_iterator iter = ids.begin();
284        iter != ids.end();
285        ++iter) {
286     const BookmarkNode* node = model->GetNodeByID(*iter);
287     if (!node)
288       continue;
289     const BookmarkNode* parent = node->parent();
290     model->Remove(parent, parent->GetIndexOf(node));
291   }
292 }
293
294 void AddIfNotBookmarked(BookmarkModel* model,
295                         const GURL& url,
296                         const string16& title) {
297   std::vector<const BookmarkNode*> bookmarks;
298   model->GetNodesByURL(url, &bookmarks);
299   if (!bookmarks.empty())
300     return;  // Nothing to do, a bookmark with that url already exists.
301
302   content::RecordAction(content::UserMetricsAction("BookmarkAdded"));
303   const BookmarkNode* parent = model->GetParentForNewNodes();
304   model->AddURL(parent, parent->child_count(), title, url);
305 }
306
307 void RemoveAllBookmarks(BookmarkModel* model, const GURL& url) {
308   std::vector<const BookmarkNode*> bookmarks;
309   model->GetNodesByURL(url, &bookmarks);
310
311   // Remove all the bookmarks.
312   for (size_t i = 0; i < bookmarks.size(); ++i) {
313     const BookmarkNode* node = bookmarks[i];
314     int index = node->parent()->GetIndexOf(node);
315     if (index > -1)
316       model->Remove(node->parent(), index);
317   }
318 }
319
320 }  // namespace bookmark_utils