Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1297)

Unified Diff: content/browser/download/download_browsertest.cc

Issue 10950015: Shift "commit point" for when a download will no longer accept cancels. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Incorporated comments. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/browser/download/download_file.h » ('j') | content/browser/download/download_file.h » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/download/download_browsertest.cc
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 6fab6e25e38dc4f0b6836bba8233aedb996fd2e0..46030b1e08c42065384c72aa98fd76f078939263 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -8,10 +8,16 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/scoped_temp_dir.h"
+#include "content/browser/download/download_file_factory.h"
+#include "content/browser/download/download_file_impl.h"
+#include "content/browser/download/download_file_manager.h"
#include "content/browser/download/download_item_impl.h"
#include "content/browser/download/download_manager_impl.h"
+#include "content/browser/power_save_blocker.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/test/download_test_observer.h"
+#include "content/public/test/test_utils.h"
#include "content/shell/shell.h"
#include "content/shell/shell_browser_context.h"
#include "content/shell/shell_download_manager_delegate.h"
@@ -25,9 +31,197 @@ namespace content {
namespace {
-static DownloadManager* DownloadManagerForShell(Shell* shell) {
- return BrowserContext::GetDownloadManager(
- shell->web_contents()->GetBrowserContext());
+class DownloadFileWithDelayFactory;
+
+static DownloadManagerImpl* DownloadManagerForShell(Shell* shell) {
+ // We're in a content_browsertest; we know that the DownloadManager
+ // is a DownloadManagerImpl.
+ return static_cast<DownloadManagerImpl*>(
+ BrowserContext::GetDownloadManager(
+ shell->web_contents()->GetBrowserContext()));
+}
+
+class DownloadFileWithDelay : public DownloadFileImpl {
+ public:
+ DownloadFileWithDelay(
+ const DownloadCreateInfo* info,
+ scoped_ptr<content::ByteStreamReader> stream,
+ DownloadRequestHandleInterface* request_handle,
+ scoped_refptr<content::DownloadManager> download_manager,
+ bool calculate_hash,
+ scoped_ptr<content::PowerSaveBlocker> power_save_blocker,
+ const net::BoundNetLog& bound_net_log,
+ // |owner| is required to outlive the DownloadFileWithDelay.
+ DownloadFileWithDelayFactory* owner);
+
+ virtual ~DownloadFileWithDelay();
+
+ // Wraps DownloadFileImpl::Rename and intercepts the return callback,
+ // storing it in the factory that produced this object for later
+ // retrieval.
+ virtual void Rename(const FilePath& full_path,
+ bool overwrite_existing_file,
+ const RenameCompletionCallback& callback) OVERRIDE;
+
+ // Wraps DownloadFileImpl::Detach and intercepts the return callback,
+ // storing it in the factory that produced this object for later
+ // retrieval.
+ virtual void Detach(base::Closure callback) OVERRIDE;
+
+ private:
+ static void RenameCallbackWrapper(
+ DownloadFileWithDelayFactory* factory,
+ const RenameCompletionCallback& original_callback,
+ content::DownloadInterruptReason reason,
+ const FilePath& path);
+
+ static void DetachCallbackWrapper(
+ DownloadFileWithDelayFactory* factory,
+ const base::Closure& original_callback);
+
+ // May only be used on the UI thread.
+ DownloadFileWithDelayFactory* owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadFileWithDelay);
+};
+
+class DownloadFileWithDelayFactory : public DownloadFileFactory {
+ public:
+ DownloadFileWithDelayFactory();
+ virtual ~DownloadFileWithDelayFactory();
+
+ // DownloadFileFactory interface.
+ virtual content::DownloadFile* CreateFile(
+ DownloadCreateInfo* info,
+ scoped_ptr<content::ByteStreamReader> stream,
+ DownloadManager* download_manager,
+ bool calculate_hash,
+ const net::BoundNetLog& bound_net_log) OVERRIDE;
+
+ // Must all be called on the UI thread.
+ void AddRenameCallback(base::Closure callback);
+ void AddDetachCallback(base::Closure callback);
+ void GetAllRenameCallbacks(std::vector<base::Closure>* results);
+ void GetAllDetachCallbacks(std::vector<base::Closure>* results);
+
+ // Do not return until either GetAllRenameCallbacks() or
+ // GetAllDetachCallbacks() will return a non-empty list.
+ void WaitForSomeCallback();
+
+ private:
+ std::vector<base::Closure> rename_callbacks_;
+ std::vector<base::Closure> detach_callbacks_;
+ bool waiting_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadFileWithDelayFactory);
+};
+
+DownloadFileWithDelay::DownloadFileWithDelay(
+ const DownloadCreateInfo* info,
+ scoped_ptr<content::ByteStreamReader> stream,
+ DownloadRequestHandleInterface* request_handle,
+ scoped_refptr<content::DownloadManager> download_manager,
+ bool calculate_hash,
+ scoped_ptr<content::PowerSaveBlocker> power_save_blocker,
+ const net::BoundNetLog& bound_net_log,
+ DownloadFileWithDelayFactory* owner)
+ : DownloadFileImpl(info, stream.Pass(), request_handle, download_manager,
+ calculate_hash, power_save_blocker.Pass(),
+ bound_net_log),
+ owner_(owner) {}
+
+DownloadFileWithDelay::~DownloadFileWithDelay() {}
+
+void DownloadFileWithDelay::Rename(const FilePath& full_path,
+ bool overwrite_existing_file,
+ const RenameCompletionCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DownloadFileImpl::Rename(
+ full_path, overwrite_existing_file,
+ base::Bind(DownloadFileWithDelay::RenameCallbackWrapper,
+ base::Unretained(owner_), callback));
+}
+
+void DownloadFileWithDelay::Detach(base::Closure callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DownloadFileImpl::Detach(
+ base::Bind(DownloadFileWithDelay::DetachCallbackWrapper,
+ base::Unretained(owner_), callback));
+}
+
+// static
+void DownloadFileWithDelay::RenameCallbackWrapper(
+ DownloadFileWithDelayFactory* factory,
+ const RenameCompletionCallback& original_callback,
+ content::DownloadInterruptReason reason,
+ const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ factory->AddRenameCallback(base::Bind(original_callback, reason, path));
+}
+
+// static
+void DownloadFileWithDelay::DetachCallbackWrapper(
+ DownloadFileWithDelayFactory* factory,
+ const base::Closure& original_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ factory->AddDetachCallback(original_callback);
+}
+
+DownloadFileWithDelayFactory::DownloadFileWithDelayFactory()
+ : waiting_(false) {}
+DownloadFileWithDelayFactory::~DownloadFileWithDelayFactory() {}
+
+DownloadFile* DownloadFileWithDelayFactory::CreateFile(
+ DownloadCreateInfo* info,
+ scoped_ptr<content::ByteStreamReader> stream,
+ DownloadManager* download_manager,
+ bool calculate_hash,
+ const net::BoundNetLog& bound_net_log) {
+
+ return new DownloadFileWithDelay(
+ info, stream.Pass(), new DownloadRequestHandle(info->request_handle),
+ download_manager, calculate_hash,
+ scoped_ptr<content::PowerSaveBlocker>(
+ new content::PowerSaveBlocker(
+ content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
+ "Download in progress")).Pass(),
+ bound_net_log, this);
+}
+
+void DownloadFileWithDelayFactory::AddRenameCallback(base::Closure callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ rename_callbacks_.push_back(callback);
+ if (waiting_)
+ MessageLoopForUI::current()->Quit();
+}
+
+void DownloadFileWithDelayFactory::AddDetachCallback(base::Closure callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ detach_callbacks_.push_back(callback);
+ if (waiting_)
+ MessageLoopForUI::current()->Quit();
+}
+
+void DownloadFileWithDelayFactory::GetAllRenameCallbacks(
+ std::vector<base::Closure>* results) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ results->swap(rename_callbacks_);
+}
+
+void DownloadFileWithDelayFactory::GetAllDetachCallbacks(
+ std::vector<base::Closure>* results) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ results->swap(detach_callbacks_);
+}
+
+void DownloadFileWithDelayFactory::WaitForSomeCallback() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (rename_callbacks_.empty() && detach_callbacks_.empty()) {
+ waiting_ = true;
+ RunMessageLoop();
+ waiting_ = false;
+ }
}
} // namespace
@@ -114,6 +308,11 @@ class DownloadContentTest : public ContentBrowserTest {
return true;
}
+ DownloadFileManager* GetDownloadFileManager() {
+ ResourceDispatcherHostImpl* rdh(ResourceDispatcherHostImpl::Get());
+ return rdh->download_file_manager();
+ }
+
private:
static void EnsureNoPendingDownloadJobsOnIO(bool* result) {
if (URLRequestSlowDownloadJob::NumberOutstandingRequests())
@@ -127,18 +326,18 @@ class DownloadContentTest : public ContentBrowserTest {
};
IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadCancelled) {
- // TODO(rdsmith): Fragile code warning! The code below relies on the
- // DownloadTestObserverInProgress 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
- // DownloadTestObserverInProgress, 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.
+ // TODO(rdsmith): Fragile code warning! The code below relies on
+ // the DownloadTestObserverInProgress 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). By the pure semantics of
+ // DownloadTestObserverInProgress, 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.
@@ -220,4 +419,110 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, MultiDownload) {
file2, GetTestFilePath("download", "download-test.lib")));
}
+// Try to cancel just before we release the download file, by delaying final
+// rename callback.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelAtFinalRename) {
+ // Setup new factory.
+ DownloadFileWithDelayFactory* file_factory =
+ new DownloadFileWithDelayFactory();
+ GetDownloadFileManager()->SetFileFactoryForTesting(
+ scoped_ptr<content::DownloadFileFactory>(file_factory).Pass());
+
+ // Create a download
+ FilePath file(FILE_PATH_LITERAL("download-test.lib"));
+ NavigateToURL(shell(), URLRequestMockHTTPJob::GetMockUrl(file));
+
+ // Wait until the first (intermediate file) rename and execute the callback.
+ file_factory->WaitForSomeCallback();
+ std::vector<base::Closure> callbacks;
+ file_factory->GetAllDetachCallbacks(&callbacks);
+ ASSERT_TRUE(callbacks.empty());
+ file_factory->GetAllRenameCallbacks(&callbacks);
+ ASSERT_EQ(1u, callbacks.size());
+ callbacks[0].Run();
+ callbacks.clear();
+
+ // Wait until the second (final) rename callback is posted.
+ file_factory->WaitForSomeCallback();
+ file_factory->GetAllDetachCallbacks(&callbacks);
+ ASSERT_TRUE(callbacks.empty());
+ file_factory->GetAllRenameCallbacks(&callbacks);
+ ASSERT_EQ(1u, callbacks.size());
+
+ // Cancel it.
+ std::vector<DownloadItem*> items;
+ DownloadManagerForShell(shell())->GetAllDownloads(&items);
+ ASSERT_EQ(1u, items.size());
+ items[0]->Cancel(true);
+ RunAllPendingInMessageLoop();
+
+ // Check state.
+ EXPECT_EQ(DownloadItem::CANCELLED, items[0]->GetState());
+
+ // Run final rename callback.
+ callbacks[0].Run();
+ callbacks.clear();
+
+ // Check state.
+ EXPECT_EQ(DownloadItem::CANCELLED, items[0]->GetState());
+}
+
+// Try to cancel just after we release the download file, by delaying
+// release.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelAtRelease) {
+ // Setup new factory.
+ // Setup new factory.
+ DownloadFileWithDelayFactory* file_factory =
+ new DownloadFileWithDelayFactory();
+ GetDownloadFileManager()->SetFileFactoryForTesting(
+ scoped_ptr<content::DownloadFileFactory>(file_factory).Pass());
+
+ // Create a download
+ FilePath file(FILE_PATH_LITERAL("download-test.lib"));
+ NavigateToURL(shell(), URLRequestMockHTTPJob::GetMockUrl(file));
+
+ // Wait until the first (intermediate file) rename and execute the callback.
+ file_factory->WaitForSomeCallback();
+ std::vector<base::Closure> callbacks;
+ file_factory->GetAllDetachCallbacks(&callbacks);
+ ASSERT_TRUE(callbacks.empty());
+ file_factory->GetAllRenameCallbacks(&callbacks);
+ ASSERT_EQ(1u, callbacks.size());
+ callbacks[0].Run();
+ callbacks.clear();
+
+ // Wait until the second (final) rename callback is posted.
+ file_factory->WaitForSomeCallback();
+ file_factory->GetAllDetachCallbacks(&callbacks);
+ ASSERT_TRUE(callbacks.empty());
+ file_factory->GetAllRenameCallbacks(&callbacks);
+ ASSERT_EQ(1u, callbacks.size());
+
+ // Call it.
+ callbacks[0].Run();
+ callbacks.clear();
+
+ // Confirm download isn't complete yet.
+ std::vector<DownloadItem*> items;
+ DownloadManagerForShell(shell())->GetAllDownloads(&items);
+ EXPECT_EQ(DownloadItem::IN_PROGRESS, items[0]->GetState());
+
+ // Cancel the download; confirm cancel fails anyway.
+ ASSERT_EQ(1u, items.size());
+ items[0]->Cancel(true);
+ EXPECT_EQ(DownloadItem::IN_PROGRESS, items[0]->GetState());
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(DownloadItem::IN_PROGRESS, items[0]->GetState());
+
+ // Confirm detach callback and run it.
+ file_factory->WaitForSomeCallback();
+ file_factory->GetAllRenameCallbacks(&callbacks);
+ ASSERT_TRUE(callbacks.empty());
+ file_factory->GetAllDetachCallbacks(&callbacks);
+ ASSERT_EQ(1u, callbacks.size());
+ callbacks[0].Run();
+ callbacks.clear();
+ EXPECT_EQ(DownloadItem::COMPLETE, items[0]->GetState());
+}
+
} // namespace content
« no previous file with comments | « no previous file | content/browser/download/download_file.h » ('j') | content/browser/download/download_file.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698