blob: 8030eb15072f245acb541959bf9d1c88228564d2 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/test_file_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_item.h"
#include "chrome/browser/download/download_file_manager.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/history/download_create_info.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/net/url_request_mock_http_job.h"
#include "chrome/browser/net/url_request_slow_download_job.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "content/browser/cancelable_request.h"
#include "content/browser/renderer_host/resource_dispatcher_host.h"
#include "content/common/page_transition_types.h"
#include "net/base/net_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Variation of DownloadsCompleteObserver from ui_test_utils.cc; the
// specifically targeted download tests need finer granularity on waiting.
// Construction of this class defines a system state, based on some number
// of downloads being seen in a particular state + other events that
// may occur in the download system. That state will be recorded if it
// occurs at any point after construction. When that state occurs, the class
// is considered finished. Callers may either probe for the finished state, or
// wait on it.
//
// TODO(rdsmith): Detect manager going down, remove pointer to
// DownloadManager, transition to finished. (For right now we
// just use a scoped_refptr<> to keep it around, but that may cause
// timeouts on waiting if a DownloadManager::Shutdown() occurs which
// cancels our in-progress downloads.)
class DownloadsObserver : public DownloadManager::Observer,
public DownloadItem::Observer {
public:
// Create an object that will be considered finished when |wait_count|
// download items have entered state |download_finished_state|.
// If |finish_on_select_file| is true, the object will also be
// considered finished if the DownloadManager raises a
// SelectFileDialogDisplayed() notification.
// TODO(rdsmith): Add option of "dangerous accept/reject dialog" as
// a unblocking event; if that shows up when you aren't expecting it,
// it'll result in a hang/timeout as we'll never get to final rename.
// This probably means rewriting the interface to take a list of events
// to treat as completion events.
DownloadsObserver(DownloadManager* download_manager,
size_t wait_count,
DownloadItem::DownloadState download_finished_state,
bool finish_on_select_file)
: download_manager_(download_manager),
wait_count_(wait_count),
finished_downloads_at_construction_(0),
waiting_(false),
download_finished_state_(download_finished_state),
finish_on_select_file_(finish_on_select_file),
select_file_dialog_seen_(false) {
download_manager_->AddObserver(this); // Will call initial ModelChanged().
finished_downloads_at_construction_ = finished_downloads_.size();
}
~DownloadsObserver() {
std::set<DownloadItem*>::iterator it = downloads_observed_.begin();
for (; it != downloads_observed_.end(); ++it) {
(*it)->RemoveObserver(this);
}
download_manager_->RemoveObserver(this);
}
// State accessors.
bool select_file_dialog_seen() { return select_file_dialog_seen_; }
// Wait for whatever state was specified in the constructor.
void WaitForFinished() {
if (!IsFinished()) {
waiting_ = true;
ui_test_utils::RunMessageLoop();
waiting_ = false;
}
}
// Return true if everything's happened that we're configured for.
bool IsFinished() {
if (finished_downloads_.size() - finished_downloads_at_construction_
>= wait_count_)
return true;
return (finish_on_select_file_ && select_file_dialog_seen_);
}
// DownloadItem::Observer
virtual void OnDownloadUpdated(DownloadItem* download) {
if (download->state() == download_finished_state_)
DownloadInFinalState(download);
}
virtual void OnDownloadOpened(DownloadItem* download) {}
// DownloadManager::Observer
virtual void ModelChanged() {
// Regenerate DownloadItem observers. If there are any download items
// in our final state, note them in |finished_downloads_|
// (done by |OnDownloadUpdated()|).
std::vector<DownloadItem*> downloads;
download_manager_->SearchDownloads(string16(), &downloads);
std::vector<DownloadItem*>::iterator it = downloads.begin();
for (; it != downloads.end(); ++it) {
OnDownloadUpdated(*it); // Safe to call multiple times; checks state.
std::set<DownloadItem*>::const_iterator
finished_it(finished_downloads_.find(*it));
std::set<DownloadItem*>::iterator
observed_it(downloads_observed_.find(*it));
// If it isn't finished and we're aren't observing it, start.
if (finished_it == finished_downloads_.end() &&
observed_it == downloads_observed_.end()) {
(*it)->AddObserver(this);
downloads_observed_.insert(*it);
continue;
}
// If it is finished and we are observing it, stop.
if (finished_it != finished_downloads_.end() &&
observed_it != downloads_observed_.end()) {
(*it)->RemoveObserver(this);
downloads_observed_.erase(observed_it);
continue;
}
}
}
virtual void SelectFileDialogDisplayed(int32 /* id */) {
select_file_dialog_seen_ = true;
SignalIfFinished();
}
private:
// Called when we know that a download item is in a final state.
// Note that this is not the same as it first transitioning in to the
// final state; multiple notifications may occur once the item is in
// that state. So we keep our own track of transitions into final.
void DownloadInFinalState(DownloadItem* download) {
if (finished_downloads_.find(download) != finished_downloads_.end()) {
// We've already seen terminal state on this download.
return;
}
// Record the transition.
finished_downloads_.insert(download);
SignalIfFinished();
}
void SignalIfFinished() {
if (waiting_ && IsFinished())
MessageLoopForUI::current()->Quit();
}
// The observed download manager.
scoped_refptr<DownloadManager> download_manager_;
// The set of DownloadItem's that have transitioned to their finished state
// since construction of this object. When the size of this array
// reaches wait_count_, we're done.
std::set<DownloadItem*> finished_downloads_;
// The set of DownloadItem's we are currently observing. Generally there
// won't be any overlap with the above; once we see the final state
// on a DownloadItem, we'll stop observing it.
std::set<DownloadItem*> downloads_observed_;
// The number of downloads to wait on completing.
size_t wait_count_;
// The number of downloads entered in final state in initial
// ModelChanged(). We use |finished_downloads_| to track the incoming
// transitions to final state we should ignore, and to track the
// number of final state transitions that occurred between
// construction and return from wait. But some downloads may be in our
// final state (and thus be entered into |finished_downloads_|) when we
// construct this class. We don't want to count those in our transition
// to finished.
int finished_downloads_at_construction_;
// Whether an internal message loop has been started and must be quit upon
// all downloads completing.
bool waiting_;
// The state on which to consider the DownloadItem finished.
DownloadItem::DownloadState download_finished_state_;
// True if we should transition the DownloadsObserver to finished if
// the select file dialog comes up.
bool finish_on_select_file_;
// True if we've seen the select file dialog.
bool select_file_dialog_seen_;
DISALLOW_COPY_AND_ASSIGN(DownloadsObserver);
};
// WaitForFlush() returns after:
// * There are no IN_PROGRESS download items remaining on the
// DownloadManager.
// * There have been two round trip messages through the file and
// IO threads.
// This almost certainly means that a Download cancel has propagated through
// the system.
class DownloadsFlushObserver
: public DownloadManager::Observer,
public DownloadItem::Observer,
public base::RefCountedThreadSafe<DownloadsFlushObserver> {
public:
explicit DownloadsFlushObserver(DownloadManager* download_manager)
: download_manager_(download_manager),
waiting_for_zero_inprogress_(true) { }
void WaitForFlush() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
download_manager_->AddObserver(this);
ui_test_utils::RunMessageLoop();
}
// DownloadsManager observer methods.
virtual void ModelChanged() {
// Model has changed, so there may be more DownloadItems to observe.
CheckDownloadsInProgress(true);
}
// DownloadItem observer methods.
virtual void OnDownloadUpdated(DownloadItem* download) {
// No change in DownloadItem set on manager.
CheckDownloadsInProgress(false);
}
virtual void OnDownloadOpened(DownloadItem* download) {}
protected:
friend class base::RefCountedThreadSafe<DownloadsFlushObserver>;
virtual ~DownloadsFlushObserver() {
download_manager_->RemoveObserver(this);
for (std::set<DownloadItem*>::iterator it = downloads_observed_.begin();
it != downloads_observed_.end(); ++it) {
(*it)->RemoveObserver(this);
}
}
private:
// If we're waiting for that flush point, check the number
// of downloads in the IN_PROGRESS state and take appropriate
// action. If requested, also observes all downloads while iterating.
void CheckDownloadsInProgress(bool observe_downloads) {
if (waiting_for_zero_inprogress_) {
int count = 0;
std::vector<DownloadItem*> downloads;
download_manager_->SearchDownloads(string16(), &downloads);
std::vector<DownloadItem*>::iterator it = downloads.begin();
for (; it != downloads.end(); ++it) {
if ((*it)->state() == DownloadItem::IN_PROGRESS)
count++;
if (observe_downloads) {
if (downloads_observed_.find(*it) == downloads_observed_.end()) {
(*it)->AddObserver(this);
}
// Download items are forever, and we don't want to make
// assumptions about future state transitions, so once we
// start observing them, we don't stop until destruction.
}
}
if (count == 0) {
waiting_for_zero_inprogress_ = false;
// Stop observing DownloadItems. We maintain the observation
// of DownloadManager so that we don't have to independently track
// whether we are observing it for conditional destruction.
for (std::set<DownloadItem*>::iterator it = downloads_observed_.begin();
it != downloads_observed_.end(); ++it) {
(*it)->RemoveObserver(this);
}
downloads_observed_.clear();
// Trigger next step. We need to go past the IO thread twice, as
// there's a self-task posting in the IO thread cancel path.
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(this,
&DownloadsFlushObserver::PingFileThread, 2));
}
}
}
void PingFileThread(int cycle) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &DownloadsFlushObserver::PingIOThread,
cycle));
}
void PingIOThread(int cycle) {
if (--cycle) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(this, &DownloadsFlushObserver::PingFileThread,
cycle));
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask());
}
}
DownloadManager* download_manager_;
std::set<DownloadItem*> downloads_observed_;
bool waiting_for_zero_inprogress_;
DISALLOW_COPY_AND_ASSIGN(DownloadsFlushObserver);
};
// Collect the information from FILE and IO threads needed for the Cancel
// Test, specifically the number of outstanding requests on the
// ResourceDispatcherHost and the number of pending downloads on the
// DownloadFileManager.
class CancelTestDataCollector
: public base::RefCountedThreadSafe<CancelTestDataCollector> {
public:
CancelTestDataCollector()
: resource_dispatcher_host_(
g_browser_process->resource_dispatcher_host()),
rdh_pending_requests_(0),
dfm_pending_downloads_(0) { }
void WaitForDataCollected() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &CancelTestDataCollector::IOInfoCollector));
ui_test_utils::RunMessageLoop();
}
int rdh_pending_requests() { return rdh_pending_requests_; }
int dfm_pending_downloads() { return dfm_pending_downloads_; }
protected:
friend class base::RefCountedThreadSafe<CancelTestDataCollector>;
virtual ~CancelTestDataCollector() {}
private:
void IOInfoCollector() {
download_file_manager_ = resource_dispatcher_host_->download_file_manager();
rdh_pending_requests_ = resource_dispatcher_host_->pending_requests();
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(this, &CancelTestDataCollector::FileInfoCollector));
}
void FileInfoCollector() {
dfm_pending_downloads_ = download_file_manager_->NumberOfActiveDownloads();
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask());
}
ResourceDispatcherHost* resource_dispatcher_host_;
DownloadFileManager* download_file_manager_;
int rdh_pending_requests_;
int dfm_pending_downloads_;
DISALLOW_COPY_AND_ASSIGN(CancelTestDataCollector);
};
} // namespace
class DownloadTest : public InProcessBrowserTest {
public:
enum SelectExpectation {
EXPECT_NO_SELECT_DIALOG = -1,
EXPECT_NOTHING,
EXPECT_SELECT_DIALOG
};
// Returning false indicates a failure of the setup, and should be asserted
// in the caller.
virtual bool InitialSetup(bool prompt_for_download) {
bool have_test_dir = PathService::Get(chrome::DIR_TEST_DATA, &test_dir_);
EXPECT_TRUE(have_test_dir);
if (!have_test_dir)
return false;
// Sanity check default values for window / tab count and shelf visibility.
int window_count = BrowserList::size();
EXPECT_EQ(1, window_count);
EXPECT_EQ(1, browser()->tab_count());
bool is_shelf_visible = browser()->window()->IsDownloadShelfVisible();
EXPECT_FALSE(is_shelf_visible);
// Set up the temporary download folder.
bool created_downloads_dir = CreateAndSetDownloadsDirectory(browser());
EXPECT_TRUE(created_downloads_dir);
if (!created_downloads_dir)
return false;
browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload,
prompt_for_download);
return true;
}
protected:
enum SizeTestType {
SIZE_TEST_TYPE_KNOWN,
SIZE_TEST_TYPE_UNKNOWN,
};
// Location of the file source (the place from which it is downloaded).
FilePath OriginFile(FilePath file) {
return test_dir_.Append(file);
}
// Location of the file destination (place to which it is downloaded).
FilePath DestinationFile(Browser* browser, FilePath file) {
return GetDownloadDirectory(browser).Append(file);
}
// Must be called after browser creation. Creates a temporary
// directory for downloads that is auto-deleted on destruction.
// Returning false indicates a failure of the function, and should be asserted
// in the caller.
bool CreateAndSetDownloadsDirectory(Browser* browser) {
if (!browser)
return false;
if (!downloads_directory_.CreateUniqueTempDir())
return false;
browser->profile()->GetPrefs()->SetFilePath(
prefs::kDownloadDefaultDirectory,
downloads_directory_.path());
return true;
}
FilePath GetDownloadDirectory(Browser* browser) {
DownloadManager* download_mananger =
browser->profile()->GetDownloadManager();
return download_mananger->download_prefs()->download_path();
}
// Create a DownloadsObserver that will wait for the
// specified number of downloads to finish.
DownloadsObserver* CreateWaiter(Browser* browser, int num_downloads) {
DownloadManager* download_manager =
browser->profile()->GetDownloadManager();
return new DownloadsObserver(
download_manager, num_downloads,
DownloadItem::COMPLETE, // Really done
true); // Bail on select file
}
// Create a DownloadsObserver that will wait for the
// specified number of downloads to start.
DownloadsObserver* CreateInProgressWaiter(Browser* browser,
int num_downloads) {
DownloadManager* download_manager =
browser->profile()->GetDownloadManager();
return new DownloadsObserver(
download_manager, num_downloads,
DownloadItem::IN_PROGRESS, // Has started
true); // Bail on select file
}
// Download |url|, then wait for the download to finish.
// |disposition| indicates where the navigation occurs (current tab, new
// foreground tab, etc).
// |expectation| indicates whether or not a Select File dialog should be
// open when the download is finished, or if we don't care.
// If the dialog appears, the routine exits. The only effect |expectation|
// has is whether or not the test succeeds.
// |browser_test_flags| indicate what to wait for, and is an OR of 0 or more
// values in the ui_test_utils::BrowserTestWaitFlags enum.
void DownloadAndWaitWithDisposition(Browser* browser,
const GURL& url,
WindowOpenDisposition disposition,
SelectExpectation expectation,
int browser_test_flags) {
// Setup notification, navigate, and block.
scoped_ptr<DownloadsObserver> observer(CreateWaiter(browser, 1));
// This call will block until the condition specified by
// |browser_test_flags|, but will not wait for the download to finish.
ui_test_utils::NavigateToURLWithDisposition(browser,
url,
disposition,
browser_test_flags);
// Waits for the download to complete.
observer->WaitForFinished();
// If specified, check the state of the select file dialog.
if (expectation != EXPECT_NOTHING) {
EXPECT_EQ(expectation == EXPECT_SELECT_DIALOG,
observer->select_file_dialog_seen());
}
}
// Download a file in the current tab, then wait for the download to finish.
void DownloadAndWait(Browser* browser,
const GURL& url,
SelectExpectation expectation) {
DownloadAndWaitWithDisposition(
browser,
url,
CURRENT_TAB,
expectation,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
}
// Should only be called when the download is known to have finished
// (in error or not).
// Returning false indicates a failure of the function, and should be asserted
// in the caller.
bool CheckDownload(Browser* browser,
const FilePath& downloaded_filename,
const FilePath& origin_filename) {
// Find the path to which the data will be downloaded.
FilePath downloaded_file(DestinationFile(browser, downloaded_filename));
// Find the origin path (from which the data comes).
FilePath origin_file(OriginFile(origin_filename));
bool origin_file_exists = file_util::PathExists(origin_file);
EXPECT_TRUE(origin_file_exists);
if (!origin_file_exists)
return false;
// Confirm the downloaded data file exists.
bool downloaded_file_exists = file_util::PathExists(downloaded_file);
EXPECT_TRUE(downloaded_file_exists);
if (!downloaded_file_exists)
return false;
int64 origin_file_size = 0;
int64 downloaded_file_size = 0;
EXPECT_TRUE(file_util::GetFileSize(origin_file, &origin_file_size));
EXPECT_TRUE(file_util::GetFileSize(downloaded_file, &downloaded_file_size));
EXPECT_EQ(origin_file_size, downloaded_file_size);
EXPECT_TRUE(file_util::ContentsEqual(downloaded_file, origin_file));
// Delete the downloaded copy of the file.
bool downloaded_file_deleted =
file_util::DieFileDie(downloaded_file, false);
EXPECT_TRUE(downloaded_file_deleted);
return downloaded_file_deleted;
}
bool RunSizeTest(Browser* browser,
SizeTestType type,
const std::string& partial_indication,
const std::string& total_indication) {
if (!InitialSetup(false))
return false;
EXPECT_TRUE(type == SIZE_TEST_TYPE_UNKNOWN || type == SIZE_TEST_TYPE_KNOWN);
if (type != SIZE_TEST_TYPE_KNOWN && type != SIZE_TEST_TYPE_UNKNOWN)
return false;
GURL url(type == SIZE_TEST_TYPE_KNOWN ?
URLRequestSlowDownloadJob::kKnownSizeUrl :
URLRequestSlowDownloadJob::kUnknownSizeUrl);
// TODO(ahendrickson) -- |expected_title_in_progress| and
// |expected_title_finished| need to be checked.
FilePath filename;
net::FileURLToFilePath(url, &filename);
string16 expected_title_in_progress(
ASCIIToUTF16(partial_indication) + filename.LossyDisplayName());
string16 expected_title_finished(
ASCIIToUTF16(total_indication) + filename.LossyDisplayName());
// Download a partial web page in a background tab and wait.
// The mock system will not complete until it gets a special URL.
scoped_ptr<DownloadsObserver> observer(CreateWaiter(browser, 1));
ui_test_utils::NavigateToURL(browser, url);
// TODO(ahendrickson): check download status text before downloading.
// Need to:
// - Add a member function to the |DownloadShelf| interface class, that
// indicates how many members it has.
// - Add a member function to |DownloadShelf| to get the status text
// of a given member (for example, via the name in |DownloadItemView|'s
// GetAccessibleState() member function), by index.
// - Iterate over browser->window()->GetDownloadShelf()'s members
// to see if any match the status text we want. Start with the last one.
// Allow the request to finish. We do this by loading a second URL in a
// separate tab.
GURL finish_url(URLRequestSlowDownloadJob::kFinishDownloadUrl);
ui_test_utils::NavigateToURLWithDisposition(
browser,
finish_url,
NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
observer->WaitForFinished();
EXPECT_EQ(2, browser->tab_count());
// TODO(ahendrickson): check download status text after downloading.
// Make sure the download shelf is showing.
EXPECT_TRUE(IsDownloadUIVisible(browser));
FilePath basefilename(filename.BaseName());
net::FileURLToFilePath(url, &filename);
FilePath download_path = downloads_directory_.path().Append(basefilename);
bool downloaded_path_exists = file_util::PathExists(download_path);
EXPECT_TRUE(downloaded_path_exists);
if (!downloaded_path_exists)
return false;
// Delete the file we just downloaded.
EXPECT_TRUE(file_util::DieFileDie(download_path, true));
EXPECT_FALSE(file_util::PathExists(download_path));
return true;
}
void GetDownloads(Browser* browser, std::vector<DownloadItem*>* downloads) {
DCHECK(downloads);
DownloadManager* manager = browser->profile()->GetDownloadManager();
manager->SearchDownloads(string16(), downloads);
}
// Figure out if the appropriate download visibility was done. A
// utility function to support ChromeOS variations. On ChromeOS
// a webui panel is used instead of the download shelf; the
// test for TYPE_APP_PANEL detects this type of panel.
static bool IsDownloadUIVisible(Browser* browser) {
#if defined(OS_CHROMEOS)
for (BrowserList::const_iterator it = BrowserList::begin();
it != BrowserList::end(); ++it) {
if ((*it)->type() == Browser::TYPE_APP_PANEL) {
return true;
}
}
return false;
#else
return browser->window()->IsDownloadShelfVisible();
#endif
}
static void ExpectWindowCountAfterDownload(size_t expected) {
#if defined(OS_CHROMEOS)
// On ChromeOS, a download panel is created to display
// download information, and this counts as a window.
expected++;
#endif
EXPECT_EQ(expected, BrowserList::size());
}
private:
// Location of the test data.
FilePath test_dir_;
// Location of the downloads directory for these tests
ScopedTempDir downloads_directory_;
};
// Get History Information.
class DownloadsHistoryDataCollector {
public:
explicit DownloadsHistoryDataCollector(int64 download_db_handle,
DownloadManager* manager)
: result_valid_(false),
download_db_handle_(download_db_handle) {
HistoryService* hs =
manager->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
DCHECK(hs);
hs->QueryDownloads(
&callback_consumer_,
NewCallback(this,
&DownloadsHistoryDataCollector::OnQueryDownloadsComplete));
// Cannot complete immediately because the history backend runs on a
// separate thread, so we can assume that the RunMessageLoop below will
// be exited by the Quit in OnQueryDownloadsComplete.
ui_test_utils::RunMessageLoop();
}
bool GetDownloadsHistoryEntry(DownloadCreateInfo* result) {
DCHECK(result);
*result = result_;
return result_valid_;
}
private:
void OnQueryDownloadsComplete(
std::vector<DownloadCreateInfo>* entries) {
result_valid_ = false;
for (std::vector<DownloadCreateInfo>::const_iterator it = entries->begin();
it != entries->end(); ++it) {
if (it->db_handle == download_db_handle_) {
result_ = *it;
result_valid_ = true;
}
}
MessageLoopForUI::current()->Quit();
}
DownloadCreateInfo result_;
bool result_valid_;
int64 download_db_handle_;
CancelableRequestConsumer callback_consumer_;
DISALLOW_COPY_AND_ASSIGN(DownloadsHistoryDataCollector);
};
// NOTES:
//
// Files for these tests are found in DIR_TEST_DATA (currently
// "chrome\test\data\", see chrome_paths.cc).
// Mock responses have extension .mock-http-headers appended to the file name.
// Download a file due to the associated MIME type.
IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadMimeType) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
// Download the file and wait. We do not expect the Select File dialog.
DownloadAndWait(browser(), url, EXPECT_NO_SELECT_DIALOG);
// Check state.
EXPECT_EQ(1, browser()->tab_count());
CheckDownload(browser(), file, file);
EXPECT_TRUE(IsDownloadUIVisible(browser()));
}
#if defined(OS_WIN)
// Download a file and confirm that the zone identifier (on windows)
// is set to internet.
IN_PROC_BROWSER_TEST_F(DownloadTest, CheckInternetZone) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
// Download the file and wait. We do not expect the Select File dialog.
DownloadAndWait(browser(), url, EXPECT_NO_SELECT_DIALOG);
// Check state. Special file state must be checked before CheckDownload,
// as CheckDownload will delete the output file.
EXPECT_EQ(1, browser()->tab_count());
FilePath downloaded_file(DestinationFile(browser(), file));
if (file_util::VolumeSupportsADS(downloaded_file))
EXPECT_TRUE(file_util::HasInternetZoneIdentifier(downloaded_file));
CheckDownload(browser(), file, file);
EXPECT_TRUE(IsDownloadUIVisible(browser()));
}
#endif
// Put up a Select File dialog when the file is downloaded, due to its MIME
// type.
//
// This test runs correctly, but leaves behind turds in the test user's
// download directory because of http://crbug.com/62099. No big loss; it
// was primarily confirming DownloadsObserver wait on select file dialog
// functionality anyway.
IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_DownloadMimeTypeSelect) {
ASSERT_TRUE(InitialSetup(true));
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
// Download the file and wait. We expect the Select File dialog to appear
// due to the MIME type.
DownloadAndWait(browser(), url, EXPECT_SELECT_DIALOG);
// Check state.
EXPECT_EQ(1, browser()->tab_count());
// Since we exited while the Select File dialog was visible, there should not
// be anything in the download shelf and so it should not be visible.
EXPECT_FALSE(IsDownloadUIVisible(browser()));
}
// Access a file with a viewable mime-type, verify that a download
// did not initiate.
IN_PROC_BROWSER_TEST_F(DownloadTest, NoDownload) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test2.html"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
FilePath file_path(DestinationFile(browser(), file));
// Open a web page and wait.
ui_test_utils::NavigateToURL(browser(), url);
// Check that we did not download the web page.
EXPECT_FALSE(file_util::PathExists(file_path));
// Check state.
EXPECT_EQ(1, browser()->tab_count());
EXPECT_FALSE(IsDownloadUIVisible(browser()));
}
// Download a 0-size file with a content-disposition header, verify that the
// download tab opened and the file exists as the filename specified in the
// header. This also ensures we properly handle empty file downloads.
// The download shelf should be visible in the current tab.
IN_PROC_BROWSER_TEST_F(DownloadTest, ContentDisposition) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test3.gif"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
FilePath download_file(FILE_PATH_LITERAL("download-test3-attachment.gif"));
// Download a file and wait.
DownloadAndWait(browser(), url, EXPECT_NO_SELECT_DIALOG);
CheckDownload(browser(), download_file, file);
// Check state.
EXPECT_EQ(1, browser()->tab_count());
EXPECT_TRUE(IsDownloadUIVisible(browser()));
}
#if !defined(OS_CHROMEOS) // Download shelf is not per-window on ChromeOS.
// Test that the download shelf is per-window by starting a download in one
// tab, opening a second tab, closing the shelf, going back to the first tab,
// and checking that the shelf is closed.
IN_PROC_BROWSER_TEST_F(DownloadTest, PerWindowShelf) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test3.gif"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
FilePath download_file(FILE_PATH_LITERAL("download-test3-attachment.gif"));
// Download a file and wait.
DownloadAndWait(browser(), url, EXPECT_NO_SELECT_DIALOG);
CheckDownload(browser(), download_file, file);
// Check state.
EXPECT_EQ(1, browser()->tab_count());
EXPECT_TRUE(IsDownloadUIVisible(browser()));
// Open a second tab and wait.
EXPECT_NE(static_cast<TabContentsWrapper*>(NULL),
browser()->AddSelectedTabWithURL(GURL(), PageTransition::TYPED));
EXPECT_EQ(2, browser()->tab_count());
EXPECT_TRUE(IsDownloadUIVisible(browser()));
// Hide the download shelf.
browser()->window()->GetDownloadShelf()->Close();
EXPECT_FALSE(IsDownloadUIVisible(browser()));
// Go to the first tab.
browser()->ActivateTabAt(0, true);
EXPECT_EQ(2, browser()->tab_count());
// The download shelf should not be visible.
EXPECT_FALSE(IsDownloadUIVisible(browser()));
}
#endif // !OS_CHROMEOS
// UnknownSize and KnownSize are tests which depend on
// URLRequestSlowDownloadJob to serve content in a certain way. Data will be
// sent in two chunks where the first chunk is 35K and the second chunk is 10K.
// The test will first attempt to download a file; but the server will "pause"
// in the middle until the server receives a second request for
// "download-finish". At that time, the download will finish.
// These tests don't currently test much due to holes in |RunSizeTest()|. See
// comments in that routine for details.
IN_PROC_BROWSER_TEST_F(DownloadTest, UnknownSize) {
ASSERT_TRUE(RunSizeTest(browser(), SIZE_TEST_TYPE_UNKNOWN,
"32.0 KB - ", "100% - "));
}
IN_PROC_BROWSER_TEST_F(DownloadTest, KnownSize) {
ASSERT_TRUE(RunSizeTest(browser(), SIZE_TEST_TYPE_KNOWN,
"71% - ", "100% - "));
}
// Test that when downloading an item in Incognito mode, we don't crash when
// closing the last Incognito window (http://crbug.com/13983).
// Also check that the download shelf is not visible after closing the
// Incognito window.
IN_PROC_BROWSER_TEST_F(DownloadTest, IncognitoDownload) {
ASSERT_TRUE(InitialSetup(false));
// Open an Incognito window.
Browser* incognito = CreateIncognitoBrowser(); // Waits.
ASSERT_TRUE(incognito);
int window_count = BrowserList::size();
EXPECT_EQ(2, window_count);
// Download a file in the Incognito window and wait.
CreateAndSetDownloadsDirectory(incognito);
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
// Since |incognito| is a separate browser, we have to set it up explicitly.
incognito->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload,
false);
DownloadAndWait(incognito, url, EXPECT_NO_SELECT_DIALOG);
// We should still have 2 windows.
ExpectWindowCountAfterDownload(2);
// Verify that the download shelf is showing for the Incognito window.
bool is_shelf_visible = IsDownloadUIVisible(incognito);
EXPECT_TRUE(is_shelf_visible);
// Close the Incognito window and don't crash.
incognito->CloseWindow();
#if !defined(OS_MACOSX)
// On Mac OS X, the UI window close is delayed until the outermost
// message loop runs. So it isn't possible to get a BROWSER_CLOSED
// notification inside of a test.
ui_test_utils::WaitForNotificationFrom(NotificationType::BROWSER_CLOSED,
Source<Browser>(incognito));
ExpectWindowCountAfterDownload(1);
#endif
// Verify that the regular window does not have a download shelf.
is_shelf_visible = IsDownloadUIVisible(browser());
#if defined(OS_CHROMEOS)
// On ChromeOS it's a popup rather than a download shelf, and it sticks
// around.
EXPECT_TRUE(is_shelf_visible);
#else
EXPECT_FALSE(is_shelf_visible);
#endif
CheckDownload(browser(), file, file);
}
// Navigate to a new background page, but don't download. Confirm that the
// download shelf is not visible and that we have two tabs.
IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab1) {
ASSERT_TRUE(InitialSetup(false));
// Because it's an HTML link, it should open a web page rather than
// downloading.
FilePath file1(FILE_PATH_LITERAL("download-test2.html"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file1));
// Open a web page and wait.
ui_test_utils::NavigateToURLWithDisposition(
browser(),
url,
NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
// We should have two tabs now.
EXPECT_EQ(2, browser()->tab_count());
EXPECT_FALSE(IsDownloadUIVisible(browser()));
}
// Download a file in a background tab. Verify that the tab is closed
// automatically, and that the download shelf is visible in the current tab.
IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab1) {
ASSERT_TRUE(InitialSetup(false));
// Download a file in a new background tab and wait. The tab is automatically
// closed when the download begins.
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
DownloadAndWaitWithDisposition(
browser(),
url,
NEW_BACKGROUND_TAB,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
// When the download finishes, we should still have one tab.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
EXPECT_EQ(1, browser()->tab_count());
CheckDownload(browser(), file, file);
}
// Open a web page in the current tab, then download a file in another tab via
// a Javascript call.
// Verify that we have 2 tabs, and the download shelf is visible in the current
// tab.
//
// The download_page1.html page contains an openNew() function that opens a
// tab and then downloads download-test1.lib.
IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab2) {
ASSERT_TRUE(InitialSetup(false));
// Because it's an HTML link, it should open a web page rather than
// downloading.
FilePath file1(FILE_PATH_LITERAL("download_page1.html"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file1));
// Open a web page and wait.
ui_test_utils::NavigateToURL(browser(), url);
// Download a file in a new tab and wait (via Javascript).
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
DownloadAndWaitWithDisposition(browser(),
GURL("javascript:openNew()"),
CURRENT_TAB,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
// When the download finishes, we should have two tabs.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
EXPECT_EQ(2, browser()->tab_count());
CheckDownload(browser(), file, file);
}
// Open a web page in the current tab, open another tab via a Javascript call,
// then download a file in the new tab.
// Verify that we have 2 tabs, and the download shelf is visible in the current
// tab.
//
// The download_page2.html page contains an openNew() function that opens a
// tab.
IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab3) {
ASSERT_TRUE(InitialSetup(false));
// Because it's an HTML link, it should open a web page rather than
// downloading.
FilePath file1(FILE_PATH_LITERAL("download_page2.html"));
GURL url1(URLRequestMockHTTPJob::GetMockUrl(file1));
// Open a web page and wait.
ui_test_utils::NavigateToURL(browser(), url1);
// Open a new tab and wait.
ui_test_utils::NavigateToURLWithDisposition(
browser(),
GURL("javascript:openNew()"),
CURRENT_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
EXPECT_EQ(2, browser()->tab_count());
// Download a file and wait.
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
DownloadAndWaitWithDisposition(browser(),
url,
CURRENT_TAB,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_NONE);
// When the download finishes, we should have two tabs.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
EXPECT_EQ(2, browser()->tab_count());
CheckDownload(browser(), file, file);
}
// Open a web page in the current tab, then download a file via Javascript,
// which will do so in a temporary tab.
// Verify that we have 1 tab, and the download shelf is visible.
//
// The download_page3.html page contains an openNew() function that opens a
// tab with download-test1.lib in the URL. When the URL is determined to be
// a download, the tab is closed automatically.
IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab2) {
ASSERT_TRUE(InitialSetup(false));
// Because it's an HTML link, it should open a web page rather than
// downloading.
FilePath file1(FILE_PATH_LITERAL("download_page3.html"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file1));
// Open a web page and wait.
ui_test_utils::NavigateToURL(browser(), url);
// Download a file and wait.
// The file to download is "download-test1.lib".
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
DownloadAndWaitWithDisposition(browser(),
GURL("javascript:openNew()"),
CURRENT_TAB,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
// When the download finishes, we should still have one tab.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
EXPECT_EQ(1, browser()->tab_count());
CheckDownload(browser(), file, file);
}
// Open a web page in the current tab, then call Javascript via a button to
// download a file in a new tab, which is closed automatically when the
// download begins.
// Verify that we have 1 tab, and the download shelf is visible.
//
// The download_page4.html page contains a form with download-test1.lib as the
// action.
IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab3) {
ASSERT_TRUE(InitialSetup(false));
// Because it's an HTML link, it should open a web page rather than
// downloading.
FilePath file1(FILE_PATH_LITERAL("download_page4.html"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file1));
// Open a web page and wait.
ui_test_utils::NavigateToURL(browser(), url);
// Download a file in a new tab and wait. The tab will automatically close
// when the download begins.
// The file to download is "download-test1.lib".
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
DownloadAndWaitWithDisposition(
browser(),
GURL("javascript:document.getElementById('form').submit()"),
CURRENT_TAB,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
// When the download finishes, we should still have one tab.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
EXPECT_EQ(1, browser()->tab_count());
CheckDownload(browser(), file, file);
}
// Download a file in a new window.
// Verify that we have 2 windows, and the download shelf is not visible in the
// first window, but is visible in the second window.
// Close the new window.
// Verify that we have 1 window, and the download shelf is not visible.
//
// Regression test for http://crbug.com/44454
IN_PROC_BROWSER_TEST_F(DownloadTest, NewWindow) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
#if !defined(OS_MACOSX)
// See below.
Browser* first_browser = browser();
#endif
// Download a file in a new window and wait.
DownloadAndWaitWithDisposition(browser(),
url,
NEW_WINDOW,
EXPECT_NO_SELECT_DIALOG,
ui_test_utils::BROWSER_TEST_NONE);
// When the download finishes, the download shelf SHOULD NOT be visible in
// the first window.
ExpectWindowCountAfterDownload(2);
EXPECT_EQ(1, browser()->tab_count());
#if defined(OS_CHROMEOS)
// Except on chromeos the download UI isn't window-specific.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
#else
EXPECT_FALSE(IsDownloadUIVisible(browser()));
#endif
// The download shelf SHOULD be visible in the second window.
std::set<Browser*> original_browsers;
original_browsers.insert(browser());
Browser* download_browser =
ui_test_utils::GetBrowserNotInSet(original_browsers);
ASSERT_TRUE(download_browser != NULL);
EXPECT_NE(download_browser, browser());
EXPECT_EQ(1, download_browser->tab_count());
EXPECT_TRUE(IsDownloadUIVisible(download_browser));
// Close the new window.
download_browser->CloseWindow();
#if !defined(OS_MACOSX)
// On Mac OS X, the UI window close is delayed until the outermost
// message loop runs. So it isn't possible to get a BROWSER_CLOSED
// notification inside of a test.
ui_test_utils::WaitForNotificationFrom(NotificationType::BROWSER_CLOSED,
Source<Browser>(download_browser));
EXPECT_EQ(first_browser, browser());
ExpectWindowCountAfterDownload(1);
#endif
EXPECT_EQ(1, browser()->tab_count());
#if defined(OS_CHROMEOS)
// On ChromeOS the popup sticks around.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
#else
// Otherwise, the download shelf should not be visible in the
// remaining window.
EXPECT_FALSE(IsDownloadUIVisible(browser()));
#endif
CheckDownload(browser(), file, file);
}
IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadCancelled) {
ASSERT_TRUE(InitialSetup(false));
EXPECT_EQ(1, browser()->tab_count());
// TODO(rdsmith): Fragile code warning! The code below relies on the
// DownloadsObserver only finishing when the new download has reached
// the state of being entered into the history and being user-visible
// (that's what's required for the Remove to be valid and for the
// download shelf to be visible). By the pure semantics of
// DownloadsObserver, that's not guaranteed; DownloadItems are created
// in the IN_PROGRESS state and made known to the DownloadManager
// immediately, so any ModelChanged event on the DownloadManager after
// navigation would allow the observer to return. However, the only
// ModelChanged() event the code will currently fire is in
// OnCreateDownloadEntryComplete, at which point the download item will
// be in the state we need.
// The right way to fix this is to create finer grained states on the
// DownloadItem, and wait for the state that indicates the item has been
// entered in the history and made visible in the UI.
// Create a download, wait until it's started, and confirm
// we're in the expected state.
scoped_ptr<DownloadsObserver> observer(CreateInProgressWaiter(browser(), 1));
ui_test_utils::NavigateToURL(
browser(), GURL(URLRequestSlowDownloadJob::kUnknownSizeUrl));
observer->WaitForFinished();
std::vector<DownloadItem*> downloads;
browser()->profile()->GetDownloadManager()->SearchDownloads(
string16(), &downloads);
ASSERT_EQ(1u, downloads.size());
ASSERT_EQ(DownloadItem::IN_PROGRESS, downloads[0]->state());
EXPECT_TRUE(IsDownloadUIVisible(browser()));
// Cancel the download and wait for download system quiesce.
downloads[0]->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
scoped_refptr<DownloadsFlushObserver> flush_observer(
new DownloadsFlushObserver(browser()->profile()->GetDownloadManager()));
flush_observer->WaitForFlush();
// Get the important info from other threads and check it.
scoped_refptr<CancelTestDataCollector> info(new CancelTestDataCollector());
info->WaitForDataCollected();
EXPECT_EQ(0, info->rdh_pending_requests());
EXPECT_EQ(0, info->dfm_pending_downloads());
// Using "DownloadItem::Remove" follows the discard dangerous download path,
// which completely removes the browser from the shelf and closes the shelf
// if it was there.
#if defined(OS_CHROMEOS)
// Except under ChromeOS in which case if we've brought up the file
// picker panel, it stays.
EXPECT_TRUE(IsDownloadUIVisible(browser()));
#else
EXPECT_FALSE(IsDownloadUIVisible(browser()));
#endif
}
// Confirm a download makes it into the history properly.
IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadHistoryCheck) {
ASSERT_TRUE(InitialSetup(false));
FilePath file(FILE_PATH_LITERAL("download-test1.lib"));
GURL url(URLRequestMockHTTPJob::GetMockUrl(file));
FilePath origin_file(OriginFile(file));
int64 origin_size;
file_util::GetFileSize(origin_file, &origin_size);
// Download the file and wait. We do not expect the Select File dialog.
DownloadAndWait(browser(), url, EXPECT_NO_SELECT_DIALOG);
// Get details of what downloads have just happened.
std::vector<DownloadItem*> downloads;
GetDownloads(browser(), &downloads);
ASSERT_EQ(1u, downloads.size());
int64 db_handle = downloads[0]->db_handle();
// Check state.
EXPECT_EQ(1, browser()->tab_count());
CheckDownload(browser(), file, file);
EXPECT_TRUE(IsDownloadUIVisible(browser()));
// Check history results.
DownloadsHistoryDataCollector history_collector(
db_handle,
browser()->profile()->GetDownloadManager());
DownloadCreateInfo info;
EXPECT_TRUE(history_collector.GetDownloadsHistoryEntry(&info)) << db_handle;
EXPECT_EQ(file, info.path.BaseName());
EXPECT_EQ(url, info.url());
// Ignore start_time.
EXPECT_EQ(origin_size, info.received_bytes);
EXPECT_EQ(origin_size, info.total_bytes);
EXPECT_EQ(DownloadItem::COMPLETE, info.state);
}