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 "base/bind.h"
6#include "base/bind_helpers.h"
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/files/file_path.h"
10#include "base/files/scoped_temp_dir.h"
11#include "base/path_service.h"
12#include "base/prefs/pref_member.h"
13#include "base/prefs/pref_service.h"
14#include "base/test/test_file_util.h"
15#include "chrome/app/chrome_command_ids.h"
16#include "chrome/browser/download/chrome_download_manager_delegate.h"
17#include "chrome/browser/download/download_history.h"
18#include "chrome/browser/download/download_prefs.h"
19#include "chrome/browser/download/download_service.h"
20#include "chrome/browser/download/download_service_factory.h"
21#include "chrome/browser/download/save_package_file_picker.h"
22#include "chrome/browser/history/download_row.h"
23#include "chrome/browser/net/url_request_mock_util.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_commands.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/tabs/tab_strip_model.h"
29#include "chrome/common/chrome_paths.h"
30#include "chrome/common/chrome_switches.h"
31#include "chrome/common/pref_names.h"
32#include "chrome/common/url_constants.h"
33#include "chrome/test/base/in_process_browser_test.h"
34#include "chrome/test/base/ui_test_utils.h"
35#include "content/public/browser/download_item.h"
36#include "content/public/browser/download_manager.h"
37#include "content/public/browser/notification_service.h"
38#include "content/public/browser/notification_types.h"
39#include "content/public/browser/web_contents.h"
40#include "content/public/test/test_utils.h"
41#include "content/test/net/url_request_mock_http_job.h"
42#include "testing/gtest/include/gtest/gtest.h"
43
44using content::BrowserContext;
45using content::BrowserThread;
46using content::DownloadItem;
47using content::DownloadManager;
48using content::URLRequestMockHTTPJob;
49using content::WebContents;
50
51namespace {
52
53// Waits for an item record in the downloads database to match |filter|. See
54// DownloadStoredProperly() below for an example filter.
55class DownloadPersistedObserver : public DownloadHistory::Observer {
56 public:
57  typedef base::Callback<bool(
58      DownloadItem* item,
59      const history::DownloadRow&)> PersistedFilter;
60
61  DownloadPersistedObserver(Profile* profile, const PersistedFilter& filter)
62    : profile_(profile),
63      filter_(filter),
64      waiting_(false),
65      persisted_(false) {
66    DownloadServiceFactory::GetForBrowserContext(profile_)->
67      GetDownloadHistory()->AddObserver(this);
68  }
69
70  virtual ~DownloadPersistedObserver() {
71    DownloadService* service = DownloadServiceFactory::GetForBrowserContext(
72        profile_);
73    if (service && service->GetDownloadHistory())
74      service->GetDownloadHistory()->RemoveObserver(this);
75  }
76
77  bool WaitForPersisted() {
78    if (persisted_)
79      return true;
80    waiting_ = true;
81    content::RunMessageLoop();
82    waiting_ = false;
83    return persisted_;
84  }
85
86  virtual void OnDownloadStored(DownloadItem* item,
87                                const history::DownloadRow& info) OVERRIDE {
88    persisted_ = persisted_ || filter_.Run(item, info);
89    if (persisted_ && waiting_)
90      base::MessageLoopForUI::current()->Quit();
91  }
92
93 private:
94  Profile* profile_;
95  DownloadItem* item_;
96  PersistedFilter filter_;
97  bool waiting_;
98  bool persisted_;
99
100  DISALLOW_COPY_AND_ASSIGN(DownloadPersistedObserver);
101};
102
103// Waits for an item record to be removed from the downloads database.
104class DownloadRemovedObserver : public DownloadPersistedObserver {
105 public:
106  DownloadRemovedObserver(Profile* profile, int32 download_id)
107      : DownloadPersistedObserver(profile, PersistedFilter()),
108        removed_(false),
109        waiting_(false),
110        download_id_(download_id) {
111  }
112  virtual ~DownloadRemovedObserver() {}
113
114  bool WaitForRemoved() {
115    if (removed_)
116      return true;
117    waiting_ = true;
118    content::RunMessageLoop();
119    waiting_ = false;
120    return removed_;
121  }
122
123  virtual void OnDownloadStored(DownloadItem* item,
124                                const history::DownloadRow& info) OVERRIDE {
125  }
126
127  virtual void OnDownloadsRemoved(const DownloadHistory::IdSet& ids) OVERRIDE {
128    removed_ = ids.find(download_id_) != ids.end();
129    if (removed_ && waiting_)
130      base::MessageLoopForUI::current()->Quit();
131  }
132
133 private:
134  bool removed_;
135  bool waiting_;
136  int32 download_id_;
137
138  DISALLOW_COPY_AND_ASSIGN(DownloadRemovedObserver);
139};
140
141bool DownloadStoredProperly(
142    const GURL& expected_url,
143    const base::FilePath& expected_path,
144    int64 num_files,
145    DownloadItem::DownloadState expected_state,
146    DownloadItem* item,
147    const history::DownloadRow& info) {
148  // This function may be called multiple times for a given test. Returning
149  // false doesn't necessarily mean that the test has failed or will fail, it
150  // might just mean that the test hasn't passed yet.
151  if (info.target_path != expected_path) {
152    VLOG(20) << __FUNCTION__ << " " << info.target_path.value()
153             << " != " << expected_path.value();
154    return false;
155  }
156  if (info.url_chain.size() != 1u) {
157    VLOG(20) << __FUNCTION__ << " " << info.url_chain.size()
158             << " != 1";
159    return false;
160  }
161  if (info.url_chain[0] != expected_url) {
162    VLOG(20) << __FUNCTION__ << " " << info.url_chain[0].spec()
163             << " != " << expected_url.spec();
164    return false;
165  }
166  if ((num_files >= 0) && (info.received_bytes != num_files)) {
167    VLOG(20) << __FUNCTION__ << " " << num_files
168             << " != " << info.received_bytes;
169    return false;
170  }
171  if (info.state != expected_state) {
172    VLOG(20) << __FUNCTION__ << " " << info.state
173             << " != " << expected_state;
174    return false;
175  }
176  return true;
177}
178
179const base::FilePath::CharType kTestDir[] = FILE_PATH_LITERAL("save_page");
180
181static const char kAppendedExtension[] =
182#if defined(OS_WIN)
183    ".htm";
184#else
185    ".html";
186#endif
187
188// Loosely based on logic in DownloadTestObserver.
189class DownloadItemCreatedObserver : public DownloadManager::Observer {
190 public:
191  explicit DownloadItemCreatedObserver(DownloadManager* manager)
192      : waiting_(false), manager_(manager) {
193    manager->AddObserver(this);
194  }
195
196  virtual ~DownloadItemCreatedObserver() {
197    if (manager_)
198      manager_->RemoveObserver(this);
199  }
200
201  // Wait for the first download item created after object creation.
202  // Note that this class provides no protection against the download
203  // being destroyed between creation and return of WaitForNewDownloadItem();
204  // the caller must guarantee that in some other fashion.
205  void WaitForDownloadItem(std::vector<DownloadItem*>* items_seen) {
206    if (!manager_) {
207      // The manager went away before we were asked to wait; return
208      // what we have, even if it's null.
209      *items_seen = items_seen_;
210      return;
211    }
212
213    if (items_seen_.empty()) {
214      waiting_ = true;
215      content::RunMessageLoop();
216      waiting_ = false;
217    }
218
219    *items_seen = items_seen_;
220    return;
221  }
222
223 private:
224  // DownloadManager::Observer
225  virtual void OnDownloadCreated(
226      DownloadManager* manager, DownloadItem* item) OVERRIDE {
227    DCHECK_EQ(manager, manager_);
228    items_seen_.push_back(item);
229
230    if (waiting_)
231      base::MessageLoopForUI::current()->Quit();
232  }
233
234  virtual void ManagerGoingDown(DownloadManager* manager) OVERRIDE {
235    manager_->RemoveObserver(this);
236    manager_ = NULL;
237    if (waiting_)
238      base::MessageLoopForUI::current()->Quit();
239  }
240
241  bool waiting_;
242  DownloadManager* manager_;
243  std::vector<DownloadItem*> items_seen_;
244
245  DISALLOW_COPY_AND_ASSIGN(DownloadItemCreatedObserver);
246};
247
248class SavePackageFinishedObserver : public content::DownloadManager::Observer {
249 public:
250  SavePackageFinishedObserver(content::DownloadManager* manager,
251                              const base::Closure& callback)
252      : download_manager_(manager),
253        callback_(callback) {
254    download_manager_->AddObserver(this);
255  }
256
257  virtual ~SavePackageFinishedObserver() {
258    if (download_manager_)
259      download_manager_->RemoveObserver(this);
260  }
261
262  // DownloadManager::Observer:
263  virtual void OnSavePackageSuccessfullyFinished(
264      content::DownloadManager* manager, content::DownloadItem* item) OVERRIDE {
265    callback_.Run();
266  }
267  virtual void ManagerGoingDown(content::DownloadManager* manager) OVERRIDE {
268    download_manager_->RemoveObserver(this);
269    download_manager_ = NULL;
270  }
271
272 private:
273  content::DownloadManager* download_manager_;
274  base::Closure callback_;
275
276  DISALLOW_COPY_AND_ASSIGN(SavePackageFinishedObserver);
277};
278
279class SavePageBrowserTest : public InProcessBrowserTest {
280 public:
281  SavePageBrowserTest() {}
282  virtual ~SavePageBrowserTest();
283
284 protected:
285  virtual void SetUp() OVERRIDE {
286    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir_));
287    ASSERT_TRUE(save_dir_.CreateUniqueTempDir());
288    InProcessBrowserTest::SetUp();
289  }
290
291  virtual void SetUpOnMainThread() OVERRIDE {
292    browser()->profile()->GetPrefs()->SetFilePath(
293        prefs::kDownloadDefaultDirectory, save_dir_.path());
294    browser()->profile()->GetPrefs()->SetFilePath(
295        prefs::kSaveFileDefaultDirectory, save_dir_.path());
296    BrowserThread::PostTask(
297        BrowserThread::IO, FROM_HERE,
298        base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
299  }
300
301  GURL NavigateToMockURL(const std::string& prefix) {
302    GURL url = URLRequestMockHTTPJob::GetMockUrl(
303        base::FilePath(kTestDir).AppendASCII(prefix + ".htm"));
304    ui_test_utils::NavigateToURL(browser(), url);
305    return url;
306  }
307
308  // Returns full paths of destination file and directory.
309  void GetDestinationPaths(const std::string& prefix,
310                base::FilePath* full_file_name,
311                base::FilePath* dir) {
312    *full_file_name = save_dir_.path().AppendASCII(prefix + ".htm");
313    *dir = save_dir_.path().AppendASCII(prefix + "_files");
314  }
315
316  WebContents* GetCurrentTab(Browser* browser) const {
317    WebContents* current_tab =
318        browser->tab_strip_model()->GetActiveWebContents();
319    EXPECT_TRUE(current_tab);
320    return current_tab;
321  }
322
323  // Returns true if and when there was a single download created, and its url
324  // is |expected_url|.
325  bool VerifySavePackageExpectations(
326      Browser* browser,
327      const GURL& expected_url) const {
328    // Generally, there should only be one download item created
329    // in all of these tests.  If it's already here, grab it; if not,
330    // wait for it to show up.
331    std::vector<DownloadItem*> items;
332    DownloadManager* manager(
333        BrowserContext::GetDownloadManager(browser->profile()));
334    manager->GetAllDownloads(&items);
335    if (items.size() == 0u) {
336      DownloadItemCreatedObserver(manager).WaitForDownloadItem(&items);
337    }
338
339    EXPECT_EQ(1u, items.size());
340    if (1u != items.size())
341      return false;
342    DownloadItem* download_item(items[0]);
343
344    return (expected_url == download_item->GetOriginalUrl());
345  }
346
347  // Note on synchronization:
348  //
349  // For each Save Page As operation, we create a corresponding shell
350  // DownloadItem to display progress to the user.  That DownloadItem goes
351  // through its own state transitions, including being persisted out to the
352  // history database, and the download shelf is not shown until after the
353  // persistence occurs.  Save Package completion (and marking the DownloadItem
354  // as completed) occurs asynchronously from persistence.  Thus if we want to
355  // examine either UI state or DB state, we need to wait until both the save
356  // package operation is complete and the relevant download item has been
357  // persisted.
358
359  DownloadManager* GetDownloadManager() const {
360    DownloadManager* download_manager =
361        BrowserContext::GetDownloadManager(browser()->profile());
362    EXPECT_TRUE(download_manager);
363    return download_manager;
364  }
365
366  // Path to directory containing test data.
367  base::FilePath test_dir_;
368
369  // Temporary directory we will save pages to.
370  base::ScopedTempDir save_dir_;
371
372 private:
373  DISALLOW_COPY_AND_ASSIGN(SavePageBrowserTest);
374};
375
376SavePageBrowserTest::~SavePageBrowserTest() {
377}
378
379// Disabled on Windows due to flakiness. http://crbug.com/162323
380// TODO(linux_aura) http://crbug.com/163931
381#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
382#define MAYBE_SaveHTMLOnly DISABLED_SaveHTMLOnly
383#else
384#define MAYBE_SaveHTMLOnly SaveHTMLOnly
385#endif
386IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_SaveHTMLOnly) {
387  GURL url = NavigateToMockURL("a");
388
389  base::FilePath full_file_name, dir;
390  GetDestinationPaths("a", &full_file_name, &dir);
391  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
392      &DownloadStoredProperly, url, full_file_name, 1,
393      DownloadItem::COMPLETE));
394  scoped_refptr<content::MessageLoopRunner> loop_runner(
395      new content::MessageLoopRunner);
396  SavePackageFinishedObserver observer(
397      content::BrowserContext::GetDownloadManager(browser()->profile()),
398      loop_runner->QuitClosure());
399  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
400                                        content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
401  loop_runner->Run();
402  ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
403  persisted.WaitForPersisted();
404  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
405  EXPECT_TRUE(base::PathExists(full_file_name));
406  EXPECT_FALSE(base::PathExists(dir));
407  EXPECT_TRUE(base::ContentsEqual(test_dir_.Append(base::FilePath(
408      kTestDir)).Append(FILE_PATH_LITERAL("a.htm")), full_file_name));
409}
410
411// http://crbug.com/162323
412// http://crbug.com/163931
413IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, DISABLED_SaveHTMLOnlyCancel) {
414  GURL url = NavigateToMockURL("a");
415  DownloadManager* manager(GetDownloadManager());
416  std::vector<DownloadItem*> downloads;
417  manager->GetAllDownloads(&downloads);
418  ASSERT_EQ(0u, downloads.size());
419
420  base::FilePath full_file_name, dir;
421  GetDestinationPaths("a", &full_file_name, &dir);
422  DownloadItemCreatedObserver creation_observer(manager);
423  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
424      &DownloadStoredProperly, url, full_file_name, -1,
425      DownloadItem::CANCELLED));
426  // -1 to disable number of files check; we don't update after cancel, and
427  // we don't know when the single file completed in relationship to
428  // the cancel.
429
430  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
431                                        content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
432  std::vector<DownloadItem*> items;
433  creation_observer.WaitForDownloadItem(&items);
434  ASSERT_EQ(1UL, items.size());
435  ASSERT_EQ(url.spec(), items[0]->GetOriginalUrl().spec());
436  items[0]->Cancel(true);
437  // TODO(rdsmith): Fix DII::Cancel() to actually cancel the save package.
438  // Currently it's ignored.
439
440  persisted.WaitForPersisted();
441
442  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
443
444  // TODO(benjhayden): Figure out how to safely wait for SavePackage's finished
445  // notification, then expect the contents of the downloaded file.
446}
447
448class DelayingDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
449 public:
450  explicit DelayingDownloadManagerDelegate(Profile* profile)
451    : ChromeDownloadManagerDelegate(profile) {
452  }
453  virtual bool ShouldCompleteDownload(
454      content::DownloadItem* item,
455      const base::Closure& user_complete_callback) OVERRIDE {
456    return false;
457  }
458
459 protected:
460  virtual ~DelayingDownloadManagerDelegate() {}
461
462 private:
463  DISALLOW_COPY_AND_ASSIGN(DelayingDownloadManagerDelegate);
464};
465
466IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveHTMLOnlyTabDestroy) {
467  GURL url = NavigateToMockURL("a");
468  DownloadManager* manager(GetDownloadManager());
469  scoped_refptr<DelayingDownloadManagerDelegate> delaying_delegate(
470      new DelayingDownloadManagerDelegate(browser()->profile()));
471  delaying_delegate->SetNextId(content::DownloadItem::kInvalidId + 1);
472  manager->SetDelegate(delaying_delegate.get());
473  std::vector<DownloadItem*> downloads;
474  manager->GetAllDownloads(&downloads);
475  ASSERT_EQ(0u, downloads.size());
476
477  base::FilePath full_file_name, dir;
478  GetDestinationPaths("a", &full_file_name, &dir);
479  DownloadItemCreatedObserver creation_observer(manager);
480  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
481                                        content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
482  std::vector<DownloadItem*> items;
483  creation_observer.WaitForDownloadItem(&items);
484  ASSERT_TRUE(items.size() == 1);
485
486  // Close the tab; does this cancel the download?
487  GetCurrentTab(browser())->Close();
488  EXPECT_EQ(DownloadItem::CANCELLED, items[0]->GetState());
489
490  EXPECT_FALSE(base::PathExists(full_file_name));
491  EXPECT_FALSE(base::PathExists(dir));
492
493  manager->SetDelegate(NULL);
494}
495
496// Disabled on Windows due to flakiness. http://crbug.com/162323
497// TODO(linux_aura) http://crbug.com/163931
498#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
499#define MAYBE_SaveViewSourceHTMLOnly DISABLED_SaveViewSourceHTMLOnly
500#else
501#define MAYBE_SaveViewSourceHTMLOnly SaveViewSourceHTMLOnly
502#endif
503IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_SaveViewSourceHTMLOnly) {
504  base::FilePath file_name(FILE_PATH_LITERAL("a.htm"));
505  GURL view_source_url = URLRequestMockHTTPJob::GetMockViewSourceUrl(
506      base::FilePath(kTestDir).Append(file_name));
507  GURL actual_page_url = URLRequestMockHTTPJob::GetMockUrl(
508      base::FilePath(kTestDir).Append(file_name));
509  ui_test_utils::NavigateToURL(browser(), view_source_url);
510
511  base::FilePath full_file_name, dir;
512  GetDestinationPaths("a", &full_file_name, &dir);
513  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
514      &DownloadStoredProperly, actual_page_url, full_file_name, 1,
515      DownloadItem::COMPLETE));
516  scoped_refptr<content::MessageLoopRunner> loop_runner(
517      new content::MessageLoopRunner);
518  SavePackageFinishedObserver observer(
519      content::BrowserContext::GetDownloadManager(browser()->profile()),
520      loop_runner->QuitClosure());
521  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
522                                        content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
523  loop_runner->Run();
524  ASSERT_TRUE(VerifySavePackageExpectations(browser(), actual_page_url));
525  persisted.WaitForPersisted();
526
527  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
528
529  EXPECT_TRUE(base::PathExists(full_file_name));
530  EXPECT_FALSE(base::PathExists(dir));
531  EXPECT_TRUE(base::ContentsEqual(
532      test_dir_.Append(base::FilePath(kTestDir)).Append(file_name),
533      full_file_name));
534}
535
536// Disabled on Windows due to flakiness. http://crbug.com/162323
537// TODO(linux_aura) http://crbug.com/163931
538#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
539#define MAYBE_SaveCompleteHTML DISABLED_SaveCompleteHTML
540#else
541#define MAYBE_SaveCompleteHTML SaveCompleteHTML
542#endif
543IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_SaveCompleteHTML) {
544  GURL url = NavigateToMockURL("b");
545
546  base::FilePath full_file_name, dir;
547  GetDestinationPaths("b", &full_file_name, &dir);
548  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
549      &DownloadStoredProperly, url, full_file_name, 3,
550      DownloadItem::COMPLETE));
551  scoped_refptr<content::MessageLoopRunner> loop_runner(
552      new content::MessageLoopRunner);
553  SavePackageFinishedObserver observer(
554      content::BrowserContext::GetDownloadManager(browser()->profile()),
555      loop_runner->QuitClosure());
556  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(
557      full_file_name, dir, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
558  loop_runner->Run();
559  ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
560  persisted.WaitForPersisted();
561
562  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
563
564  EXPECT_TRUE(base::PathExists(full_file_name));
565  EXPECT_TRUE(base::PathExists(dir));
566  EXPECT_TRUE(base::TextContentsEqual(
567      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("b.saved1.htm"),
568      full_file_name));
569  EXPECT_TRUE(base::ContentsEqual(
570      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("1.png"),
571      dir.AppendASCII("1.png")));
572  EXPECT_TRUE(base::ContentsEqual(
573      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("1.css"),
574      dir.AppendASCII("1.css")));
575}
576
577// Invoke a save page during the initial navigation.
578// (Regression test for http://crbug.com/156538).
579// Disabled on Windows due to flakiness. http://crbug.com/162323
580// TODO(linux_aura) http://crbug.com/163931
581#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
582#define MAYBE_SaveDuringInitialNavigationIncognito DISABLED_SaveDuringInitialNavigationIncognito
583#else
584#define MAYBE_SaveDuringInitialNavigationIncognito SaveDuringInitialNavigationIncognito
585#endif
586IN_PROC_BROWSER_TEST_F(SavePageBrowserTest,
587                       MAYBE_SaveDuringInitialNavigationIncognito) {
588  // Open an Incognito window.
589  Browser* incognito = CreateIncognitoBrowser();  // Waits.
590  ASSERT_TRUE(incognito);
591
592  // Create a download item creation waiter on that window.
593  DownloadItemCreatedObserver creation_observer(
594      BrowserContext::GetDownloadManager(incognito->profile()));
595
596  // Navigate, unblocking with new tab.
597  GURL url = URLRequestMockHTTPJob::GetMockUrl(
598      base::FilePath(kTestDir).AppendASCII("b.htm"));
599  NavigateToURLWithDisposition(incognito, url, NEW_FOREGROUND_TAB,
600                               ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
601
602  // Save the page before completion.
603  base::FilePath full_file_name, dir;
604  GetDestinationPaths("b", &full_file_name, &dir);
605  scoped_refptr<content::MessageLoopRunner> loop_runner(
606      new content::MessageLoopRunner);
607  SavePackageFinishedObserver observer(
608      content::BrowserContext::GetDownloadManager(incognito->profile()),
609      loop_runner->QuitClosure());
610  ASSERT_TRUE(GetCurrentTab(incognito)->SavePage(
611      full_file_name, dir, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
612
613  loop_runner->Run();
614  ASSERT_TRUE(VerifySavePackageExpectations(incognito, url));
615
616  // Confirm download shelf is visible.
617  EXPECT_TRUE(incognito->window()->IsDownloadShelfVisible());
618
619  // We can't check more than this because SavePackage is racing with
620  // the page load.  If the page load won the race, then SavePackage
621  // might have completed. If the page load lost the race, then
622  // SavePackage will cancel because there aren't any resources to
623  // save.
624}
625
626IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, NoSave) {
627  ui_test_utils::NavigateToURL(browser(), GURL(content::kAboutBlankURL));
628  EXPECT_FALSE(chrome::CanSavePage(browser()));
629}
630
631// Disabled on Windows due to flakiness. http://crbug.com/162323
632// TODO(linux_aura) http://crbug.com/163931
633#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
634#define MAYBE_FileNameFromPageTitle DISABLED_FileNameFromPageTitle
635#else
636#define MAYBE_FileNameFromPageTitle FileNameFromPageTitle
637#endif
638IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_FileNameFromPageTitle) {
639  GURL url = NavigateToMockURL("b");
640
641  base::FilePath full_file_name = save_dir_.path().AppendASCII(
642      std::string("Test page for saving page feature") + kAppendedExtension);
643  base::FilePath dir = save_dir_.path().AppendASCII(
644      "Test page for saving page feature_files");
645  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
646      &DownloadStoredProperly, url, full_file_name, 3,
647      DownloadItem::COMPLETE));
648  scoped_refptr<content::MessageLoopRunner> loop_runner(
649      new content::MessageLoopRunner);
650  SavePackageFinishedObserver observer(
651      content::BrowserContext::GetDownloadManager(browser()->profile()),
652      loop_runner->QuitClosure());
653  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(
654      full_file_name, dir, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
655
656  loop_runner->Run();
657  ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
658  persisted.WaitForPersisted();
659
660  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
661
662  EXPECT_TRUE(base::PathExists(full_file_name));
663  EXPECT_TRUE(base::PathExists(dir));
664  EXPECT_TRUE(base::TextContentsEqual(
665      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("b.saved2.htm"),
666      full_file_name));
667  EXPECT_TRUE(base::ContentsEqual(
668      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("1.png"),
669      dir.AppendASCII("1.png")));
670  EXPECT_TRUE(base::ContentsEqual(
671      test_dir_.Append(base::FilePath(kTestDir)).AppendASCII("1.css"),
672      dir.AppendASCII("1.css")));
673}
674
675// Disabled on Windows due to flakiness. http://crbug.com/162323
676// TODO(linux_aura) http://crbug.com/163931
677#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
678#define MAYBE_RemoveFromList DISABLED_RemoveFromList
679#else
680#define MAYBE_RemoveFromList RemoveFromList
681#endif
682IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_RemoveFromList) {
683  GURL url = NavigateToMockURL("a");
684
685  base::FilePath full_file_name, dir;
686  GetDestinationPaths("a", &full_file_name, &dir);
687  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
688      &DownloadStoredProperly, url, full_file_name, 1,
689      DownloadItem::COMPLETE));
690  scoped_refptr<content::MessageLoopRunner> loop_runner(
691      new content::MessageLoopRunner);
692  SavePackageFinishedObserver observer(
693      content::BrowserContext::GetDownloadManager(browser()->profile()),
694      loop_runner->QuitClosure());
695  ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
696                                        content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
697
698  loop_runner->Run();
699  ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
700  persisted.WaitForPersisted();
701
702  EXPECT_TRUE(browser()->window()->IsDownloadShelfVisible());
703
704  DownloadManager* manager(GetDownloadManager());
705  std::vector<DownloadItem*> downloads;
706  manager->GetAllDownloads(&downloads);
707  ASSERT_EQ(1UL, downloads.size());
708  DownloadRemovedObserver removed(browser()->profile(), downloads[0]->GetId());
709
710  EXPECT_EQ(manager->RemoveAllDownloads(), 1);
711
712  removed.WaitForRemoved();
713
714  EXPECT_TRUE(base::PathExists(full_file_name));
715  EXPECT_FALSE(base::PathExists(dir));
716  EXPECT_TRUE(base::ContentsEqual(test_dir_.Append(base::FilePath(
717      kTestDir)).Append(FILE_PATH_LITERAL("a.htm")), full_file_name));
718}
719
720// This tests that a webpage with the title "test.exe" is saved as
721// "test.exe.htm".
722// We probably don't care to handle this on Linux or Mac.
723#if defined(OS_WIN)
724IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, CleanFilenameFromPageTitle) {
725  const base::FilePath file_name(FILE_PATH_LITERAL("c.htm"));
726  base::FilePath download_dir =
727      DownloadPrefs::FromDownloadManager(GetDownloadManager())->
728          DownloadPath();
729  base::FilePath full_file_name =
730      download_dir.AppendASCII(std::string("test.exe") + kAppendedExtension);
731  base::FilePath dir = download_dir.AppendASCII("test.exe_files");
732
733  EXPECT_FALSE(base::PathExists(full_file_name));
734  GURL url = URLRequestMockHTTPJob::GetMockUrl(
735      base::FilePath(kTestDir).Append(file_name));
736  ui_test_utils::NavigateToURL(browser(), url);
737
738  SavePackageFilePicker::SetShouldPromptUser(false);
739  scoped_refptr<content::MessageLoopRunner> loop_runner(
740      new content::MessageLoopRunner);
741  SavePackageFinishedObserver observer(
742      content::BrowserContext::GetDownloadManager(browser()->profile()),
743      loop_runner->QuitClosure());
744  chrome::SavePage(browser());
745  loop_runner->Run();
746
747  EXPECT_TRUE(base::PathExists(full_file_name));
748
749  EXPECT_TRUE(file_util::DieFileDie(full_file_name, false));
750  EXPECT_TRUE(file_util::DieFileDie(dir, true));
751}
752#endif
753
754class SavePageAsMHTMLBrowserTest : public SavePageBrowserTest {
755 public:
756  SavePageAsMHTMLBrowserTest() {}
757  virtual ~SavePageAsMHTMLBrowserTest();
758  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
759    command_line->AppendSwitch(switches::kSavePageAsMHTML);
760  }
761
762 private:
763  DISALLOW_COPY_AND_ASSIGN(SavePageAsMHTMLBrowserTest);
764};
765
766SavePageAsMHTMLBrowserTest::~SavePageAsMHTMLBrowserTest() {
767}
768
769IN_PROC_BROWSER_TEST_F(SavePageAsMHTMLBrowserTest, SavePageAsMHTML) {
770  static const int64 kFileSizeMin = 2758;
771  GURL url = NavigateToMockURL("b");
772  base::FilePath download_dir = DownloadPrefs::FromDownloadManager(
773      GetDownloadManager())->DownloadPath();
774  base::FilePath full_file_name = download_dir.AppendASCII(std::string(
775      "Test page for saving page feature.mhtml"));
776  SavePackageFilePicker::SetShouldPromptUser(false);
777  DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
778      &DownloadStoredProperly, url, full_file_name, -1,
779      DownloadItem::COMPLETE));
780  scoped_refptr<content::MessageLoopRunner> loop_runner(
781      new content::MessageLoopRunner);
782  SavePackageFinishedObserver observer(
783      content::BrowserContext::GetDownloadManager(browser()->profile()),
784      loop_runner->QuitClosure());
785  chrome::SavePage(browser());
786  loop_runner->Run();
787  ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
788  persisted.WaitForPersisted();
789
790  ASSERT_TRUE(base::PathExists(full_file_name));
791  int64 actual_file_size = -1;
792  EXPECT_TRUE(file_util::GetFileSize(full_file_name, &actual_file_size));
793  EXPECT_LE(kFileSizeMin, actual_file_size);
794}
795
796IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SavePageBrowserTest_NonMHTML) {
797  SavePackageFilePicker::SetShouldPromptUser(false);
798  GURL url("data:text/plain,foo");
799  ui_test_utils::NavigateToURL(browser(), url);
800  scoped_refptr<content::MessageLoopRunner> loop_runner(
801      new content::MessageLoopRunner);
802  SavePackageFinishedObserver observer(
803      content::BrowserContext::GetDownloadManager(browser()->profile()),
804      loop_runner->QuitClosure());
805  chrome::SavePage(browser());
806  loop_runner->Run();
807  base::FilePath download_dir = DownloadPrefs::FromDownloadManager(
808      GetDownloadManager())->DownloadPath();
809  base::FilePath filename = download_dir.AppendASCII("dataurl.txt");
810  ASSERT_TRUE(base::PathExists(filename));
811  std::string contents;
812  EXPECT_TRUE(file_util::ReadFileToString(filename, &contents));
813  EXPECT_EQ("foo", contents);
814}
815
816}  // namespace
817
818