Home | History | Annotate | Download | only in 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 <string>
      6 
      7 #include "base/file_path.h"
      8 #include "base/path_service.h"
      9 #include "base/memory/scoped_temp_dir.h"
     10 #include "base/string_util.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/download/save_package.h"
     13 #include "chrome/browser/net/url_request_mock_http_job.h"
     14 #include "content/browser/renderer_host/test_render_view_host.h"
     15 #include "content/browser/tab_contents/test_tab_contents.h"
     16 #include "googleurl/src/gurl.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 #define FPL FILE_PATH_LITERAL
     20 #if defined(OS_WIN)
     21 #define HTML_EXTENSION ".htm"
     22 // This second define is needed because MSVC is broken.
     23 #define FPL_HTML_EXTENSION L".htm"
     24 #else
     25 #define HTML_EXTENSION ".html"
     26 #define FPL_HTML_EXTENSION ".html"
     27 #endif
     28 
     29 namespace {
     30 
     31 // This constant copied from save_package.cc.
     32 #if defined(OS_WIN)
     33 const uint32 kMaxFilePathLength = MAX_PATH - 1;
     34 const uint32 kMaxFileNameLength = MAX_PATH - 1;
     35 #elif defined(OS_POSIX)
     36 const uint32 kMaxFilePathLength = PATH_MAX - 1;
     37 const uint32 kMaxFileNameLength = NAME_MAX;
     38 #endif
     39 
     40 // Used to make long filenames.
     41 std::string long_file_name(
     42     "EFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz01234567"
     43     "89ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz012345"
     44     "6789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz0123"
     45     "456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789a");
     46 
     47 bool HasOrdinalNumber(const FilePath::StringType& filename) {
     48   FilePath::StringType::size_type r_paren_index = filename.rfind(FPL(')'));
     49   FilePath::StringType::size_type l_paren_index = filename.rfind(FPL('('));
     50   if (l_paren_index >= r_paren_index)
     51     return false;
     52 
     53   for (FilePath::StringType::size_type i = l_paren_index + 1;
     54        i != r_paren_index; ++i) {
     55     if (!IsAsciiDigit(filename[i]))
     56       return false;
     57   }
     58 
     59   return true;
     60 }
     61 
     62 }  // namespace
     63 
     64 class SavePackageTest : public RenderViewHostTestHarness {
     65  public:
     66   SavePackageTest() {
     67   }
     68 
     69   bool GetGeneratedFilename(bool need_success_generate_filename,
     70                             const std::string& disposition,
     71                             const std::string& url,
     72                             bool need_htm_ext,
     73                             FilePath::StringType* generated_name) {
     74     SavePackage* save_package;
     75     if (need_success_generate_filename)
     76       save_package = save_package_success_.get();
     77     else
     78       save_package = save_package_fail_.get();
     79     return save_package->GenerateFileName(disposition, GURL(url), need_htm_ext,
     80                                           generated_name);
     81   }
     82 
     83   FilePath EnsureHtmlExtension(const FilePath& name) {
     84     return SavePackage::EnsureHtmlExtension(name);
     85   }
     86 
     87   FilePath EnsureMimeExtension(const FilePath& name,
     88                                const std::string& content_mime_type) {
     89     return SavePackage::EnsureMimeExtension(name, content_mime_type);
     90   }
     91 
     92   GURL GetUrlToBeSaved() {
     93     return save_package_success_->GetUrlToBeSaved();
     94   }
     95 
     96  protected:
     97   virtual void SetUp() {
     98     RenderViewHostTestHarness::SetUp();
     99 
    100     // Do the initialization in SetUp so contents() is initialized by
    101     // RenderViewHostTestHarness::SetUp.
    102     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    103 
    104     save_package_success_ = new SavePackage(contents(),
    105         temp_dir_.path().AppendASCII("testfile" HTML_EXTENSION),
    106         temp_dir_.path().AppendASCII("testfile_files"));
    107 
    108     // We need to construct a path that is *almost* kMaxFilePathLength long
    109     long_file_name.resize(kMaxFilePathLength + long_file_name.length());
    110     while (long_file_name.length() < kMaxFilePathLength)
    111       long_file_name += long_file_name;
    112     long_file_name.resize(
    113         kMaxFilePathLength - 9 - temp_dir_.path().value().length());
    114 
    115     save_package_fail_ = new SavePackage(contents(),
    116         temp_dir_.path().AppendASCII(long_file_name + HTML_EXTENSION),
    117         temp_dir_.path().AppendASCII(long_file_name + "_files"));
    118   }
    119 
    120  private:
    121   // SavePackage for successfully generating file name.
    122   scoped_refptr<SavePackage> save_package_success_;
    123   // SavePackage for failed generating file name.
    124   scoped_refptr<SavePackage> save_package_fail_;
    125 
    126   ScopedTempDir temp_dir_;
    127 
    128   DISALLOW_COPY_AND_ASSIGN(SavePackageTest);
    129 };
    130 
    131 static const struct {
    132   const char* disposition;
    133   const char* url;
    134   const FilePath::CharType* expected_name;
    135   bool need_htm_ext;
    136 } kGeneratedFiles[] = {
    137   // We mainly focus on testing duplicated names here, since retrieving file
    138   // name from disposition and url has been tested in DownloadManagerTest.
    139 
    140   // No useful information in disposition or URL, use default.
    141   {"1.html", "http://www.savepage.com/",
    142     FPL("saved_resource") FPL_HTML_EXTENSION, true},
    143 
    144   // No duplicate occurs.
    145   {"filename=1.css", "http://www.savepage.com", FPL("1.css"), false},
    146 
    147   // No duplicate occurs.
    148   {"filename=1.js", "http://www.savepage.com", FPL("1.js"), false},
    149 
    150   // Append numbers for duplicated names.
    151   {"filename=1.css", "http://www.savepage.com", FPL("1(1).css"), false},
    152 
    153   // No duplicate occurs.
    154   {"filename=1(1).js", "http://www.savepage.com", FPL("1(1).js"), false},
    155 
    156   // Append numbers for duplicated names.
    157   {"filename=1.css", "http://www.savepage.com", FPL("1(2).css"), false},
    158 
    159   // Change number for duplicated names.
    160   {"filename=1(1).css", "http://www.savepage.com", FPL("1(3).css"), false},
    161 
    162   // No duplicate occurs.
    163   {"filename=1(11).css", "http://www.savepage.com", FPL("1(11).css"), false},
    164 };
    165 
    166 TEST_F(SavePackageTest, TestSuccessfullyGenerateSavePackageFilename) {
    167   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kGeneratedFiles); ++i) {
    168     FilePath::StringType file_name;
    169     bool ok = GetGeneratedFilename(true,
    170                                    kGeneratedFiles[i].disposition,
    171                                    kGeneratedFiles[i].url,
    172                                    kGeneratedFiles[i].need_htm_ext,
    173                                    &file_name);
    174     ASSERT_TRUE(ok);
    175     EXPECT_EQ(kGeneratedFiles[i].expected_name, file_name);
    176   }
    177 }
    178 
    179 TEST_F(SavePackageTest, TestUnSuccessfullyGenerateSavePackageFilename) {
    180   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kGeneratedFiles); ++i) {
    181     FilePath::StringType file_name;
    182     bool ok = GetGeneratedFilename(false,
    183                                    kGeneratedFiles[i].disposition,
    184                                    kGeneratedFiles[i].url,
    185                                    kGeneratedFiles[i].need_htm_ext,
    186                                    &file_name);
    187     ASSERT_FALSE(ok);
    188   }
    189 }
    190 
    191 // Crashing on Windows, see http://crbug.com/79365
    192 #if defined(OS_WIN)
    193 #define MAYBE_TestLongSavePackageFilename DISABLED_TestLongSavePackageFilename
    194 #else
    195 #define MAYBE_TestLongSavePackageFilename TestLongSavePackageFilename
    196 #endif
    197 TEST_F(SavePackageTest, MAYBE_TestLongSavePackageFilename) {
    198   const std::string base_url("http://www.google.com/");
    199   const std::string long_file = long_file_name + ".css";
    200   const std::string url = base_url + long_file;
    201 
    202   FilePath::StringType filename;
    203   // Test that the filename is successfully shortened to fit.
    204   ASSERT_TRUE(GetGeneratedFilename(true, "", url, false, &filename));
    205   EXPECT_TRUE(filename.length() < long_file.length());
    206   EXPECT_FALSE(HasOrdinalNumber(filename));
    207 
    208   // Test that the filename is successfully shortened to fit, and gets an
    209   // an ordinal appended.
    210   ASSERT_TRUE(GetGeneratedFilename(true, "", url, false, &filename));
    211   EXPECT_TRUE(filename.length() < long_file.length());
    212   EXPECT_TRUE(HasOrdinalNumber(filename));
    213 
    214   // Test that the filename is successfully shortened to fit, and gets a
    215   // different ordinal appended.
    216   FilePath::StringType filename2;
    217   ASSERT_TRUE(GetGeneratedFilename(true, "", url, false, &filename2));
    218   EXPECT_TRUE(filename2.length() < long_file.length());
    219   EXPECT_TRUE(HasOrdinalNumber(filename2));
    220   EXPECT_NE(filename, filename2);
    221 }
    222 
    223 // Crashing on Windows, see http://crbug.com/79365
    224 #if defined(OS_WIN)
    225 #define MAYBE_TestLongSafePureFilename DISABLED_TestLongSafePureFilename
    226 #else
    227 #define MAYBE_TestLongSafePureFilename TestLongSafePureFilename
    228 #endif
    229 TEST_F(SavePackageTest, MAYBE_TestLongSafePureFilename) {
    230   const FilePath save_dir(FPL("test_dir"));
    231   const FilePath::StringType ext(FPL_HTML_EXTENSION);
    232   FilePath::StringType filename =
    233 #if defined(OS_WIN)
    234       ASCIIToWide(long_file_name);
    235 #else
    236       long_file_name;
    237 #endif
    238 
    239   // Test that the filename + extension doesn't exceed kMaxFileNameLength
    240   uint32 max_path = SavePackage::GetMaxPathLengthForDirectory(save_dir);
    241   ASSERT_TRUE(SavePackage::GetSafePureFileName(save_dir, ext, max_path,
    242                                                &filename));
    243   EXPECT_TRUE(filename.length() <= kMaxFileNameLength-ext.length());
    244 }
    245 
    246 static const struct {
    247   const FilePath::CharType* page_title;
    248   const FilePath::CharType* expected_name;
    249 } kExtensionTestCases[] = {
    250   // Extension is preserved if it is already proper for HTML.
    251   {FPL("filename.html"), FPL("filename.html")},
    252   {FPL("filename.HTML"), FPL("filename.HTML")},
    253   {FPL("filename.XHTML"), FPL("filename.XHTML")},
    254   {FPL("filename.xhtml"), FPL("filename.xhtml")},
    255   {FPL("filename.htm"), FPL("filename.htm")},
    256   // ".htm" is added if the extension is improper for HTML.
    257   {FPL("hello.world"), FPL("hello.world") FPL_HTML_EXTENSION},
    258   {FPL("hello.txt"), FPL("hello.txt") FPL_HTML_EXTENSION},
    259   {FPL("is.html.good"), FPL("is.html.good") FPL_HTML_EXTENSION},
    260   // ".htm" is added if the name doesn't have an extension.
    261   {FPL("helloworld"), FPL("helloworld") FPL_HTML_EXTENSION},
    262   {FPL("helloworld."), FPL("helloworld.") FPL_HTML_EXTENSION},
    263 };
    264 
    265 // Crashing on Windows, see http://crbug.com/79365
    266 #if defined(OS_WIN)
    267 #define MAYBE_TestEnsureHtmlExtension DISABLED_TestEnsureHtmlExtension
    268 #else
    269 #define MAYBE_TestEnsureHtmlExtension TestEnsureHtmlExtension
    270 #endif
    271 TEST_F(SavePackageTest, MAYBE_TestEnsureHtmlExtension) {
    272   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExtensionTestCases); ++i) {
    273     FilePath original = FilePath(kExtensionTestCases[i].page_title);
    274     FilePath expected = FilePath(kExtensionTestCases[i].expected_name);
    275     FilePath actual = EnsureHtmlExtension(original);
    276     EXPECT_EQ(expected.value(), actual.value()) << "Failed for page title: " <<
    277         kExtensionTestCases[i].page_title;
    278   }
    279 }
    280 
    281 // Crashing on Windows, see http://crbug.com/79365
    282 #if defined(OS_WIN)
    283 #define MAYBE_TestEnsureMimeExtension DISABLED_TestEnsureMimeExtension
    284 #else
    285 #define MAYBE_TestEnsureMimeExtension TestEnsureMimeExtension
    286 #endif
    287 TEST_F(SavePackageTest, MAYBE_TestEnsureMimeExtension) {
    288   static const struct {
    289     const FilePath::CharType* page_title;
    290     const FilePath::CharType* expected_name;
    291     const char* contents_mime_type;
    292   } kExtensionTests[] = {
    293     { FPL("filename.html"), FPL("filename.html"), "text/html" },
    294     { FPL("filename.htm"), FPL("filename.htm"), "text/html" },
    295     { FPL("filename.xhtml"), FPL("filename.xhtml"), "text/html" },
    296 #if defined(OS_WIN)
    297     { FPL("filename"), FPL("filename.htm"), "text/html" },
    298 #else  // defined(OS_WIN)
    299     { FPL("filename"), FPL("filename.html"), "text/html" },
    300 #endif  // defined(OS_WIN)
    301     { FPL("filename.html"), FPL("filename.html"), "text/xml" },
    302     { FPL("filename.xml"), FPL("filename.xml"), "text/xml" },
    303     { FPL("filename"), FPL("filename.xml"), "text/xml" },
    304     { FPL("filename.xhtml"), FPL("filename.xhtml"),
    305       "application/xhtml+xml" },
    306     { FPL("filename.html"), FPL("filename.html"),
    307       "application/xhtml+xml" },
    308     { FPL("filename"), FPL("filename.xhtml"), "application/xhtml+xml" },
    309     { FPL("filename.txt"), FPL("filename.txt"), "text/plain" },
    310     { FPL("filename"), FPL("filename.txt"), "text/plain" },
    311     { FPL("filename.css"), FPL("filename.css"), "text/css" },
    312     { FPL("filename"), FPL("filename.css"), "text/css" },
    313     { FPL("filename.abc"), FPL("filename.abc"), "unknown/unknown" },
    314     { FPL("filename"), FPL("filename"), "unknown/unknown" },
    315   };
    316   for (uint32 i = 0; i < ARRAYSIZE_UNSAFE(kExtensionTests); ++i) {
    317     FilePath original = FilePath(kExtensionTests[i].page_title);
    318     FilePath expected = FilePath(kExtensionTests[i].expected_name);
    319     std::string mime_type(kExtensionTests[i].contents_mime_type);
    320     FilePath actual = EnsureMimeExtension(original, mime_type);
    321     EXPECT_EQ(expected.value(), actual.value()) << "Failed for page title: " <<
    322         kExtensionTests[i].page_title << " MIME:" << mime_type;
    323   }
    324 }
    325 
    326 // Test that the suggested names generated by SavePackage are reasonable:
    327 // If the name is a URL, retrieve only the path component since the path name
    328 // generation code will turn the entire URL into the file name leading to bad
    329 // extension names. For example, a page with no title and a URL:
    330 // http://www.foo.com/a/path/name.txt will turn into file:
    331 // "http www.foo.com a path name.txt", when we want to save it as "name.txt".
    332 
    333 static const struct SuggestedSaveNameTestCase {
    334   const char* page_url;
    335   const string16 page_title;
    336   const FilePath::CharType* expected_name;
    337   bool ensure_html_extension;
    338 } kSuggestedSaveNames[] = {
    339   // Title overrides the URL.
    340   { "http://foo.com",
    341     ASCIIToUTF16("A page title"),
    342     FPL("A page title") FPL_HTML_EXTENSION,
    343     true
    344   },
    345   // Extension is preserved.
    346   { "http://foo.com",
    347     ASCIIToUTF16("A page title with.ext"),
    348     FPL("A page title with.ext"),
    349     false
    350   },
    351   // If the title matches the URL, use the last component of the URL.
    352   { "http://foo.com/bar",
    353     ASCIIToUTF16("http://foo.com/bar"),
    354     FPL("bar"),
    355     false
    356   },
    357   // If the title matches the URL, but there is no "filename" component,
    358   // use the domain.
    359   { "http://foo.com",
    360     ASCIIToUTF16("http://foo.com"),
    361     FPL("foo.com"),
    362     false
    363   },
    364   // Make sure fuzzy matching works.
    365   { "http://foo.com/bar",
    366     ASCIIToUTF16("foo.com/bar"),
    367     FPL("bar"),
    368     false
    369   },
    370   // A URL-like title that does not match the title is respected in full.
    371   { "http://foo.com",
    372     ASCIIToUTF16("http://www.foo.com/path/title.txt"),
    373     FPL("http   www.foo.com path title.txt"),
    374     false
    375   },
    376 };
    377 
    378 // Crashing on Windows, see http://crbug.com/79365
    379 #if defined(OS_WIN)
    380 #define MAYBE_TestSuggestedSaveNames DISABLED_TestSuggestedSaveNames
    381 #else
    382 #define MAYBE_TestSuggestedSaveNames TestSuggestedSaveNames
    383 #endif
    384 TEST_F(SavePackageTest, MAYBE_TestSuggestedSaveNames) {
    385   for (size_t i = 0; i < arraysize(kSuggestedSaveNames); ++i) {
    386     scoped_refptr<SavePackage> save_package(
    387         new SavePackage(contents(), FilePath(), FilePath()));
    388     save_package->page_url_ = GURL(kSuggestedSaveNames[i].page_url);
    389     save_package->title_ = kSuggestedSaveNames[i].page_title;
    390 
    391     FilePath save_name = save_package->GetSuggestedNameForSaveAs(
    392         kSuggestedSaveNames[i].ensure_html_extension,
    393         std::string());
    394     EXPECT_EQ(kSuggestedSaveNames[i].expected_name, save_name.value()) <<
    395         "Test case " << i;
    396   }
    397 }
    398 
    399 static const FilePath::CharType* kTestDir = FILE_PATH_LITERAL("save_page");
    400 
    401 // GetUrlToBeSaved method should return correct url to be saved.
    402 TEST_F(SavePackageTest, TestGetUrlToBeSaved) {
    403   FilePath file_name(FILE_PATH_LITERAL("a.htm"));
    404   GURL url = URLRequestMockHTTPJob::GetMockUrl(
    405                  FilePath(kTestDir).Append(file_name));
    406   NavigateAndCommit(url);
    407   EXPECT_EQ(url, GetUrlToBeSaved());
    408 }
    409 
    410 // GetUrlToBeSaved method sould return actual url to be saved,
    411 // instead of the displayed url used to view source of a page.
    412 // Ex:GetUrlToBeSaved method should return http://www.google.com
    413 // when user types view-source:http://www.google.com
    414 TEST_F(SavePackageTest, TestGetUrlToBeSavedViewSource) {
    415   FilePath file_name(FILE_PATH_LITERAL("a.htm"));
    416   GURL view_source_url = URLRequestMockHTTPJob::GetMockViewSourceUrl(
    417                              FilePath(kTestDir).Append(file_name));
    418   GURL actual_url = URLRequestMockHTTPJob::GetMockUrl(
    419                         FilePath(kTestDir).Append(file_name));
    420   NavigateAndCommit(view_source_url);
    421   EXPECT_EQ(actual_url, GetUrlToBeSaved());
    422   EXPECT_EQ(view_source_url, contents()->GetURL());
    423 }
    424