diff options
author | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-24 19:37:03 +0000 |
---|---|---|
committer | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-24 19:37:03 +0000 |
commit | 5a17d49ad15cedcfc396308358eba00d45816bb3 (patch) | |
tree | 714fbbbcf970b7d2217b67126acaf68a634eb485 /chrome/browser/ui/browser_close_browsertest.cc | |
parent | 32a28289f14c656088243e9b3dfb33229eb492d1 (diff) | |
download | chromium_src-5a17d49ad15cedcfc396308358eba00d45816bb3.zip chromium_src-5a17d49ad15cedcfc396308358eba00d45816bb3.tar.gz chromium_src-5a17d49ad15cedcfc396308358eba00d45816bb3.tar.bz2 |
Fix warning prompting on closing a window that will cancel downloads.
Handles two cases: Last window close (-> shuts download browser), and
last incognito window in an incognito profile (-> cancels downloads
on that profile).
Note that this doesn't cover the macintosh, which goes through different
code (http://crbug.com/88419) and the warning for incognito close is not
ideal (http://crbug.com/88421). This CL includes some modularization
to make resolving those issues easier.
BUG=61257
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=105662
Review URL: http://codereview.chromium.org/7466033
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106958 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/browser_close_browsertest.cc')
-rw-r--r-- | chrome/browser/ui/browser_close_browsertest.cc | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/chrome/browser/ui/browser_close_browsertest.cc b/chrome/browser/ui/browser_close_browsertest.cc new file mode 100644 index 0000000..be7386c --- /dev/null +++ b/chrome/browser/ui/browser_close_browsertest.cc @@ -0,0 +1,551 @@ +// 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/command_line.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/stringprintf.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/download/download_service.h" +#include "chrome/browser/download/download_service_factory.h" +#include "chrome/browser/download/download_test_observer.h" +#include "chrome/browser/net/url_request_mock_util.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/webui/active_downloads_ui.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/browser/download/download_item.h" +#include "content/browser/net/url_request_slow_download_job.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/public/common/page_transition_types.h" + +class BrowserCloseTest : public InProcessBrowserTest { + public: + // Structure defining test cases for DownloadsCloseCheck. + struct DownloadsCloseCheckCase { + std::string DebugString() const; + + // Input + struct { + struct { + int windows; + int downloads; + } regular; + struct { + int windows; + int downloads; + } incognito; + } profile_a; + + struct { + struct { + int windows; + int downloads; + } regular; + struct { + int windows; + int downloads; + } incognito; + } profile_b; + + // We always probe a window in profile A. + enum { REGULAR = 0, INCOGNITO = 1 } window_to_probe; + + // Output + Browser::DownloadClosePreventionType type; + + // Unchecked if type == DOWNLOAD_CLOSE_OK. + int num_blocking; + }; + + protected: + virtual void SetUpOnMainThread() OVERRIDE { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); + } + + // Create a second profile to work within multi-profile. + Profile* CreateSecondProfile() { + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + + if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir)) + return NULL; + + Profile* second_profile = + g_browser_process->profile_manager()->GetProfile( + second_profile_data_dir_.path()); + if (!second_profile) + return NULL; + + bool result = second_profile_downloads_dir_.CreateUniqueTempDir(); + if (!result) + return NULL; + second_profile->GetPrefs()->SetFilePath( + prefs::kDownloadDefaultDirectory, + second_profile_downloads_dir_.path()); + + return second_profile; + } + + // Create |num_downloads| number of downloads that are stalled + // (will quickly get to a place where the server won't + // provide any more data) so that we can test closing the + // browser with active downloads. + void CreateStalledDownloads(Browser* browser, int num_downloads) { + GURL url(URLRequestSlowDownloadJob::kKnownSizeUrl); + + if (num_downloads == 0) + return; + + // Setup an observer waiting for the given number of downloads + // to get to IN_PROGRESS. + DownloadManager* download_manager = + browser->profile()->GetDownloadManager(); + scoped_ptr<DownloadTestObserver> observer( + new DownloadTestObserver( + download_manager, num_downloads, + DownloadItem::IN_PROGRESS, + true, // Bail on select file. + DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); + + // Set of that number of downloads. + while (num_downloads--) + ui_test_utils::NavigateToURLWithDisposition( + browser, url, NEW_BACKGROUND_TAB, + ui_test_utils::BROWSER_TEST_NONE); + + // Wait for them. + observer->WaitForFinished(); + } + + // All all downloads created in CreateStalledDownloads() to + // complete, and block in this routine until they do complete. + void CompleteAllDownloads(Browser* browser) { + GURL finish_url(URLRequestSlowDownloadJob::kFinishDownloadUrl); + ui_test_utils::NavigateToURL(browser, finish_url); + + // Go through and, for every single profile, wait until there are + // no active downloads on that download manager. + std::vector<Profile*> profiles( + g_browser_process->profile_manager()->GetLoadedProfiles()); + for (std::vector<Profile*>::const_iterator pit = profiles.begin(); + pit != profiles.end(); ++pit) { + DownloadService* download_service = + DownloadServiceFactory::GetForProfile(*pit); + if (download_service->HasCreatedDownloadManager()) { + DownloadManager *mgr = download_service->GetDownloadManager(); + scoped_refptr<DownloadTestFlushObserver> observer( + new DownloadTestFlushObserver(mgr)); + observer->WaitForFlush(); + } + if ((*pit)->HasOffTheRecordProfile()) { + DownloadService* incognito_download_service = + DownloadServiceFactory::GetForProfile( + (*pit)->GetOffTheRecordProfile()); + if (incognito_download_service->HasCreatedDownloadManager()) { + DownloadManager *mgr = + incognito_download_service->GetDownloadManager(); + scoped_refptr<DownloadTestFlushObserver> observer( + new DownloadTestFlushObserver(mgr)); + observer->WaitForFlush(); + } + } + } + } + + // Create a Browser (with associated window) on the specified profile. + Browser* CreateBrowserOnProfile(Profile* profile) { + Browser* new_browser = Browser::Create(profile); + new_browser->AddSelectedTabWithURL(GURL(chrome::kAboutBlankURL), + content::PAGE_TRANSITION_START_PAGE); + ui_test_utils::WaitForLoadStop( + new_browser->GetSelectedTabContents()); + new_browser->window()->Show(); + return new_browser; + } + + // Adjust the number of browsers and associated windows up or down + // to |num_windows|. This routine assumes that there is only a single + // browser associated with the profile on entry. |*base_browser| contains + // this browser, and the profile is derived from that browser. On output, + // if |*base_browser| was destroyed (because |num_windows == 0|), NULL + // is assigned to that memory location. + bool AdjustBrowsersOnProfile(Browser** base_browser, int num_windows) { + int num_downloads_blocking; + if (num_windows == 0) { + if (Browser::DOWNLOAD_CLOSE_OK != + (*base_browser)->OkToCloseWithInProgressDownloads( + &num_downloads_blocking)) + return false; + (*base_browser)->window()->Close(); + *base_browser = 0; + return true; + } + + // num_windows > 0 + Profile* profile((*base_browser)->profile()); + for (int w = 1; w < num_windows; ++w) { + CreateBrowserOnProfile(profile); + } + return true; + } + + int TotalUnclosedBrowsers() { + int count = 0; + for (BrowserList::const_iterator iter = BrowserList::begin(); + iter != BrowserList::end(); ++iter) + if (!(*iter)->IsAttemptingToCloseBrowser()) { + count++; + } + return count; + } + + // Note that this is invalid to call if TotalUnclosedBrowsers() == 0. + Browser* FirstUnclosedBrowser() { + for (BrowserList::const_iterator iter = BrowserList::begin(); + iter != BrowserList::end(); ++iter) + if (!(*iter)->IsAttemptingToCloseBrowser()) + return (*iter); + return NULL; + } + + bool SetupForDownloadCloseCheck() { + first_profile_ = browser()->profile(); + + bool result = first_profile_downloads_dir_.CreateUniqueTempDir(); + EXPECT_TRUE(result); + if (!result) return false; + first_profile_->GetPrefs()->SetFilePath( + prefs::kDownloadDefaultDirectory, + first_profile_downloads_dir_.path()); + + second_profile_ = CreateSecondProfile(); + EXPECT_TRUE(second_profile_); + if (!second_profile_) return false; + + return true; + } + + // Test a specific DownloadsCloseCheckCase. Returns false if + // an assertion has failed and the test should be aborted. + bool ExecuteDownloadCloseCheckCase(size_t i) { + const DownloadsCloseCheckCase& check_case(download_close_check_cases[i]); + + // Test invariant: so that we don't actually try and close the browser, + // we always enter the function with a single browser window open on the + // main profile. That means we need to exit the function the same way. + // So we setup everything except for the |first_profile_| regular, and then + // flip the bit on the main window. + // Note that this means that browser() is unreliable in the context + // of this function or its callers; we'll be killing that main window + // and recreating it fairly frequently. + int unclosed_browsers = TotalUnclosedBrowsers(); + EXPECT_EQ(1, unclosed_browsers); + if (1 != unclosed_browsers) + return false; + + Browser* entry_browser = FirstUnclosedBrowser(); + EXPECT_EQ(first_profile_, entry_browser->profile()) + << "Case" << i + << ": " << check_case.DebugString(); + if (first_profile_ != entry_browser->profile()) + return false; + int total_download_count = DownloadService::DownloadCountAllProfiles(); + EXPECT_EQ(0, total_download_count) + << "Case " << i + << ": " << check_case.DebugString(); + if (0 != total_download_count) + return false; + + Profile* first_profile_incognito = first_profile_->GetOffTheRecordProfile(); + Profile* second_profile_incognito = + second_profile_->GetOffTheRecordProfile(); + + // For simplicty of coding, we create a window on each profile so that + // we can easily create downloads, then we destroy or create windows + // as necessary. + Browser* browser_a_regular(CreateBrowserOnProfile(first_profile_)); + Browser* browser_a_incognito( + CreateBrowserOnProfile(first_profile_incognito)); + Browser* browser_b_regular(CreateBrowserOnProfile(second_profile_)); + Browser* browser_b_incognito( + CreateBrowserOnProfile(second_profile_incognito)); + + // Kill our entry browser. + entry_browser->window()->Close(); + entry_browser = NULL; + + // Create all downloads needed. + CreateStalledDownloads( + browser_a_regular, check_case.profile_a.regular.downloads); + CreateStalledDownloads( + browser_a_incognito, check_case.profile_a.incognito.downloads); + CreateStalledDownloads( + browser_b_regular, check_case.profile_b.regular.downloads); + CreateStalledDownloads( + browser_b_incognito, check_case.profile_b.incognito.downloads); + + // Adjust the windows + Browser** browsers[] = { + &browser_a_regular, &browser_a_incognito, + &browser_b_regular, &browser_b_incognito + }; + int window_counts[] = { + check_case.profile_a.regular.windows, + check_case.profile_a.incognito.windows, + check_case.profile_b.regular.windows, + check_case.profile_b.incognito.windows, + }; + for (size_t j = 0; j < arraysize(browsers); ++j) { + bool result = AdjustBrowsersOnProfile(browsers[j], window_counts[j]); + EXPECT_TRUE(result); + if (!result) + return false; + } + ui_test_utils::RunAllPendingInMessageLoop(); + +#if defined(OS_CHROMEOS) + // Get rid of downloads panel on ChromeOS + Browser* panel = NULL; +#if defined(TOUCH_UI) + ActiveDownloadsUI::GetPopup(&panel); +#else + panel = ActiveDownloadsUI::GetPopup(); +#endif + if (panel) + panel->CloseWindow(); + ui_test_utils::RunAllPendingInMessageLoop(); +#endif + + // All that work, for this one little test. + EXPECT_TRUE((check_case.window_to_probe == + DownloadsCloseCheckCase::REGULAR) || + (check_case.window_to_probe == + DownloadsCloseCheckCase::INCOGNITO)); + if (!((check_case.window_to_probe == + DownloadsCloseCheckCase::REGULAR) || + (check_case.window_to_probe == + DownloadsCloseCheckCase::INCOGNITO))) + return false; + + int num_downloads_blocking; + Browser* browser_to_probe = + (check_case.window_to_probe == DownloadsCloseCheckCase::REGULAR ? + browser_a_regular : + browser_a_incognito); + Browser::DownloadClosePreventionType type = + browser_to_probe->OkToCloseWithInProgressDownloads( + &num_downloads_blocking); + EXPECT_EQ(check_case.type, type) << "Case " << i + << ": " << check_case.DebugString(); + if (type != Browser::DOWNLOAD_CLOSE_OK) + EXPECT_EQ(check_case.num_blocking, num_downloads_blocking) + << "Case " << i + << ": " << check_case.DebugString(); + + // Release all the downloads. + CompleteAllDownloads(browser_to_probe); + + // Create a new main window and kill everything else. + entry_browser = CreateBrowserOnProfile(first_profile_); + for (BrowserList::const_iterator bit = BrowserList::begin(); + bit != BrowserList::end(); ++bit) { + if ((*bit) != entry_browser) { + EXPECT_TRUE((*bit)->window()); + if (!(*bit)->window()) + return false; + (*bit)->window()->Close(); + } + } + ui_test_utils::RunAllPendingInMessageLoop(); + + return true; + } + + static const DownloadsCloseCheckCase download_close_check_cases[]; + + // DownloadCloseCheck variables. + Profile* first_profile_; + Profile* second_profile_; + + ScopedTempDir first_profile_downloads_dir_; + ScopedTempDir second_profile_data_dir_; + ScopedTempDir second_profile_downloads_dir_; +}; + +const BrowserCloseTest::DownloadsCloseCheckCase +BrowserCloseTest::download_close_check_cases[] = { + // Top level nesting is {profile_a, profile_b} + // Second level nesting is {regular, incognito + // Third level (inner) nesting is {windows, downloads} + + // Last window (incognito) triggers browser close warning. + {{{0, 0}, {1, 1}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN, 1}, + + // Last incognito window triggers incognito close warning. + {{{1, 0}, {1, 1}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_LAST_WINDOW_IN_INCOGNITO_PROFILE, 1}, + + // Last incognito window with no downloads triggers no warning. + {{{0, 0}, {1, 0}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_OK}, + + // Last incognito window with window+download on another incognito profile + // triggers no warning. + {{{0, 0}, {1, 0}}, {{0, 0}, {1, 1}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_OK}, + + // Non-last incognito window triggers no warning. + {{{0, 0}, {2, 1}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_OK}, + + // Non-last regular window triggers no warning. + {{{2, 1}, {0, 0}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_OK}, + + // Last regular window triggers browser close. + {{{1, 1}, {0, 0}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN, 1}, + + // Last regular window triggers browser close for download on different + // profile. + {{{1, 0}, {0, 0}}, {{0, 1}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN, 1}, + + // Last regular window triggers no warning if incognito + // active (http://crbug.com/61257). + {{{1, 0}, {1, 1}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_OK}, + + // Last regular window triggers no warning if other profile window active. + {{{1, 1}, {0, 0}}, {{1, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_OK}, + + // Last regular window triggers no warning if other incognito window + // active. + {{{1, 0}, {0, 0}}, {{0, 0}, {1, 1}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_OK}, + + // Last regular window triggers no warning if incognito active. + {{{1, 1}, {1, 0}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_OK}, + + // Test plural for regular. + {{{1, 2}, {0, 0}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::REGULAR, + Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN, 2}, + + // Test plural for incognito. + {{{1, 0}, {1, 2}}, {{0, 0}, {0, 0}}, + BrowserCloseTest::DownloadsCloseCheckCase::INCOGNITO, + Browser::DOWNLOAD_CLOSE_LAST_WINDOW_IN_INCOGNITO_PROFILE, 2}, +}; + +std::string BrowserCloseTest::DownloadsCloseCheckCase::DebugString() const { + std::string result; + result += "{"; + if (profile_a.regular.windows || profile_a.regular.downloads) + result += base::StringPrintf("Regular profile A: (%d w, %d d), ", + profile_a.regular.windows, + profile_a.regular.downloads); + if (profile_a.incognito.windows || profile_a.incognito.downloads) + result += base::StringPrintf("Incognito profile A: (%d w, %d d), ", + profile_a.incognito.windows, + profile_a.incognito.downloads); + if (profile_b.regular.windows || profile_b.regular.downloads) + result += base::StringPrintf("Regular profile B: (%d w, %d d), ", + profile_b.regular.windows, + profile_b.regular.downloads); + if (profile_b.incognito.windows || profile_b.incognito.downloads) + result += base::StringPrintf("Incognito profile B: (%d w, %d d), ", + profile_b.incognito.windows, + profile_b.incognito.downloads); + result += (window_to_probe == REGULAR ? "Probe regular" : + window_to_probe == INCOGNITO ? "Probe incognito" : + "Probe unknown"); + result += "} -> "; + if (type == Browser::DOWNLOAD_CLOSE_OK) { + result += "No warning"; + } else { + result += base::StringPrintf( + "%s (%d downloads) warning", + (type == Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN ? "Browser shutdown" : + type == Browser::DOWNLOAD_CLOSE_LAST_WINDOW_IN_INCOGNITO_PROFILE ? + "Incognito close" : "Unknown"), + num_blocking); + } + return result; +} + +// The following test is split into three chunks to reduce the chance +// of hitting the 25s timeout. + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_0) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = 0; i < arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_1) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = arraysize(download_close_check_cases) / 6; + i < 2 * arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_2) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = 2 * arraysize(download_close_check_cases) / 6; + i < 3 * arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_3) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = 3 * arraysize(download_close_check_cases) / 6; + i < 4 * arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_4) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = 4 * arraysize(download_close_check_cases) / 6; + i < 5 * arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} + +IN_PROC_BROWSER_TEST_F(BrowserCloseTest, DownloadsCloseCheck_5) { + ASSERT_TRUE(SetupForDownloadCloseCheck()); + for (size_t i = 5 * arraysize(download_close_check_cases) / 6; + i < 6 * arraysize(download_close_check_cases) / 6; ++i) { + ExecuteDownloadCloseCheckCase(i); + } +} |