1 // Copyright 2014 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 "components/bookmarks/browser/bookmark_utils.h"
6 
7 #include <stddef.h>
8 
9 #include <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
18 #include "components/bookmarks/browser/bookmark_model.h"
19 #include "components/bookmarks/browser/bookmark_node_data.h"
20 #include "components/bookmarks/test/test_bookmark_client.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "ui/base/clipboard/clipboard.h"
23 #include "ui/base/clipboard/scoped_clipboard_writer.h"
24 
25 using base::ASCIIToUTF16;
26 using std::string;
27 
28 namespace bookmarks {
29 namespace {
30 
31 class BookmarkUtilsTest : public testing::Test,
32                           public BaseBookmarkModelObserver {
33  public:
BookmarkUtilsTest()34   BookmarkUtilsTest()
35       : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
36         grouped_changes_beginning_count_(0),
37         grouped_changes_ended_count_(0) {}
38 
~BookmarkUtilsTest()39   ~BookmarkUtilsTest() override {}
40 
41 // Copy and paste is not yet supported on iOS. http://crbug.com/228147
42 #if !defined(OS_IOS)
TearDown()43   void TearDown() override {
44     ui::Clipboard::DestroyClipboardForCurrentThread();
45   }
46 #endif  // !defined(OS_IOS)
47 
48   // Certain user actions require multiple changes to the bookmark model,
49   // however these modifications need to be atomic for the undo framework. The
50   // BaseBookmarkModelObserver is used to inform the boundaries of the user
51   // action. For example, when multiple bookmarks are cut to the clipboard we
52   // expect one call each to GroupedBookmarkChangesBeginning/Ended.
ExpectGroupedChangeCount(int expected_beginning_count,int expected_ended_count)53   void ExpectGroupedChangeCount(int expected_beginning_count,
54                                 int expected_ended_count) {
55     // The undo framework is not used under Android.  Thus the group change
56     // events will not be fired and so should not be tested for Android.
57 #if !defined(OS_ANDROID)
58     EXPECT_EQ(grouped_changes_beginning_count_, expected_beginning_count);
59     EXPECT_EQ(grouped_changes_ended_count_, expected_ended_count);
60 #endif
61   }
62 
63  private:
64   // BaseBookmarkModelObserver:
BookmarkModelChanged()65   void BookmarkModelChanged() override {}
66 
GroupedBookmarkChangesBeginning(BookmarkModel * model)67   void GroupedBookmarkChangesBeginning(BookmarkModel* model) override {
68     ++grouped_changes_beginning_count_;
69   }
70 
GroupedBookmarkChangesEnded(BookmarkModel * model)71   void GroupedBookmarkChangesEnded(BookmarkModel* model) override {
72     ++grouped_changes_ended_count_;
73   }
74 
75   // Clipboard requires a full TaskEnvironment.
76   base::test::TaskEnvironment task_environment_;
77 
78   int grouped_changes_beginning_count_;
79   int grouped_changes_ended_count_;
80 
81   DISALLOW_COPY_AND_ASSIGN(BookmarkUtilsTest);
82 };
83 
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesWordPhraseQuery)84 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesWordPhraseQuery) {
85   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
86   const BookmarkNode* node1 = model->AddURL(model->other_node(),
87                                             0,
88                                             ASCIIToUTF16("foo bar"),
89                                             GURL("http://www.google.com"));
90   const BookmarkNode* node2 = model->AddURL(model->other_node(),
91                                             0,
92                                             ASCIIToUTF16("baz buz"),
93                                             GURL("http://www.cnn.com"));
94   const BookmarkNode* folder1 =
95       model->AddFolder(model->other_node(), 0, ASCIIToUTF16("foo"));
96   std::vector<const BookmarkNode*> nodes;
97   QueryFields query;
98   query.word_phrase_query.reset(new base::string16);
99   // No nodes are returned for empty string.
100   *query.word_phrase_query = ASCIIToUTF16("");
101   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
102   EXPECT_TRUE(nodes.empty());
103   nodes.clear();
104 
105   // No nodes are returned for space-only string.
106   *query.word_phrase_query = ASCIIToUTF16("   ");
107   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
108   EXPECT_TRUE(nodes.empty());
109   nodes.clear();
110 
111   // Node "foo bar" and folder "foo" are returned in search results.
112   *query.word_phrase_query = ASCIIToUTF16("foo");
113   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
114   ASSERT_EQ(2U, nodes.size());
115   EXPECT_TRUE(nodes[0] == folder1);
116   EXPECT_TRUE(nodes[1] == node1);
117   nodes.clear();
118 
119   // Ensure url matches return in search results.
120   *query.word_phrase_query = ASCIIToUTF16("cnn");
121   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
122   ASSERT_EQ(1U, nodes.size());
123   EXPECT_TRUE(nodes[0] == node2);
124   nodes.clear();
125 
126   // Ensure folder "foo" is not returned in more specific search.
127   *query.word_phrase_query = ASCIIToUTF16("foo bar");
128   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
129   ASSERT_EQ(1U, nodes.size());
130   EXPECT_TRUE(nodes[0] == node1);
131   nodes.clear();
132 
133   // Bookmark Bar and Other Bookmarks are not returned in search results.
134   *query.word_phrase_query = ASCIIToUTF16("Bookmark");
135   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
136   ASSERT_EQ(0U, nodes.size());
137   nodes.clear();
138 }
139 
140 // Check exact matching against a URL query.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesUrl)141 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesUrl) {
142   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
143   const BookmarkNode* node1 = model->AddURL(model->other_node(),
144                                             0,
145                                             ASCIIToUTF16("Google"),
146                                             GURL("https://www.google.com/"));
147   model->AddURL(model->other_node(),
148                 0,
149                 ASCIIToUTF16("Google Calendar"),
150                 GURL("https://www.google.com/calendar"));
151 
152   model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
153 
154   std::vector<const BookmarkNode*> nodes;
155   QueryFields query;
156   query.url.reset(new base::string16);
157   *query.url = ASCIIToUTF16("https://www.google.com/");
158   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
159   ASSERT_EQ(1U, nodes.size());
160   EXPECT_TRUE(nodes[0] == node1);
161   nodes.clear();
162 
163   *query.url = ASCIIToUTF16("calendar");
164   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
165   ASSERT_EQ(0U, nodes.size());
166   nodes.clear();
167 
168   // Empty URL should not match folders.
169   *query.url = ASCIIToUTF16("");
170   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
171   ASSERT_EQ(0U, nodes.size());
172   nodes.clear();
173 }
174 
175 // Check exact matching against a title query.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesTitle)176 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesTitle) {
177   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
178   const BookmarkNode* node1 = model->AddURL(model->other_node(),
179                                             0,
180                                             ASCIIToUTF16("Google"),
181                                             GURL("https://www.google.com/"));
182   model->AddURL(model->other_node(),
183                 0,
184                 ASCIIToUTF16("Google Calendar"),
185                 GURL("https://www.google.com/calendar"));
186 
187   const BookmarkNode* folder1 =
188       model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
189 
190   std::vector<const BookmarkNode*> nodes;
191   QueryFields query;
192   query.title.reset(new base::string16);
193   *query.title = ASCIIToUTF16("Google");
194   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
195   ASSERT_EQ(1U, nodes.size());
196   EXPECT_TRUE(nodes[0] == node1);
197   nodes.clear();
198 
199   *query.title = ASCIIToUTF16("Calendar");
200   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
201   ASSERT_EQ(0U, nodes.size());
202   nodes.clear();
203 
204   // Title should match folders.
205   *query.title = ASCIIToUTF16("Folder");
206   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
207   ASSERT_EQ(1U, nodes.size());
208   EXPECT_TRUE(nodes[0] == folder1);
209   nodes.clear();
210 }
211 
212 // Check matching against a query with multiple predicates.
TEST_F(BookmarkUtilsTest,GetBookmarksMatchingPropertiesConjunction)213 TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesConjunction) {
214   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
215   const BookmarkNode* node1 = model->AddURL(model->other_node(),
216                                             0,
217                                             ASCIIToUTF16("Google"),
218                                             GURL("https://www.google.com/"));
219   model->AddURL(model->other_node(),
220                 0,
221                 ASCIIToUTF16("Google Calendar"),
222                 GURL("https://www.google.com/calendar"));
223 
224   model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
225 
226   std::vector<const BookmarkNode*> nodes;
227   QueryFields query;
228 
229   // Test all fields matching.
230   query.word_phrase_query.reset(new base::string16(ASCIIToUTF16("www")));
231   query.url.reset(new base::string16(ASCIIToUTF16("https://www.google.com/")));
232   query.title.reset(new base::string16(ASCIIToUTF16("Google")));
233   GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
234   ASSERT_EQ(1U, nodes.size());
235   EXPECT_TRUE(nodes[0] == node1);
236   nodes.clear();
237 
238   std::unique_ptr<base::string16>* fields[] = {&query.word_phrase_query,
239                                                &query.url, &query.title};
240 
241   // Test two fields matching.
242   for (size_t i = 0; i < base::size(fields); i++) {
243     std::unique_ptr<base::string16> original_value(fields[i]->release());
244     GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
245     ASSERT_EQ(1U, nodes.size());
246     EXPECT_TRUE(nodes[0] == node1);
247     nodes.clear();
248     *fields[i] = std::move(original_value);
249   }
250 
251   // Test two fields matching with one non-matching field.
252   for (size_t i = 0; i < base::size(fields); i++) {
253     std::unique_ptr<base::string16> original_value(fields[i]->release());
254     fields[i]->reset(new base::string16(ASCIIToUTF16("fjdkslafjkldsa")));
255     GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
256     ASSERT_EQ(0U, nodes.size());
257     nodes.clear();
258     *fields[i] = std::move(original_value);
259   }
260 }
261 
262 // Copy and paste is not yet supported on iOS. http://crbug.com/228147
263 #if !defined(OS_IOS)
TEST_F(BookmarkUtilsTest,DISABLED_PasteBookmarkFromURL)264 TEST_F(BookmarkUtilsTest, DISABLED_PasteBookmarkFromURL) {
265   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
266   const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
267   const BookmarkNode* new_folder = model->AddFolder(
268       model->bookmark_bar_node(), 0, ASCIIToUTF16("New_Folder"));
269 
270   // Write blank text to clipboard.
271   {
272     ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
273     clipboard_writer.WriteText(base::string16());
274   }
275   // Now we shouldn't be able to paste from the clipboard.
276   EXPECT_FALSE(CanPasteFromClipboard(model.get(), new_folder));
277 
278   // Write some valid url to the clipboard.
279   {
280     ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
281     clipboard_writer.WriteText(url_text);
282   }
283   // Now we should be able to paste from the clipboard.
284   EXPECT_TRUE(CanPasteFromClipboard(model.get(), new_folder));
285 
286   PasteFromClipboard(model.get(), new_folder, 0);
287   ASSERT_EQ(1u, new_folder->children().size());
288 
289   // Url for added node should be same as url_text.
290   EXPECT_EQ(url_text,
291             ASCIIToUTF16(new_folder->children().front()->url().spec()));
292 }
293 
294 // TODO(https://crbug.com/1010182): Fix flakes and re-enable this test.
295 #if defined(OS_WIN) || defined(OS_MACOSX)
296 #define MAYBE_CopyPaste DISABLED_CopyPaste
297 #else
298 #define MAYBE_CopyPaste CopyPaste
299 #endif
TEST_F(BookmarkUtilsTest,MAYBE_CopyPaste)300 TEST_F(BookmarkUtilsTest, MAYBE_CopyPaste) {
301   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
302   const BookmarkNode* node = model->AddURL(model->other_node(),
303                                            0,
304                                            ASCIIToUTF16("foo bar"),
305                                            GURL("http://www.google.com"));
306 
307   // Copy a node to the clipboard.
308   std::vector<const BookmarkNode*> nodes;
309   nodes.push_back(node);
310   CopyToClipboard(model.get(), nodes, false);
311 
312   // And make sure we can paste a bookmark from the clipboard.
313   EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
314 
315   // Write some text to the clipboard.
316   {
317     ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
318     clipboard_writer.WriteText(ASCIIToUTF16("foo"));
319   }
320 
321   // Now we shouldn't be able to paste from the clipboard.
322   EXPECT_FALSE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
323 }
324 
325 // Test for updating title such that url and title pair are unique among the
326 // children of parent.
TEST_F(BookmarkUtilsTest,DISABLED_MakeTitleUnique)327 TEST_F(BookmarkUtilsTest, DISABLED_MakeTitleUnique) {
328   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
329   const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
330   const base::string16 title_text = ASCIIToUTF16("foobar");
331   const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
332 
333   const BookmarkNode* node =
334       model->AddURL(bookmark_bar_node, 0, title_text, GURL(url_text));
335 
336   EXPECT_EQ(url_text,
337             ASCIIToUTF16(bookmark_bar_node->children()[0]->url().spec()));
338   EXPECT_EQ(title_text, bookmark_bar_node->children()[0]->GetTitle());
339 
340   // Copy a node to the clipboard.
341   std::vector<const BookmarkNode*> nodes;
342   nodes.push_back(node);
343   CopyToClipboard(model.get(), nodes, false);
344 
345   // Now we should be able to paste from the clipboard.
346   EXPECT_TRUE(CanPasteFromClipboard(model.get(), bookmark_bar_node));
347 
348   PasteFromClipboard(model.get(), bookmark_bar_node, 1);
349   ASSERT_EQ(2u, bookmark_bar_node->children().size());
350 
351   // Url for added node should be same as url_text.
352   EXPECT_EQ(url_text,
353             ASCIIToUTF16(bookmark_bar_node->children()[1]->url().spec()));
354   // Title for added node should be numeric subscript suffix with copied node
355   // title.
356   EXPECT_EQ(ASCIIToUTF16("foobar (1)"),
357             bookmark_bar_node->children()[1]->GetTitle());
358 }
359 
TEST_F(BookmarkUtilsTest,DISABLED_CopyPasteMetaInfo)360 TEST_F(BookmarkUtilsTest, DISABLED_CopyPasteMetaInfo) {
361   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
362   const BookmarkNode* node = model->AddURL(model->other_node(),
363                                            0,
364                                            ASCIIToUTF16("foo bar"),
365                                            GURL("http://www.google.com"));
366   model->SetNodeMetaInfo(node, "somekey", "somevalue");
367   model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
368 
369   // Copy a node to the clipboard.
370   std::vector<const BookmarkNode*> nodes;
371   nodes.push_back(node);
372   CopyToClipboard(model.get(), nodes, false);
373 
374   // Paste node to a different folder.
375   const BookmarkNode* folder =
376       model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
377   EXPECT_EQ(0u, folder->children().size());
378 
379   // And make sure we can paste a bookmark from the clipboard.
380   EXPECT_TRUE(CanPasteFromClipboard(model.get(), folder));
381 
382   PasteFromClipboard(model.get(), folder, 0);
383   ASSERT_EQ(1u, folder->children().size());
384 
385   // Verify that the pasted node contains the same meta info.
386   const BookmarkNode* pasted = folder->children().front().get();
387   ASSERT_TRUE(pasted->GetMetaInfoMap());
388   EXPECT_EQ(2u, pasted->GetMetaInfoMap()->size());
389   std::string value;
390   EXPECT_TRUE(pasted->GetMetaInfo("somekey", &value));
391   EXPECT_EQ("somevalue", value);
392   EXPECT_TRUE(pasted->GetMetaInfo("someotherkey", &value));
393   EXPECT_EQ("someothervalue", value);
394 }
395 
396 #if defined(OS_LINUX) || defined(OS_MACOSX)
397 // http://crbug.com/396472
398 #define MAYBE_CutToClipboard DISABLED_CutToClipboard
399 #else
400 #define MAYBE_CutToClipboard CutToClipboard
401 #endif
TEST_F(BookmarkUtilsTest,MAYBE_CutToClipboard)402 TEST_F(BookmarkUtilsTest, MAYBE_CutToClipboard) {
403   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
404   model->AddObserver(this);
405 
406   base::string16 title(ASCIIToUTF16("foo"));
407   GURL url("http://foo.com");
408   const BookmarkNode* n1 = model->AddURL(model->other_node(), 0, title, url);
409   const BookmarkNode* n2 = model->AddURL(model->other_node(), 1, title, url);
410 
411   // Cut the nodes to the clipboard.
412   std::vector<const BookmarkNode*> nodes;
413   nodes.push_back(n1);
414   nodes.push_back(n2);
415   CopyToClipboard(model.get(), nodes, true);
416 
417   // Make sure the nodes were removed.
418   EXPECT_EQ(0u, model->other_node()->children().size());
419 
420   // Make sure observers were notified the set of changes should be grouped.
421   ExpectGroupedChangeCount(1, 1);
422 
423   // And make sure we can paste from the clipboard.
424   EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->other_node()));
425 }
426 
TEST_F(BookmarkUtilsTest,PasteNonEditableNodes)427 TEST_F(BookmarkUtilsTest, PasteNonEditableNodes) {
428   // Load a model with an managed node that is not editable.
429   auto client = std::make_unique<TestBookmarkClient>();
430   BookmarkNode* managed_node = client->EnableManagedNode();
431 
432   std::unique_ptr<BookmarkModel> model(
433       TestBookmarkClient::CreateModelWithClient(std::move(client)));
434   const BookmarkNode* node = model->AddURL(model->other_node(),
435                                            0,
436                                            ASCIIToUTF16("foo bar"),
437                                            GURL("http://www.google.com"));
438 
439   // Copy a node to the clipboard.
440   std::vector<const BookmarkNode*> nodes;
441   nodes.push_back(node);
442   CopyToClipboard(model.get(), nodes, false);
443 
444   // And make sure we can paste a bookmark from the clipboard.
445   EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
446 
447   // But it can't be pasted into a non-editable folder.
448   BookmarkClient* upcast = model->client();
449   EXPECT_FALSE(upcast->CanBeEditedByUser(managed_node));
450   EXPECT_FALSE(CanPasteFromClipboard(model.get(), managed_node));
451 }
452 #endif  // !defined(OS_IOS)
453 
TEST_F(BookmarkUtilsTest,GetParentForNewNodes)454 TEST_F(BookmarkUtilsTest, GetParentForNewNodes) {
455   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
456   // This tests the case where selection contains one item and that item is a
457   // folder.
458   std::vector<const BookmarkNode*> nodes;
459   nodes.push_back(model->bookmark_bar_node());
460   size_t index = size_t{-1};
461   const BookmarkNode* real_parent =
462       GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
463   EXPECT_EQ(real_parent, model->bookmark_bar_node());
464   EXPECT_EQ(0u, index);
465 
466   nodes.clear();
467 
468   // This tests the case where selection contains one item and that item is an
469   // url.
470   const BookmarkNode* page1 = model->AddURL(model->bookmark_bar_node(),
471                                             0,
472                                             ASCIIToUTF16("Google"),
473                                             GURL("http://google.com"));
474   nodes.push_back(page1);
475   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
476   EXPECT_EQ(real_parent, model->bookmark_bar_node());
477   EXPECT_EQ(1u, index);
478 
479   // This tests the case where selection has more than one item.
480   const BookmarkNode* folder1 =
481       model->AddFolder(model->bookmark_bar_node(), 1, ASCIIToUTF16("Folder 1"));
482   nodes.push_back(folder1);
483   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
484   EXPECT_EQ(real_parent, model->bookmark_bar_node());
485   EXPECT_EQ(2u, index);
486 
487   // This tests the case where selection doesn't contain any items.
488   nodes.clear();
489   real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
490   EXPECT_EQ(real_parent, model->bookmark_bar_node());
491   EXPECT_EQ(2u, index);
492 }
493 
494 // Verifies that meta info is copied when nodes are cloned.
TEST_F(BookmarkUtilsTest,CloneMetaInfo)495 TEST_F(BookmarkUtilsTest, CloneMetaInfo) {
496   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
497   // Add a node containing meta info.
498   const BookmarkNode* node = model->AddURL(model->other_node(),
499                                            0,
500                                            ASCIIToUTF16("foo bar"),
501                                            GURL("http://www.google.com"));
502   model->SetNodeMetaInfo(node, "somekey", "somevalue");
503   model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
504 
505   // Clone node to a different folder.
506   const BookmarkNode* folder =
507       model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
508   std::vector<BookmarkNodeData::Element> elements;
509   BookmarkNodeData::Element node_data(node);
510   elements.push_back(node_data);
511   EXPECT_EQ(0u, folder->children().size());
512   CloneBookmarkNode(model.get(), elements, folder, 0, false);
513   ASSERT_EQ(1u, folder->children().size());
514 
515   // Verify that the cloned node contains the same meta info.
516   const BookmarkNode* clone = folder->children().front().get();
517   ASSERT_TRUE(clone->GetMetaInfoMap());
518   EXPECT_EQ(2u, clone->GetMetaInfoMap()->size());
519   std::string value;
520   EXPECT_TRUE(clone->GetMetaInfo("somekey", &value));
521   EXPECT_EQ("somevalue", value);
522   EXPECT_TRUE(clone->GetMetaInfo("someotherkey", &value));
523   EXPECT_EQ("someothervalue", value);
524 }
525 
526 // Verifies that meta info fields in the non cloned set are not copied when
527 // cloning a bookmark.
TEST_F(BookmarkUtilsTest,CloneBookmarkResetsNonClonedKey)528 TEST_F(BookmarkUtilsTest, CloneBookmarkResetsNonClonedKey) {
529   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
530   model->AddNonClonedKey("foo");
531   const BookmarkNode* parent = model->other_node();
532   const BookmarkNode* node = model->AddURL(
533       parent, 0, ASCIIToUTF16("title"), GURL("http://www.google.com"));
534   model->SetNodeMetaInfo(node, "foo", "ignored value");
535   model->SetNodeMetaInfo(node, "bar", "kept value");
536   std::vector<BookmarkNodeData::Element> elements;
537   BookmarkNodeData::Element node_data(node);
538   elements.push_back(node_data);
539 
540   // Cloning a bookmark should clear the non cloned key.
541   CloneBookmarkNode(model.get(), elements, parent, 0, true);
542   ASSERT_EQ(2u, parent->children().size());
543   std::string value;
544   EXPECT_FALSE(parent->children().front()->GetMetaInfo("foo", &value));
545 
546   // Other keys should still be cloned.
547   EXPECT_TRUE(parent->children().front()->GetMetaInfo("bar", &value));
548   EXPECT_EQ("kept value", value);
549 }
550 
551 // Verifies that meta info fields in the non cloned set are not copied when
552 // cloning a folder.
TEST_F(BookmarkUtilsTest,CloneFolderResetsNonClonedKey)553 TEST_F(BookmarkUtilsTest, CloneFolderResetsNonClonedKey) {
554   std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
555   model->AddNonClonedKey("foo");
556   const BookmarkNode* parent = model->other_node();
557   const BookmarkNode* node = model->AddFolder(parent, 0, ASCIIToUTF16("title"));
558   model->SetNodeMetaInfo(node, "foo", "ignored value");
559   model->SetNodeMetaInfo(node, "bar", "kept value");
560   std::vector<BookmarkNodeData::Element> elements;
561   BookmarkNodeData::Element node_data(node);
562   elements.push_back(node_data);
563 
564   // Cloning a folder should clear the non cloned key.
565   CloneBookmarkNode(model.get(), elements, parent, 0, true);
566   ASSERT_EQ(2u, parent->children().size());
567   std::string value;
568   EXPECT_FALSE(parent->children().front()->GetMetaInfo("foo", &value));
569 
570   // Other keys should still be cloned.
571   EXPECT_TRUE(parent->children().front()->GetMetaInfo("bar", &value));
572   EXPECT_EQ("kept value", value);
573 }
574 
TEST_F(BookmarkUtilsTest,RemoveAllBookmarks)575 TEST_F(BookmarkUtilsTest, RemoveAllBookmarks) {
576   // Load a model with an managed node that is not editable.
577   auto client = std::make_unique<TestBookmarkClient>();
578   BookmarkNode* managed_node = client->EnableManagedNode();
579 
580   std::unique_ptr<BookmarkModel> model(
581       TestBookmarkClient::CreateModelWithClient(std::move(client)));
582   EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
583   EXPECT_TRUE(model->other_node()->children().empty());
584   EXPECT_TRUE(model->mobile_node()->children().empty());
585   EXPECT_TRUE(managed_node->children().empty());
586 
587   const base::string16 title = base::ASCIIToUTF16("Title");
588   const GURL url("http://google.com");
589   model->AddURL(model->bookmark_bar_node(), 0, title, url);
590   model->AddURL(model->other_node(), 0, title, url);
591   model->AddURL(model->mobile_node(), 0, title, url);
592   model->AddURL(managed_node, 0, title, url);
593 
594   std::vector<const BookmarkNode*> nodes;
595   model->GetNodesByURL(url, &nodes);
596   ASSERT_EQ(4u, nodes.size());
597 
598   RemoveAllBookmarks(model.get(), url);
599 
600   nodes.clear();
601   model->GetNodesByURL(url, &nodes);
602   ASSERT_EQ(1u, nodes.size());
603   EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
604   EXPECT_TRUE(model->other_node()->children().empty());
605   EXPECT_TRUE(model->mobile_node()->children().empty());
606   EXPECT_EQ(1u, managed_node->children().size());
607 }
608 
609 }  // namespace
610 }  // namespace bookmarks
611