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