Home | History | Annotate | Download | only in bookmarks
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/bookmarks/bookmark_html_writer.h"
      6 
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/i18n/time_formatting.h"
      9 #include "base/path_service.h"
     10 #include "base/run_loop.h"
     11 #include "base/strings/string16.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/time/time.h"
     15 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     16 #include "chrome/browser/favicon/favicon_service.h"
     17 #include "chrome/browser/favicon/favicon_service_factory.h"
     18 #include "chrome/browser/history/history_service.h"
     19 #include "chrome/browser/history/history_service_factory.h"
     20 #include "chrome/common/importer/imported_bookmark_entry.h"
     21 #include "chrome/common/importer/imported_favicon_usage.h"
     22 #include "chrome/test/base/testing_profile.h"
     23 #include "chrome/utility/importer/bookmark_html_reader.h"
     24 #include "components/bookmarks/browser/bookmark_model.h"
     25 #include "components/bookmarks/test/bookmark_test_helpers.h"
     26 #include "content/public/test/test_browser_thread_bundle.h"
     27 #include "grit/components_strings.h"
     28 #include "testing/gtest/include/gtest/gtest.h"
     29 #include "third_party/skia/include/core/SkBitmap.h"
     30 #include "ui/base/l10n/l10n_util.h"
     31 #include "ui/gfx/codec/png_codec.h"
     32 
     33 namespace {
     34 
     35 const int kIconWidth = 16;
     36 const int kIconHeight = 16;
     37 
     38 void MakeTestSkBitmap(int w, int h, SkBitmap* bmp) {
     39   bmp->allocN32Pixels(w, h);
     40 
     41   uint32_t* src_data = bmp->getAddr32(0, 0);
     42   for (int i = 0; i < w * h; i++) {
     43     src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
     44   }
     45 }
     46 
     47 }  // namespace
     48 
     49 class BookmarkHTMLWriterTest : public testing::Test {
     50  protected:
     51   virtual void SetUp() {
     52     testing::Test::SetUp();
     53     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     54     path_ = temp_dir_.path().AppendASCII("bookmarks.html");
     55   }
     56 
     57   // Converts an ImportedBookmarkEntry to a string suitable for assertion
     58   // testing.
     59   base::string16 BookmarkEntryToString(const ImportedBookmarkEntry& entry) {
     60     base::string16 result;
     61     result.append(base::ASCIIToUTF16("on_toolbar="));
     62     if (entry.in_toolbar)
     63       result.append(base::ASCIIToUTF16("true"));
     64     else
     65       result.append(base::ASCIIToUTF16("false"));
     66 
     67     result.append(base::ASCIIToUTF16(" url=") +
     68         base::UTF8ToUTF16(entry.url.spec()));
     69 
     70     result.append(base::ASCIIToUTF16(" path="));
     71     for (size_t i = 0; i < entry.path.size(); ++i) {
     72       if (i != 0)
     73         result.append(base::ASCIIToUTF16("/"));
     74       result.append(entry.path[i]);
     75     }
     76 
     77     result.append(base::ASCIIToUTF16(" title="));
     78     result.append(entry.title);
     79 
     80     result.append(base::ASCIIToUTF16(" time="));
     81     result.append(base::TimeFormatFriendlyDateAndTime(entry.creation_time));
     82     return result;
     83   }
     84 
     85   // Creates a set of bookmark values to a string for assertion testing.
     86   base::string16 BookmarkValuesToString(bool on_toolbar,
     87                                   const GURL& url,
     88                                   const base::string16& title,
     89                                   base::Time creation_time,
     90                                   const base::string16& f1,
     91                                   const base::string16& f2,
     92                                   const base::string16& f3) {
     93     ImportedBookmarkEntry entry;
     94     entry.in_toolbar = on_toolbar;
     95     entry.url = url;
     96     if (!f1.empty()) {
     97       entry.path.push_back(f1);
     98       if (!f2.empty()) {
     99         entry.path.push_back(f2);
    100         if (!f3.empty())
    101           entry.path.push_back(f3);
    102       }
    103     }
    104     entry.title = title;
    105     entry.creation_time = creation_time;
    106     return BookmarkEntryToString(entry);
    107   }
    108 
    109   void AssertBookmarkEntryEquals(const ImportedBookmarkEntry& entry,
    110                                  bool on_toolbar,
    111                                  const GURL& url,
    112                                  const base::string16& title,
    113                                  base::Time creation_time,
    114                                  const base::string16& f1,
    115                                  const base::string16& f2,
    116                                  const base::string16& f3) {
    117     EXPECT_EQ(BookmarkValuesToString(on_toolbar, url, title, creation_time,
    118                                      f1, f2, f3),
    119               BookmarkEntryToString(entry));
    120   }
    121 
    122   base::ScopedTempDir temp_dir_;
    123   base::FilePath path_;
    124 };
    125 
    126 // Class that will notify message loop when file is written.
    127 class BookmarksObserver : public BookmarksExportObserver {
    128  public:
    129   explicit BookmarksObserver(base::RunLoop* loop) : loop_(loop) {
    130     DCHECK(loop);
    131   }
    132 
    133   virtual void OnExportFinished() OVERRIDE {
    134     loop_->Quit();
    135   }
    136 
    137  private:
    138   base::RunLoop* loop_;
    139 
    140   DISALLOW_COPY_AND_ASSIGN(BookmarksObserver);
    141 };
    142 
    143 // Tests bookmark_html_writer by populating a BookmarkModel, writing it out by
    144 // way of bookmark_html_writer, then using the importer to read it back in.
    145 TEST_F(BookmarkHTMLWriterTest, Test) {
    146   content::TestBrowserThreadBundle thread_bundle;
    147 
    148   TestingProfile profile;
    149   ASSERT_TRUE(profile.CreateHistoryService(true, false));
    150   profile.BlockUntilHistoryProcessesPendingRequests();
    151   profile.CreateFaviconService();
    152   profile.CreateBookmarkModel(true);
    153 
    154   BookmarkModel* model = BookmarkModelFactory::GetForProfile(&profile);
    155   test::WaitForBookmarkModelToLoad(model);
    156 
    157   // Create test PNG representing favicon for url1.
    158   SkBitmap bitmap;
    159   MakeTestSkBitmap(kIconWidth, kIconHeight, &bitmap);
    160   std::vector<unsigned char> icon_data;
    161   gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &icon_data);
    162 
    163   // Populate the BookmarkModel. This creates the following bookmark structure:
    164   // Bookmarks bar
    165   //   F1
    166   //     url1
    167   //     F2
    168   //       url2
    169   //   url3
    170   //   url4
    171   // Other
    172   //   url1
    173   //   url2
    174   //   F3
    175   //     F4
    176   //       url1
    177   // Mobile
    178   //   url1
    179   //   <bookmark without a title.>
    180   base::string16 f1_title = base::ASCIIToUTF16("F\"&;<1\"");
    181   base::string16 f2_title = base::ASCIIToUTF16("F2");
    182   base::string16 f3_title = base::ASCIIToUTF16("F 3");
    183   base::string16 f4_title = base::ASCIIToUTF16("F4");
    184   base::string16 url1_title = base::ASCIIToUTF16("url 1");
    185   base::string16 url2_title = base::ASCIIToUTF16("url&2");
    186   base::string16 url3_title = base::ASCIIToUTF16("url\"3");
    187   base::string16 url4_title = base::ASCIIToUTF16("url\"&;");
    188   base::string16 unnamed_bookmark_title = base::ASCIIToUTF16("");
    189   GURL url1("http://url1");
    190   GURL url1_favicon("http://url1/icon.ico");
    191   GURL url2("http://url2");
    192   GURL url3("http://url3");
    193   GURL url4("javascript:alert(\"Hello!\");");
    194   GURL unnamed_bookmark_url("about:blank");
    195   base::Time t1(base::Time::Now());
    196   base::Time t2(t1 + base::TimeDelta::FromHours(1));
    197   base::Time t3(t1 + base::TimeDelta::FromHours(1));
    198   base::Time t4(t1 + base::TimeDelta::FromHours(1));
    199   const BookmarkNode* f1 = model->AddFolder(
    200       model->bookmark_bar_node(), 0, f1_title);
    201   model->AddURLWithCreationTimeAndMetaInfo(f1, 0, url1_title, url1, t1, NULL);
    202   HistoryServiceFactory::GetForProfile(&profile, Profile::EXPLICIT_ACCESS)->
    203       AddPage(url1, base::Time::Now(), history::SOURCE_BROWSED);
    204   FaviconServiceFactory::GetForProfile(&profile, Profile::EXPLICIT_ACCESS)
    205       ->SetFavicons(url1,
    206                     url1_favicon,
    207                     favicon_base::FAVICON,
    208                     gfx::Image::CreateFrom1xBitmap(bitmap));
    209   const BookmarkNode* f2 = model->AddFolder(f1, 1, f2_title);
    210   model->AddURLWithCreationTimeAndMetaInfo(f2, 0, url2_title, url2, t2, NULL);
    211   model->AddURLWithCreationTimeAndMetaInfo(
    212       model->bookmark_bar_node(), 1, url3_title, url3, t3, NULL);
    213 
    214   model->AddURLWithCreationTimeAndMetaInfo(
    215       model->other_node(), 0, url1_title, url1, t1, NULL);
    216   model->AddURLWithCreationTimeAndMetaInfo(
    217       model->other_node(), 1, url2_title, url2, t2, NULL);
    218   const BookmarkNode* f3 = model->AddFolder(model->other_node(), 2, f3_title);
    219   const BookmarkNode* f4 = model->AddFolder(f3, 0, f4_title);
    220   model->AddURLWithCreationTimeAndMetaInfo(f4, 0, url1_title, url1, t1, NULL);
    221   model->AddURLWithCreationTimeAndMetaInfo(
    222       model->bookmark_bar_node(), 2, url4_title, url4, t4, NULL);
    223   model->AddURLWithCreationTimeAndMetaInfo(
    224       model->mobile_node(), 0, url1_title, url1, t1, NULL);
    225   model->AddURLWithCreationTimeAndMetaInfo(model->mobile_node(),
    226                                            1,
    227                                            unnamed_bookmark_title,
    228                                            unnamed_bookmark_url,
    229                                            t2,
    230                                            NULL);
    231 
    232   base::RunLoop run_loop;
    233 
    234   // Write to a temp file.
    235   BookmarksObserver observer(&run_loop);
    236   bookmark_html_writer::WriteBookmarks(&profile, path_, &observer);
    237   run_loop.Run();
    238 
    239   // Clear favicon so that it would be read from file.
    240   FaviconServiceFactory::GetForProfile(&profile, Profile::EXPLICIT_ACCESS)
    241       ->SetFavicons(url1, url1_favicon, favicon_base::FAVICON, gfx::Image());
    242 
    243   // Read the bookmarks back in.
    244   std::vector<ImportedBookmarkEntry> parsed_bookmarks;
    245   std::vector<ImportedFaviconUsage> favicons;
    246   bookmark_html_reader::ImportBookmarksFile(base::Callback<bool(void)>(),
    247                                             base::Callback<bool(const GURL&)>(),
    248                                             path_,
    249                                             &parsed_bookmarks,
    250                                             &favicons);
    251 
    252   // Check loaded favicon (url1 is represented by 4 separate bookmarks).
    253   EXPECT_EQ(4U, favicons.size());
    254   for (size_t i = 0; i < favicons.size(); i++) {
    255     if (url1_favicon == favicons[i].favicon_url) {
    256       EXPECT_EQ(1U, favicons[i].urls.size());
    257       std::set<GURL>::const_iterator iter = favicons[i].urls.find(url1);
    258       ASSERT_TRUE(iter != favicons[i].urls.end());
    259       ASSERT_TRUE(*iter == url1);
    260       ASSERT_TRUE(favicons[i].png_data == icon_data);
    261     }
    262   }
    263 
    264   // Verify we got back what we wrote.
    265   ASSERT_EQ(9U, parsed_bookmarks.size());
    266   // Windows and ChromeOS builds use Sentence case.
    267   base::string16 bookmark_folder_name =
    268       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_FOLDER_NAME);
    269   AssertBookmarkEntryEquals(parsed_bookmarks[0], true, url1, url1_title, t1,
    270                             bookmark_folder_name, f1_title, base::string16());
    271   AssertBookmarkEntryEquals(parsed_bookmarks[1], true, url2, url2_title, t2,
    272                             bookmark_folder_name, f1_title, f2_title);
    273   AssertBookmarkEntryEquals(parsed_bookmarks[2], true, url3, url3_title, t3,
    274                             bookmark_folder_name, base::string16(),
    275                             base::string16());
    276   AssertBookmarkEntryEquals(parsed_bookmarks[3], true, url4, url4_title, t4,
    277                             bookmark_folder_name, base::string16(),
    278                             base::string16());
    279   AssertBookmarkEntryEquals(parsed_bookmarks[4], false, url1, url1_title, t1,
    280                             base::string16(), base::string16(),
    281                             base::string16());
    282   AssertBookmarkEntryEquals(parsed_bookmarks[5], false, url2, url2_title, t2,
    283                             base::string16(), base::string16(),
    284                             base::string16());
    285   AssertBookmarkEntryEquals(parsed_bookmarks[6], false, url1, url1_title, t1,
    286                             f3_title, f4_title, base::string16());
    287   AssertBookmarkEntryEquals(parsed_bookmarks[7], false, url1, url1_title, t1,
    288                             base::string16(), base::string16(),
    289                             base::string16());
    290   AssertBookmarkEntryEquals(parsed_bookmarks[8], false, unnamed_bookmark_url,
    291                             unnamed_bookmark_title, t2,
    292                             base::string16(), base::string16(),
    293                             base::string16());
    294 }
    295