• 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 <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 
HasOrdinalNumber(const FilePath::StringType & filename)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:
SavePackageTest()66   SavePackageTest() {
67   }
68 
GetGeneratedFilename(bool need_success_generate_filename,const std::string & disposition,const std::string & url,bool need_htm_ext,FilePath::StringType * generated_name)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 
EnsureHtmlExtension(const FilePath & name)83   FilePath EnsureHtmlExtension(const FilePath& name) {
84     return SavePackage::EnsureHtmlExtension(name);
85   }
86 
EnsureMimeExtension(const FilePath & name,const std::string & content_mime_type)87   FilePath EnsureMimeExtension(const FilePath& name,
88                                const std::string& content_mime_type) {
89     return SavePackage::EnsureMimeExtension(name, content_mime_type);
90   }
91 
GetUrlToBeSaved()92   GURL GetUrlToBeSaved() {
93     return save_package_success_->GetUrlToBeSaved();
94   }
95 
96  protected:
SetUp()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 
TEST_F(SavePackageTest,TestSuccessfullyGenerateSavePackageFilename)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 
TEST_F(SavePackageTest,TestUnSuccessfullyGenerateSavePackageFilename)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
TEST_F(SavePackageTest,MAYBE_TestLongSavePackageFilename)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
TEST_F(SavePackageTest,MAYBE_TestLongSafePureFilename)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
TEST_F(SavePackageTest,MAYBE_TestEnsureHtmlExtension)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
TEST_F(SavePackageTest,MAYBE_TestEnsureMimeExtension)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
TEST_F(SavePackageTest,MAYBE_TestSuggestedSaveNames)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.
TEST_F(SavePackageTest,TestGetUrlToBeSaved)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
TEST_F(SavePackageTest,TestGetUrlToBeSavedViewSource)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