// Copyright (c) 2012 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. // Disable everything on windows only. http://crbug.com/306144 #ifndef OS_WIN #include #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/json/json_reader.h" #include "base/message_loop/message_loop.h" #include "base/prefs/pref_service.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/download/download_file_icon_extractor.h" #include "chrome/browser/download/download_service.h" #include "chrome/browser/download/download_service_factory.h" #include "chrome/browser/download/download_test_file_activity_observer.h" #include "chrome/browser/extensions/api/downloads/downloads_api.h" #include "chrome/browser/extensions/browser_action_test_util.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/history/download_row.h" #include "chrome/browser/net/url_request_mock_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/common/page_transition_types.h" #include "content/public/test/download_test_observer.h" #include "content/test/net/url_request_slow_download_job.h" #include "net/base/data_url.h" #include "net/base/net_util.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_job_factory.h" #include "net/url_request/url_request_job_factory_impl.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_operation_runner.h" #include "webkit/browser/fileapi/file_system_url.h" using content::BrowserContext; using content::BrowserThread; using content::DownloadItem; using content::DownloadManager; using content::URLRequestSlowDownloadJob; namespace errors = download_extension_errors; namespace api = extensions::api::downloads; namespace { // Comparator that orders download items by their ID. Can be used with // std::sort. struct DownloadIdComparator { bool operator() (DownloadItem* first, DownloadItem* second) { return first->GetId() < second->GetId(); } }; class DownloadsEventsListener : public content::NotificationObserver { public: DownloadsEventsListener() : waiting_(false) { registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, content::NotificationService::AllSources()); } virtual ~DownloadsEventsListener() { registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, content::NotificationService::AllSources()); STLDeleteElements(&events_); } void ClearEvents() { STLDeleteElements(&events_); events_.clear(); } class Event { public: Event(Profile* profile, const std::string& event_name, const std::string& json_args, base::Time caught) : profile_(profile), event_name_(event_name), json_args_(json_args), args_(base::JSONReader::Read(json_args)), caught_(caught) { } const base::Time& caught() { return caught_; } bool Satisfies(const Event& other) const { return other.SatisfiedBy(*this); } bool SatisfiedBy(const Event& other) const { if ((profile_ != other.profile_) || (event_name_ != other.event_name_)) return false; if (((event_name_ == api::OnDeterminingFilename::kEventName) || (event_name_ == api::OnCreated::kEventName) || (event_name_ == api::OnChanged::kEventName)) && args_.get() && other.args_.get()) { base::ListValue* left_list = NULL; base::DictionaryValue* left_dict = NULL; base::ListValue* right_list = NULL; base::DictionaryValue* right_dict = NULL; if (!args_->GetAsList(&left_list) || !other.args_->GetAsList(&right_list) || !left_list->GetDictionary(0, &left_dict) || !right_list->GetDictionary(0, &right_dict)) return false; for (base::DictionaryValue::Iterator iter(*left_dict); !iter.IsAtEnd(); iter.Advance()) { base::Value* right_value = NULL; if (!right_dict->HasKey(iter.key()) || (right_dict->Get(iter.key(), &right_value) && !iter.value().Equals(right_value))) { return false; } } return true; } else if ((event_name_ == api::OnErased::kEventName) && args_.get() && other.args_.get()) { int my_id = -1, other_id = -1; return (args_->GetAsInteger(&my_id) && other.args_->GetAsInteger(&other_id) && my_id == other_id); } return json_args_ == other.json_args_; } std::string Debug() { return base::StringPrintf("Event(%p, %s, %s, %f)", profile_, event_name_.c_str(), json_args_.c_str(), caught_.ToJsTime()); } private: Profile* profile_; std::string event_name_; std::string json_args_; scoped_ptr args_; base::Time caught_; DISALLOW_COPY_AND_ASSIGN(Event); }; typedef ExtensionDownloadsEventRouter::DownloadsNotificationSource DownloadsNotificationSource; virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE { switch (type) { case chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT: { DownloadsNotificationSource* dns = content::Source(source).ptr(); Event* new_event = new Event( dns->profile, dns->event_name, *content::Details(details).ptr(), base::Time::Now()); events_.push_back(new_event); if (waiting_ && waiting_for_.get() && new_event->Satisfies(*waiting_for_)) { waiting_ = false; base::MessageLoopForUI::current()->Quit(); } break; } default: NOTREACHED(); } } bool WaitFor(Profile* profile, const std::string& event_name, const std::string& json_args) { waiting_for_.reset(new Event(profile, event_name, json_args, base::Time())); for (std::deque::const_iterator iter = events_.begin(); iter != events_.end(); ++iter) { if ((*iter)->Satisfies(*waiting_for_.get())) { return true; } } waiting_ = true; content::RunMessageLoop(); bool success = !waiting_; if (waiting_) { // Print the events that were caught since the last WaitFor() call to help // find the erroneous event. // TODO(benjhayden) Fuzzy-match and highlight the erroneous event. for (std::deque::const_iterator iter = events_.begin(); iter != events_.end(); ++iter) { if ((*iter)->caught() > last_wait_) { LOG(INFO) << "Caught " << (*iter)->Debug(); } } if (waiting_for_.get()) { LOG(INFO) << "Timed out waiting for " << waiting_for_->Debug(); } waiting_ = false; } waiting_for_.reset(); last_wait_ = base::Time::Now(); return success; } private: bool waiting_; base::Time last_wait_; scoped_ptr waiting_for_; content::NotificationRegistrar registrar_; std::deque events_; DISALLOW_COPY_AND_ASSIGN(DownloadsEventsListener); }; class DownloadExtensionTest : public ExtensionApiTest { public: DownloadExtensionTest() : extension_(NULL), incognito_browser_(NULL), current_browser_(NULL) { } protected: // Used with CreateHistoryDownloads struct HistoryDownloadInfo { // Filename to use. CreateHistoryDownloads will append this filename to the // temporary downloads directory specified by downloads_directory(). const base::FilePath::CharType* filename; // State for the download. Note that IN_PROGRESS downloads will be created // as CANCELLED. DownloadItem::DownloadState state; // Danger type for the download. Only use DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS // and DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT. content::DownloadDangerType danger_type; }; void LoadExtension(const char* name) { // Store the created Extension object so that we can attach it to // ExtensionFunctions. Also load the extension in incognito profiles for // testing incognito. extension_ = LoadExtensionIncognito(test_data_dir_.AppendASCII(name)); CHECK(extension_); content::WebContents* tab = chrome::AddSelectedTabWithURL( current_browser(), extension_->GetResourceURL("empty.html"), content::PAGE_TRANSITION_LINK); extensions::ExtensionSystem::Get(current_browser()->profile())-> event_router()->AddEventListener( api::OnCreated::kEventName, tab->GetRenderProcessHost(), GetExtensionId()); extensions::ExtensionSystem::Get(current_browser()->profile())-> event_router()->AddEventListener( api::OnChanged::kEventName, tab->GetRenderProcessHost(), GetExtensionId()); extensions::ExtensionSystem::Get(current_browser()->profile())-> event_router()->AddEventListener( api::OnErased::kEventName, tab->GetRenderProcessHost(), GetExtensionId()); } content::RenderProcessHost* AddFilenameDeterminer() { content::WebContents* tab = chrome::AddSelectedTabWithURL( current_browser(), extension_->GetResourceURL("empty.html"), content::PAGE_TRANSITION_LINK); extensions::ExtensionSystem::Get(current_browser()->profile())-> event_router()->AddEventListener( api::OnDeterminingFilename::kEventName, tab->GetRenderProcessHost(), GetExtensionId()); return tab->GetRenderProcessHost(); } void RemoveFilenameDeterminer(content::RenderProcessHost* host) { extensions::ExtensionSystem::Get(current_browser()->profile())-> event_router()->RemoveEventListener( api::OnDeterminingFilename::kEventName, host, GetExtensionId()); } Browser* current_browser() { return current_browser_; } // InProcessBrowserTest virtual void SetUpOnMainThread() OVERRIDE { ExtensionApiTest::SetUpOnMainThread(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); InProcessBrowserTest::SetUpOnMainThread(); GoOnTheRecord(); CreateAndSetDownloadsDirectory(); current_browser()->profile()->GetPrefs()->SetBoolean( prefs::kPromptForDownload, false); GetOnRecordManager()->RemoveAllDownloads(); events_listener_.reset(new DownloadsEventsListener()); // Disable file chooser for current profile. DownloadTestFileActivityObserver observer(current_browser()->profile()); observer.EnableFileChooser(false); } void GoOnTheRecord() { current_browser_ = browser(); } void GoOffTheRecord() { if (!incognito_browser_) { incognito_browser_ = CreateIncognitoBrowser(); GetOffRecordManager()->RemoveAllDownloads(); // Disable file chooser for incognito profile. DownloadTestFileActivityObserver observer(incognito_browser_->profile()); observer.EnableFileChooser(false); } current_browser_ = incognito_browser_; } bool WaitFor(const std::string& event_name, const std::string& json_args) { return events_listener_->WaitFor( current_browser()->profile(), event_name, json_args); } bool WaitForInterruption( DownloadItem* item, content::DownloadInterruptReason expected_error, const std::string& on_created_event) { if (!WaitFor(api::OnCreated::kEventName, on_created_event)) return false; // Now, onCreated is always fired before interruption. return WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"error\": {\"current\": \"%s\"}," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"interrupted\"}}]", item->GetId(), content::InterruptReasonDebugString( expected_error).c_str())); } void ClearEvents() { events_listener_->ClearEvents(); } std::string GetExtensionURL() { return extension_->url().spec(); } std::string GetExtensionId() { return extension_->id(); } std::string GetFilename(const char* path) { std::string result = downloads_directory_.path().AppendASCII(path).AsUTF8Unsafe(); #if defined(OS_WIN) for (std::string::size_type next = result.find("\\"); next != std::string::npos; next = result.find("\\", next)) { result.replace(next, 1, "\\\\"); next += 2; } #endif return result; } DownloadManager* GetOnRecordManager() { return BrowserContext::GetDownloadManager(browser()->profile()); } DownloadManager* GetOffRecordManager() { return BrowserContext::GetDownloadManager( browser()->profile()->GetOffTheRecordProfile()); } DownloadManager* GetCurrentManager() { return (current_browser_ == incognito_browser_) ? GetOffRecordManager() : GetOnRecordManager(); } // Creates a set of history downloads based on the provided |history_info| // array. |count| is the number of elements in |history_info|. On success, // |items| will contain |count| DownloadItems in the order that they were // specified in |history_info|. Returns true on success and false otherwise. bool CreateHistoryDownloads(const HistoryDownloadInfo* history_info, size_t count, DownloadManager::DownloadVector* items) { DownloadIdComparator download_id_comparator; base::Time current = base::Time::Now(); items->clear(); GetOnRecordManager()->GetAllDownloads(items); CHECK_EQ(0, static_cast(items->size())); std::vector url_chain; url_chain.push_back(GURL()); for (size_t i = 0; i < count; ++i) { DownloadItem* item = GetOnRecordManager()->CreateDownloadItem( content::DownloadItem::kInvalidId + 1 + i, downloads_directory().Append(history_info[i].filename), downloads_directory().Append(history_info[i].filename), url_chain, GURL(), // URL Chain, referrer current, current, // start_time, end_time std::string(), std::string(), // etag, last_modified 1, 1, // received_bytes, total_bytes history_info[i].state, // state content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, content::DOWNLOAD_INTERRUPT_REASON_NONE, false); // opened items->push_back(item); } // Order by ID so that they are in the order that we created them. std::sort(items->begin(), items->end(), download_id_comparator); // Set the danger type if necessary. for (size_t i = 0; i < count; ++i) { if (history_info[i].danger_type != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT, history_info[i].danger_type); items->at(i)->OnContentCheckCompleted(history_info[i].danger_type); } } return true; } void CreateSlowTestDownloads( size_t count, DownloadManager::DownloadVector* items) { for (size_t i = 0; i < count; ++i) { scoped_ptr observer( CreateInProgressDownloadObserver(1)); GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl); ui_test_utils::NavigateToURLWithDisposition( current_browser(), slow_download_url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); observer->WaitForFinished(); EXPECT_EQ( 1u, observer->NumDownloadsSeenInState(DownloadItem::IN_PROGRESS)); } GetCurrentManager()->GetAllDownloads(items); ASSERT_EQ(count, items->size()); } DownloadItem* CreateSlowTestDownload() { scoped_ptr observer( CreateInProgressDownloadObserver(1)); GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl); DownloadManager* manager = GetCurrentManager(); EXPECT_EQ(0, manager->NonMaliciousInProgressCount()); EXPECT_EQ(0, manager->InProgressCount()); if (manager->InProgressCount() != 0) return NULL; ui_test_utils::NavigateToURLWithDisposition( current_browser(), slow_download_url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); observer->WaitForFinished(); EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::IN_PROGRESS)); DownloadManager::DownloadVector items; manager->GetAllDownloads(&items); DownloadItem* new_item = NULL; for (DownloadManager::DownloadVector::iterator iter = items.begin(); iter != items.end(); ++iter) { if ((*iter)->GetState() == DownloadItem::IN_PROGRESS) { // There should be only one IN_PROGRESS item. EXPECT_EQ(NULL, new_item); new_item = *iter; } } return new_item; } void FinishPendingSlowDownloads() { scoped_ptr observer( CreateDownloadObserver(1)); GURL finish_url(URLRequestSlowDownloadJob::kFinishDownloadUrl); ui_test_utils::NavigateToURLWithDisposition( current_browser(), finish_url, NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); observer->WaitForFinished(); EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); } content::DownloadTestObserver* CreateDownloadObserver(size_t download_count) { return new content::DownloadTestObserverTerminal( GetCurrentManager(), download_count, content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); } content::DownloadTestObserver* CreateInProgressDownloadObserver( size_t download_count) { return new content::DownloadTestObserverInProgress( GetCurrentManager(), download_count); } bool RunFunction(UIThreadExtensionFunction* function, const std::string& args) { scoped_refptr delete_function(function); SetUpExtensionFunction(function); bool result = extension_function_test_utils::RunFunction( function, args, browser(), GetFlags()); if (!result) { LOG(ERROR) << function->GetError(); } return result; } extension_function_test_utils::RunFunctionFlags GetFlags() { return current_browser()->profile()->IsOffTheRecord() ? extension_function_test_utils::INCLUDE_INCOGNITO : extension_function_test_utils::NONE; } // extension_function_test_utils::RunFunction*() only uses browser for its // profile(), so pass it the on-record browser so that it always uses the // on-record profile to match real-life behavior. base::Value* RunFunctionAndReturnResult( scoped_refptr function, const std::string& args) { SetUpExtensionFunction(function.get()); return extension_function_test_utils::RunFunctionAndReturnSingleResult( function.get(), args, browser(), GetFlags()); } std::string RunFunctionAndReturnError( scoped_refptr function, const std::string& args) { SetUpExtensionFunction(function.get()); return extension_function_test_utils::RunFunctionAndReturnError( function.get(), args, browser(), GetFlags()); } bool RunFunctionAndReturnString( scoped_refptr function, const std::string& args, std::string* result_string) { SetUpExtensionFunction(function.get()); scoped_ptr result(RunFunctionAndReturnResult(function, args)); EXPECT_TRUE(result.get()); return result.get() && result->GetAsString(result_string); } std::string DownloadItemIdAsArgList(const DownloadItem* download_item) { return base::StringPrintf("[%d]", download_item->GetId()); } const base::FilePath& downloads_directory() { return downloads_directory_.path(); } DownloadsEventsListener* events_listener() { return events_listener_.get(); } private: void SetUpExtensionFunction(UIThreadExtensionFunction* function) { if (extension_) { // Recreate the tab each time for insulation. content::WebContents* tab = chrome::AddSelectedTabWithURL( current_browser(), extension_->GetResourceURL("empty.html"), content::PAGE_TRANSITION_LINK); function->set_extension(extension_); function->SetRenderViewHost(tab->GetRenderViewHost()); } } void CreateAndSetDownloadsDirectory() { ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); current_browser()->profile()->GetPrefs()->SetFilePath( prefs::kDownloadDefaultDirectory, downloads_directory_.path()); } base::ScopedTempDir downloads_directory_; const extensions::Extension* extension_; Browser* incognito_browser_; Browser* current_browser_; scoped_ptr events_listener_; DISALLOW_COPY_AND_ASSIGN(DownloadExtensionTest); }; class MockIconExtractorImpl : public DownloadFileIconExtractor { public: MockIconExtractorImpl(const base::FilePath& path, IconLoader::IconSize icon_size, const std::string& response) : expected_path_(path), expected_icon_size_(icon_size), response_(response) { } virtual ~MockIconExtractorImpl() {} virtual bool ExtractIconURLForPath(const base::FilePath& path, IconLoader::IconSize icon_size, IconURLCallback callback) OVERRIDE { EXPECT_STREQ(expected_path_.value().c_str(), path.value().c_str()); EXPECT_EQ(expected_icon_size_, icon_size); if (expected_path_ == path && expected_icon_size_ == icon_size) { callback_ = callback; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&MockIconExtractorImpl::RunCallback, base::Unretained(this))); return true; } else { return false; } } private: void RunCallback() { callback_.Run(response_); // Drop the reference on extension function to avoid memory leaks. callback_ = IconURLCallback(); } base::FilePath expected_path_; IconLoader::IconSize expected_icon_size_; std::string response_; IconURLCallback callback_; }; bool ItemNotInProgress(DownloadItem* item) { return item->GetState() != DownloadItem::IN_PROGRESS; } // Cancels the underlying DownloadItem when the ScopedCancellingItem goes out of // scope. Like a scoped_ptr, but for DownloadItems. class ScopedCancellingItem { public: explicit ScopedCancellingItem(DownloadItem* item) : item_(item) {} ~ScopedCancellingItem() { item_->Cancel(true); content::DownloadUpdatedObserver observer( item_, base::Bind(&ItemNotInProgress)); observer.WaitForEvent(); } DownloadItem* get() { return item_; } private: DownloadItem* item_; DISALLOW_COPY_AND_ASSIGN(ScopedCancellingItem); }; // Cancels all the underlying DownloadItems when the ScopedItemVectorCanceller // goes out of scope. Generalization of ScopedCancellingItem to many // DownloadItems. class ScopedItemVectorCanceller { public: explicit ScopedItemVectorCanceller(DownloadManager::DownloadVector* items) : items_(items) { } ~ScopedItemVectorCanceller() { for (DownloadManager::DownloadVector::const_iterator item = items_->begin(); item != items_->end(); ++item) { if ((*item)->GetState() == DownloadItem::IN_PROGRESS) (*item)->Cancel(true); content::DownloadUpdatedObserver observer( (*item), base::Bind(&ItemNotInProgress)); observer.WaitForEvent(); } } private: DownloadManager::DownloadVector* items_; DISALLOW_COPY_AND_ASSIGN(ScopedItemVectorCanceller); }; // Writes an HTML5 file so that it can be downloaded. class HTML5FileWriter { public: static bool CreateFileForTesting(fileapi::FileSystemContext* context, const fileapi::FileSystemURL& path, const char*data, int length) { // Create a temp file. base::FilePath temp_file; if (!file_util::CreateTemporaryFile(&temp_file) || file_util::WriteFile(temp_file, data, length) != length) { return false; } // Invoke the fileapi to copy it into the sandboxed filesystem. bool result = false; base::WaitableEvent done_event(true, false); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&CreateFileForTestingOnIOThread, base::Unretained(context), path, temp_file, base::Unretained(&result), base::Unretained(&done_event))); // Wait for that to finish. done_event.Wait(); base::DeleteFile(temp_file, false); return result; } private: static void CopyInCompletion(bool* result, base::WaitableEvent* done_event, base::PlatformFileError error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); *result = error == base::PLATFORM_FILE_OK; done_event->Signal(); } static void CreateFileForTestingOnIOThread( fileapi::FileSystemContext* context, const fileapi::FileSystemURL& path, const base::FilePath& temp_file, bool* result, base::WaitableEvent* done_event) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); context->operation_runner()->CopyInForeignFile( temp_file, path, base::Bind(&CopyInCompletion, base::Unretained(result), base::Unretained(done_event))); } }; // TODO(benjhayden) Merge this with the other TestObservers. class JustInProgressDownloadObserver : public content::DownloadTestObserverInProgress { public: JustInProgressDownloadObserver( DownloadManager* download_manager, size_t wait_count) : content::DownloadTestObserverInProgress(download_manager, wait_count) { } virtual ~JustInProgressDownloadObserver() {} private: virtual bool IsDownloadInFinalState(DownloadItem* item) OVERRIDE { return item->GetState() == DownloadItem::IN_PROGRESS; } DISALLOW_COPY_AND_ASSIGN(JustInProgressDownloadObserver); }; bool ItemIsInterrupted(DownloadItem* item) { return item->GetState() == DownloadItem::INTERRUPTED; } content::DownloadInterruptReason InterruptReasonExtensionToContent( api::InterruptReason error) { switch (error) { case api::INTERRUPT_REASON_NONE: return content::DOWNLOAD_INTERRUPT_REASON_NONE; #define INTERRUPT_REASON(name, value) \ case api::INTERRUPT_REASON_##name: \ return content::DOWNLOAD_INTERRUPT_REASON_##name; #include "content/public/browser/download_interrupt_reason_values.h" #undef INTERRUPT_REASON } NOTREACHED(); return content::DOWNLOAD_INTERRUPT_REASON_NONE; } api::InterruptReason InterruptReasonContentToExtension( content::DownloadInterruptReason error) { switch (error) { case content::DOWNLOAD_INTERRUPT_REASON_NONE: return api::INTERRUPT_REASON_NONE; #define INTERRUPT_REASON(name, value) \ case content::DOWNLOAD_INTERRUPT_REASON_##name: \ return api::INTERRUPT_REASON_##name; #include "content/public/browser/download_interrupt_reason_values.h" #undef INTERRUPT_REASON } NOTREACHED(); return api::INTERRUPT_REASON_NONE; } } // namespace IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Open) { LoadExtension("downloads_split"); EXPECT_STREQ(errors::kInvalidId, RunFunctionAndReturnError( new DownloadsOpenFunction(), "[-42]").c_str()); DownloadItem* download_item = CreateSlowTestDownload(); ASSERT_TRUE(download_item); EXPECT_FALSE(download_item->GetOpened()); EXPECT_FALSE(download_item->GetOpenWhenComplete()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"application/octet-stream\"," " \"paused\": false," " \"url\": \"%s\"}]", download_item->GetURL().spec().c_str()))); EXPECT_STREQ(errors::kNotComplete, RunFunctionAndReturnError( new DownloadsOpenFunction(), DownloadItemIdAsArgList(download_item)).c_str()); FinishPendingSlowDownloads(); EXPECT_FALSE(download_item->GetOpened()); EXPECT_TRUE(RunFunction(new DownloadsOpenFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_TRUE(download_item->GetOpened()); } IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_PauseResumeCancelErase) { DownloadItem* download_item = CreateSlowTestDownload(); ASSERT_TRUE(download_item); std::string error; // Call pause(). It should succeed and the download should be paused on // return. EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_TRUE(download_item->IsPaused()); // Calling removeFile on a non-active download yields kNotComplete // and should not crash. http://crbug.com/319984 error = RunFunctionAndReturnError(new DownloadsRemoveFileFunction(), DownloadItemIdAsArgList(download_item)); EXPECT_STREQ(errors::kNotComplete, error.c_str()); // Calling pause() twice shouldn't be an error. EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_TRUE(download_item->IsPaused()); // Now try resuming this download. It should succeed. EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_FALSE(download_item->IsPaused()); // Resume again. Resuming a download that wasn't paused is not an error. EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_FALSE(download_item->IsPaused()); // Pause again. EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_TRUE(download_item->IsPaused()); // And now cancel. EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_EQ(DownloadItem::CANCELLED, download_item->GetState()); // Cancel again. Shouldn't have any effect. EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), DownloadItemIdAsArgList(download_item))); EXPECT_EQ(DownloadItem::CANCELLED, download_item->GetState()); // Calling paused on a non-active download yields kNotInProgress. error = RunFunctionAndReturnError( new DownloadsPauseFunction(), DownloadItemIdAsArgList(download_item)); EXPECT_STREQ(errors::kNotInProgress, error.c_str()); // Calling resume on a non-active download yields kNotResumable error = RunFunctionAndReturnError( new DownloadsResumeFunction(), DownloadItemIdAsArgList(download_item)); EXPECT_STREQ(errors::kNotResumable, error.c_str()); // Calling pause on a non-existent download yields kInvalidId. error = RunFunctionAndReturnError( new DownloadsPauseFunction(), "[-42]"); EXPECT_STREQ(errors::kInvalidId, error.c_str()); // Calling resume on a non-existent download yields kInvalidId error = RunFunctionAndReturnError( new DownloadsResumeFunction(), "[-42]"); EXPECT_STREQ(errors::kInvalidId, error.c_str()); // Calling removeFile on a non-existent download yields kInvalidId. error = RunFunctionAndReturnError( new DownloadsRemoveFileFunction(), "[-42]"); EXPECT_STREQ(errors::kInvalidId, error.c_str()); int id = download_item->GetId(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsEraseFunction(), base::StringPrintf("[{\"id\": %d}]", id))); DownloadManager::DownloadVector items; GetCurrentManager()->GetAllDownloads(&items); EXPECT_EQ(0UL, items.size()); ASSERT_TRUE(result); download_item = NULL; base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); int element = -1; ASSERT_TRUE(result_list->GetInteger(0, &element)); EXPECT_EQ(id, element); } scoped_refptr MockedGetFileIconFunction( const base::FilePath& expected_path, IconLoader::IconSize icon_size, const std::string& response) { scoped_refptr function( new DownloadsGetFileIconFunction()); function->SetIconExtractorForTesting(new MockIconExtractorImpl( expected_path, icon_size, response)); return function; } // Test downloads.getFileIcon() on in-progress, finished, cancelled and deleted // download items. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_FileIcon_Active) { DownloadItem* download_item = CreateSlowTestDownload(); ASSERT_TRUE(download_item); ASSERT_FALSE(download_item->GetTargetFilePath().empty()); std::string args32(base::StringPrintf("[%d, {\"size\": 32}]", download_item->GetId())); std::string result_string; // Get the icon for the in-progress download. This call should succeed even // if the file type isn't registered. // Test whether the correct path is being pased into the icon extractor. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), base::StringPrintf("[%d, {}]", download_item->GetId()), &result_string)); // Now try a 16x16 icon. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::SMALL, "foo"), base::StringPrintf("[%d, {\"size\": 16}]", download_item->GetId()), &result_string)); // Explicitly asking for 32x32 should give us a 32x32 icon. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), args32, &result_string)); // Finish the download and try again. FinishPendingSlowDownloads(); EXPECT_EQ(DownloadItem::COMPLETE, download_item->GetState()); EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), args32, &result_string)); // Check the path passed to the icon extractor post-completion. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), args32, &result_string)); // Now create another download. download_item = CreateSlowTestDownload(); ASSERT_TRUE(download_item); ASSERT_FALSE(download_item->GetTargetFilePath().empty()); args32 = base::StringPrintf("[%d, {\"size\": 32}]", download_item->GetId()); // Cancel the download. As long as the download has a target path, we should // be able to query the file icon. download_item->Cancel(true); ASSERT_FALSE(download_item->GetTargetFilePath().empty()); // Let cleanup complete on the FILE thread. content::RunAllPendingInMessageLoop(BrowserThread::FILE); // Check the path passed to the icon extractor post-cancellation. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( download_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), args32, &result_string)); // Simulate an error during icon load by invoking the mock with an empty // result string. std::string error = RunFunctionAndReturnError( MockedGetFileIconFunction(download_item->GetTargetFilePath(), IconLoader::NORMAL, std::string()), args32); EXPECT_STREQ(errors::kIconNotFound, error.c_str()); // Once the download item is deleted, we should return kInvalidId. int id = download_item->GetId(); download_item->Remove(); download_item = NULL; EXPECT_EQ(static_cast(NULL), GetCurrentManager()->GetDownload(id)); error = RunFunctionAndReturnError(new DownloadsGetFileIconFunction(), args32); EXPECT_STREQ(errors::kInvalidId, error.c_str()); } // Test that we can acquire file icons for history downloads regardless of // whether they exist or not. If the file doesn't exist we should receive a // generic icon from the OS/toolkit that may or may not be specific to the file // type. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_FileIcon_History) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("real.txt"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }, { FILE_PATH_LITERAL("fake.txt"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS } }; DownloadManager::DownloadVector all_downloads; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &all_downloads)); base::FilePath real_path = all_downloads[0]->GetTargetFilePath(); base::FilePath fake_path = all_downloads[1]->GetTargetFilePath(); EXPECT_EQ(0, file_util::WriteFile(real_path, "", 0)); ASSERT_TRUE(base::PathExists(real_path)); ASSERT_FALSE(base::PathExists(fake_path)); for (DownloadManager::DownloadVector::iterator iter = all_downloads.begin(); iter != all_downloads.end(); ++iter) { std::string result_string; // Use a MockIconExtractorImpl to test if the correct path is being passed // into the DownloadFileIconExtractor. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( (*iter)->GetTargetFilePath(), IconLoader::NORMAL, "hello"), base::StringPrintf("[%d, {\"size\": 32}]", (*iter)->GetId()), &result_string)); EXPECT_STREQ("hello", result_string.c_str()); } } // Test passing the empty query to search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchEmptyQuery) { ScopedCancellingItem item(CreateSlowTestDownload()); ASSERT_TRUE(item.get()); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); } // Test the |filenameRegex| parameter for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchFilenameRegex) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("foobar"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }, { FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS } }; DownloadManager::DownloadVector all_downloads; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &all_downloads)); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"filenameRegex\": \"foobar\"}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); base::DictionaryValue* item_value = NULL; ASSERT_TRUE(result_list->GetDictionary(0, &item_value)); int item_id = -1; ASSERT_TRUE(item_value->GetInteger("id", &item_id)); ASSERT_EQ(all_downloads[0]->GetId(), static_cast(item_id)); } // Test the |id| parameter for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchId) { DownloadManager::DownloadVector items; CreateSlowTestDownloads(2, &items); ScopedItemVectorCanceller delete_items(&items); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), base::StringPrintf( "[{\"id\": %u}]", items[0]->GetId()))); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); base::DictionaryValue* item_value = NULL; ASSERT_TRUE(result_list->GetDictionary(0, &item_value)); int item_id = -1; ASSERT_TRUE(item_value->GetInteger("id", &item_id)); ASSERT_EQ(items[0]->GetId(), static_cast(item_id)); } // Test specifying both the |id| and |filename| parameters for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchIdAndFilename) { DownloadManager::DownloadVector items; CreateSlowTestDownloads(2, &items); ScopedItemVectorCanceller delete_items(&items); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"id\": 0, \"filename\": \"foobar\"}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(0UL, result_list->GetSize()); } // Test a single |orderBy| parameter for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchOrderBy) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }, { FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS } }; DownloadManager::DownloadVector items; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &items)); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"orderBy\": [\"filename\"]}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(2UL, result_list->GetSize()); base::DictionaryValue* item0_value = NULL; base::DictionaryValue* item1_value = NULL; ASSERT_TRUE(result_list->GetDictionary(0, &item0_value)); ASSERT_TRUE(result_list->GetDictionary(1, &item1_value)); std::string item0_name, item1_name; ASSERT_TRUE(item0_value->GetString("filename", &item0_name)); ASSERT_TRUE(item1_value->GetString("filename", &item1_name)); ASSERT_GT(items[0]->GetTargetFilePath().value(), items[1]->GetTargetFilePath().value()); ASSERT_LT(item0_name, item1_name); } // Test specifying an empty |orderBy| parameter for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchOrderByEmpty) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }, { FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS } }; DownloadManager::DownloadVector items; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &items)); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"orderBy\": []}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(2UL, result_list->GetSize()); base::DictionaryValue* item0_value = NULL; base::DictionaryValue* item1_value = NULL; ASSERT_TRUE(result_list->GetDictionary(0, &item0_value)); ASSERT_TRUE(result_list->GetDictionary(1, &item1_value)); std::string item0_name, item1_name; ASSERT_TRUE(item0_value->GetString("filename", &item0_name)); ASSERT_TRUE(item1_value->GetString("filename", &item1_name)); ASSERT_GT(items[0]->GetTargetFilePath().value(), items[1]->GetTargetFilePath().value()); ASSERT_GT(item0_name, item1_name); } // Test the |danger| option for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchDanger) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT }, { FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS } }; DownloadManager::DownloadVector items; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &items)); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"danger\": \"content\"}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); } // Test the |state| option for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchState) { DownloadManager::DownloadVector items; CreateSlowTestDownloads(2, &items); ScopedItemVectorCanceller delete_items(&items); items[0]->Cancel(true); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"state\": \"in_progress\"}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); } // Test the |limit| option for search(). IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchLimit) { DownloadManager::DownloadVector items; CreateSlowTestDownloads(2, &items); ScopedItemVectorCanceller delete_items(&items); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{\"limit\": 1}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); } // Test invalid search parameters. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchInvalid) { std::string error = RunFunctionAndReturnError( new DownloadsSearchFunction(), "[{\"filenameRegex\": \"(\"}]"); EXPECT_STREQ(errors::kInvalidFilter, error.c_str()); error = RunFunctionAndReturnError( new DownloadsSearchFunction(), "[{\"orderBy\": [\"goat\"]}]"); EXPECT_STREQ(errors::kInvalidOrderBy, error.c_str()); error = RunFunctionAndReturnError( new DownloadsSearchFunction(), "[{\"limit\": -1}]"); EXPECT_STREQ(errors::kInvalidQueryLimit, error.c_str()); } // Test searching using multiple conditions through multiple downloads. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchPlural) { const HistoryDownloadInfo kHistoryInfo[] = { { FILE_PATH_LITERAL("aaa"), DownloadItem::CANCELLED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }, { FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT }, { FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT }, }; DownloadManager::DownloadVector items; ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo), &items)); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{" "\"state\": \"complete\", " "\"danger\": \"content\", " "\"orderBy\": [\"filename\"], " "\"limit\": 1}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); base::DictionaryValue* item_value = NULL; ASSERT_TRUE(result_list->GetDictionary(0, &item_value)); base::FilePath::StringType item_name; ASSERT_TRUE(item_value->GetString("filename", &item_name)); ASSERT_EQ(items[2]->GetTargetFilePath().value(), item_name); } // Test that incognito downloads are only visible in incognito contexts, and // test that on-record downloads are visible in both incognito and on-record // contexts, for DownloadsSearchFunction, DownloadsPauseFunction, // DownloadsResumeFunction, and DownloadsCancelFunction. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) { scoped_ptr result_value; base::ListValue* result_list = NULL; base::DictionaryValue* result_dict = NULL; base::FilePath::StringType filename; bool is_incognito = false; std::string error; std::string on_item_arg; std::string off_item_arg; std::string result_string; // Set up one on-record item and one off-record item. // Set up the off-record item first because otherwise there are mysteriously 3 // items total instead of 2. // TODO(benjhayden): Figure out where the third item comes from. GoOffTheRecord(); DownloadItem* off_item = CreateSlowTestDownload(); ASSERT_TRUE(off_item); off_item_arg = DownloadItemIdAsArgList(off_item); GoOnTheRecord(); DownloadItem* on_item = CreateSlowTestDownload(); ASSERT_TRUE(on_item); on_item_arg = DownloadItemIdAsArgList(on_item); ASSERT_TRUE(on_item->GetTargetFilePath() != off_item->GetTargetFilePath()); // Extensions running in the incognito window should have access to both // items because the Test extension is in spanning mode. GoOffTheRecord(); result_value.reset(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{}]")); ASSERT_TRUE(result_value.get()); ASSERT_TRUE(result_value->GetAsList(&result_list)); ASSERT_EQ(2UL, result_list->GetSize()); ASSERT_TRUE(result_list->GetDictionary(0, &result_dict)); ASSERT_TRUE(result_dict->GetString("filename", &filename)); ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito)); EXPECT_TRUE(on_item->GetTargetFilePath() == base::FilePath(filename)); EXPECT_FALSE(is_incognito); ASSERT_TRUE(result_list->GetDictionary(1, &result_dict)); ASSERT_TRUE(result_dict->GetString("filename", &filename)); ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito)); EXPECT_TRUE(off_item->GetTargetFilePath() == base::FilePath(filename)); EXPECT_TRUE(is_incognito); // Extensions running in the on-record window should have access only to the // on-record item. GoOnTheRecord(); result_value.reset(RunFunctionAndReturnResult( new DownloadsSearchFunction(), "[{}]")); ASSERT_TRUE(result_value.get()); ASSERT_TRUE(result_value->GetAsList(&result_list)); ASSERT_EQ(1UL, result_list->GetSize()); ASSERT_TRUE(result_list->GetDictionary(0, &result_dict)); ASSERT_TRUE(result_dict->GetString("filename", &filename)); EXPECT_TRUE(on_item->GetTargetFilePath() == base::FilePath(filename)); ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito)); EXPECT_FALSE(is_incognito); // Pausing/Resuming the off-record item while on the record should return an // error. Cancelling "non-existent" downloads is not an error. error = RunFunctionAndReturnError(new DownloadsPauseFunction(), off_item_arg); EXPECT_STREQ(errors::kInvalidId, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), off_item_arg); EXPECT_STREQ(errors::kInvalidId, error.c_str()); error = RunFunctionAndReturnError( new DownloadsGetFileIconFunction(), base::StringPrintf("[%d, {}]", off_item->GetId())); EXPECT_STREQ(errors::kInvalidId, error.c_str()); GoOffTheRecord(); // Do the FileIcon test for both the on- and off-items while off the record. // NOTE(benjhayden): This does not include the FileIcon test from history, // just active downloads. This shouldn't be a problem. EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( on_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), base::StringPrintf("[%d, {}]", on_item->GetId()), &result_string)); EXPECT_TRUE(RunFunctionAndReturnString(MockedGetFileIconFunction( off_item->GetTargetFilePath(), IconLoader::NORMAL, "foo"), base::StringPrintf("[%d, {}]", off_item->GetId()), &result_string)); // Do the pause/resume/cancel test for both the on- and off-items while off // the record. EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), on_item_arg)); EXPECT_TRUE(on_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), on_item_arg)); EXPECT_TRUE(on_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), on_item_arg)); EXPECT_FALSE(on_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), on_item_arg)); EXPECT_FALSE(on_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), on_item_arg)); EXPECT_TRUE(on_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), on_item_arg)); EXPECT_EQ(DownloadItem::CANCELLED, on_item->GetState()); EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), on_item_arg)); EXPECT_EQ(DownloadItem::CANCELLED, on_item->GetState()); error = RunFunctionAndReturnError(new DownloadsPauseFunction(), on_item_arg); EXPECT_STREQ(errors::kNotInProgress, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), on_item_arg); EXPECT_STREQ(errors::kNotResumable, error.c_str()); EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), off_item_arg)); EXPECT_TRUE(off_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), off_item_arg)); EXPECT_TRUE(off_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), off_item_arg)); EXPECT_FALSE(off_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsResumeFunction(), off_item_arg)); EXPECT_FALSE(off_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsPauseFunction(), off_item_arg)); EXPECT_TRUE(off_item->IsPaused()); EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), off_item_arg)); EXPECT_EQ(DownloadItem::CANCELLED, off_item->GetState()); EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), off_item_arg)); EXPECT_EQ(DownloadItem::CANCELLED, off_item->GetState()); error = RunFunctionAndReturnError(new DownloadsPauseFunction(), off_item_arg); EXPECT_STREQ(errors::kNotInProgress, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), off_item_arg); EXPECT_STREQ(errors::kNotResumable, error.c_str()); } // Test that we can start a download and that the correct sequence of events is // fired for it. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Basic) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that we can start a download from an incognito context, and that the // download knows that it's incognito. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Incognito) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); GoOffTheRecord(); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": true," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\":%d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\":%d," " \"state\": {" " \"current\": \"complete\"," " \"previous\": \"in_progress\"}}]", result_id))); } #if defined(OS_WIN) && defined(USE_AURA) // This test is very flaky on Win Aura. http://crbug.com/248438 #define MAYBE_DownloadExtensionTest_Download_UnsafeHeaders \ DISABLED_DownloadExtensionTest_Download_UnsafeHeaders #else #define MAYBE_DownloadExtensionTest_Download_UnsafeHeaders \ DownloadExtensionTest_Download_UnsafeHeaders #endif // Test that we disallow certain headers case-insensitively. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, MAYBE_DownloadExtensionTest_Download_UnsafeHeaders) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); GoOnTheRecord(); static const char* kUnsafeHeaders[] = { "Accept-chArsEt", "accept-eNcoding", "coNNection", "coNteNt-leNgth", "cooKIE", "cOOkie2", "coNteNt-traNsfer-eNcodiNg", "dAtE", "ExpEcT", "hOsT", "kEEp-aLivE", "rEfErEr", "tE", "trAilER", "trANsfer-eNcodiNg", "upGRAde", "usER-agENt", "viA", "pRoxY-", "sEc-", "pRoxY-probably-not-evil", "sEc-probably-not-evil", "oRiGiN", "Access-Control-Request-Headers", "Access-Control-Request-Method", }; for (size_t index = 0; index < arraysize(kUnsafeHeaders); ++index) { std::string download_url = test_server()->GetURL("slow?0").spec(); EXPECT_STREQ(errors::kInvalidHeader, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"unsafe-header-%d.txt\"," " \"headers\": [{" " \"name\": \"%s\"," " \"value\": \"unsafe\"}]}]", download_url.c_str(), static_cast(index), kUnsafeHeaders[index])).c_str()); } } #if defined(OS_WIN) #define MAYBE_DownloadExtensionTest_Download_Subdirectory\ DISABLED_DownloadExtensionTest_Download_Subdirectory #else #define MAYBE_DownloadExtensionTest_Download_Subdirectory\ DownloadExtensionTest_Download_Subdirectory #endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, MAYBE_DownloadExtensionTest_Download_Subdirectory) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"sub/dir/ect/ory.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("sub/dir/ect/ory.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that invalid filenames are disallowed. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_InvalidFilename) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); EXPECT_STREQ(errors::kInvalidFilename, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"../../../../../etc/passwd\"}]", download_url.c_str())).c_str()); } // Test that downloading invalid URLs immediately returns kInvalidURLError. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_InvalidURLs) { LoadExtension("downloads_split"); GoOnTheRecord(); static const char* kInvalidURLs[] = { "foo bar", "../hello", "/hello", "http://", "#frag", "foo/bar.html#frag", "google.com/", }; for (size_t index = 0; index < arraysize(kInvalidURLs); ++index) { EXPECT_STREQ(errors::kInvalidURL, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", kInvalidURLs[index])).c_str()) << kInvalidURLs[index]; } EXPECT_STREQ("net::ERR_ACCESS_DENIED", RunFunctionAndReturnError( new DownloadsDownloadFunction(), "[{\"url\": \"javascript:document.write(\\\"hello\\\");\"}]").c_str()); EXPECT_STREQ("net::ERR_ACCESS_DENIED", RunFunctionAndReturnError( new DownloadsDownloadFunction(), "[{\"url\": \"javascript:return false;\"}]").c_str()); EXPECT_STREQ("net::ERR_NOT_IMPLEMENTED", RunFunctionAndReturnError( new DownloadsDownloadFunction(), "[{\"url\": \"ftp://example.com/example.txt\"}]").c_str()); } // TODO(benjhayden): Set up a test ftp server, add ftp://localhost* to // permissions, test downloading from ftp. // Valid URLs plus fragments are still valid URLs. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_URLFragment) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0#fragment").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Valid data URLs are valid URLs. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_DataURL) { LoadExtension("downloads_split"); std::string download_url = "data:text/plain,hello"; GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"data.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("data.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Valid file URLs are valid URLs. #if defined(OS_WIN) && defined(USE_AURA) // Disabled due to crbug.com/175711 #define MAYBE_DownloadExtensionTest_Download_File \ DISABLED_DownloadExtensionTest_Download_File #else #define MAYBE_DownloadExtensionTest_Download_File \ DownloadExtensionTest_Download_File #endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, MAYBE_DownloadExtensionTest_Download_File) { GoOnTheRecord(); LoadExtension("downloads_split"); std::string download_url = "file:///"; #if defined(OS_WIN) download_url += "C:/"; #endif scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"file.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/html\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("file.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that auth-basic-succeed would fail if the resource requires the // Authorization header and chrome fails to propagate it back to the server. // This tests both that testserver.py does not succeed when it should fail as // well as how the downloads extension API exposes the failure to extensions. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_AuthBasic_Fail) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("auth-basic").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"auth-basic-fail.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitForInterruption( item, content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/html\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); } // Test that DownloadsDownloadFunction propagates |headers| to the URLRequest. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Headers) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("files/downloads/" "a_zip_file.zip?expected_headers=Foo:bar&expected_headers=Qx:yo").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"headers-succeed.txt\"," " \"headers\": [" " {\"name\": \"Foo\", \"value\": \"bar\"}," " {\"name\": \"Qx\", \"value\":\"yo\"}]}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"application/octet-stream\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("headers-succeed.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that headers-succeed would fail if the resource requires the headers and // chrome fails to propagate them back to the server. This tests both that // testserver.py does not succeed when it should fail as well as how the // downloads extension api exposes the failure to extensions. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Headers_Fail) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("files/downloads/" "a_zip_file.zip?expected_headers=Foo:bar&expected_headers=Qx:yo").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"headers-fail.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitForInterruption( item, content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"bytesReceived\": 0," " \"mime\": \"\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); } // Test that DownloadsDownloadFunction propagates the Authorization header // correctly. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_AuthBasic) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("auth-basic").spec(); // This is just base64 of 'username:secret'. static const char* kAuthorization = "dXNlcm5hbWU6c2VjcmV0"; GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"auth-basic-succeed.txt\"," " \"headers\": [{" " \"name\": \"Authorization\"," " \"value\": \"Basic %s\"}]}]", download_url.c_str(), kAuthorization))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/html\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that DownloadsDownloadFunction propagates the |method| and |body| // parameters to the URLRequest. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Post) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("files/post/downloads/" "a_zip_file.zip?expected_body=BODY").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"filename\": \"post-succeed.txt\"," " \"method\": \"POST\"," " \"body\": \"BODY\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"application/octet-stream\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("post-succeed.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // Test that downloadPostSuccess would fail if the resource requires the POST // method, and chrome fails to propagate the |method| parameter back to the // server. This tests both that testserver.py does not succeed when it should // fail, and this tests how the downloads extension api exposes the failure to // extensions. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Post_Get) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("files/post/downloads/" "a_zip_file.zip?expected_body=BODY").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"body\": \"BODY\"," " \"filename\": \"post-get.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitForInterruption( item, content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"\"," " \"paused\": false," " \"id\": %d," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); } // Test that downloadPostSuccess would fail if the resource requires the POST // method, and chrome fails to propagate the |body| parameter back to the // server. This tests both that testserver.py does not succeed when it should // fail, and this tests how the downloads extension api exposes the failure to // extensions. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Post_NoBody) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("files/post/downloads/" "a_zip_file.zip?expected_body=BODY").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," " \"method\": \"POST\"," " \"filename\": \"post-nobody.txt\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitForInterruption( item, content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"\"," " \"paused\": false," " \"id\": %d," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); } // Test that cancel()ing an in-progress download causes its state to transition // to interrupted, and test that that state transition is detectable by an // onChanged event listener. TODO(benjhayden): Test other sources of // interruptions such as server death. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Cancel) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL( "download-known-size").spec(); GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"application/octet-stream\"," " \"paused\": false," " \"id\": %d," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); item->Cancel(true); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"error\": {\"current\":\"USER_CANCELED\"}," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"interrupted\"}}]", result_id))); } // Test downloading filesystem: URLs. // NOTE: chrome disallows creating HTML5 FileSystem Files in incognito. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_FileSystemURL) { static const char* kPayloadData = "on the record\ndata"; GoOnTheRecord(); LoadExtension("downloads_split"); const std::string download_url = "filesystem:" + GetExtensionURL() + "temporary/on_record.txt"; // Setup a file in the filesystem which we can download. ASSERT_TRUE(HTML5FileWriter::CreateFileForTesting( BrowserContext::GetDefaultStoragePartition(browser()->profile())-> GetFileSystemContext(), fileapi::FileSystemURL::CreateForTest(GURL(download_url)), kPayloadData, strlen(kPayloadData))); // Now download it. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("on_record.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); std::string disk_data; EXPECT_TRUE(base::ReadFileToString(item->GetTargetFilePath(), &disk_data)); EXPECT_STREQ(kPayloadData, disk_data.c_str()); } // Test is flaky: http://crbug.com/302071 IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DISABLED_DownloadExtensionTest_OnDeterminingFilename_NoChange) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); // Wait for the onCreated and onDeterminingFilename events. ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_DangerousOverride) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("overridden.swf")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"danger\": {" " \"previous\":\"safe\"," " \"current\":\"file\"}}]", result_id))); item->ValidateDangerousDownload(); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"danger\": {" " \"previous\":\"file\"," " \"current\":\"accepted\"}}]", result_id))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); EXPECT_EQ(downloads_directory().AppendASCII("overridden.swf"), item->GetTargetFilePath()); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_ReferencesParentInvalid) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("sneaky/../../sneaky.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_IllegalFilename) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("<")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_IllegalFilenameExtension) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL( "My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}/foo")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } #if defined(OS_WIN) #define MAYBE_DownloadExtensionTest_OnDeterminingFilename_ReservedFilename\ DISABLED_DownloadExtensionTest_OnDeterminingFilename_ReservedFilename #else #define MAYBE_DownloadExtensionTest_OnDeterminingFilename_ReservedFilename\ DownloadExtensionTest_OnDeterminingFilename_ReservedFilename #endif IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, MAYBE_DownloadExtensionTest_OnDeterminingFilename_ReservedFilename) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("con.foo")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_CurDirInvalid) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL(".")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_ParentDirInvalid) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("..")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_AbsPathInvalid) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. Absolute paths should be rejected. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, downloads_directory().Append(FILE_PATH_LITERAL("sneaky.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_EmptyBasenameInvalid) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. Empty basenames should be rejected. std::string error; ASSERT_FALSE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("foo/")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_Override) { GoOnTheRecord(); LoadExtension("downloads_split"); AddFilenameDeterminer(); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("slow.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); // Start downloading a file. result.reset(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller2(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. // Also test that DetermineFilename allows (chrome) extensions to set // filenames without (filename) extensions. (Don't ask about v8 extensions or // python extensions or kernel extensions or firefox extensions...) error = ""; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("foo")), api::FILENAME_CONFLICT_ACTION_OVERWRITE, &error)); EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("foo").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } // TODO test precedence rules: install_time IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_RemoveFilenameDeterminer) { ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); GoOnTheRecord(); LoadExtension("downloads_split"); content::RenderProcessHost* host = AddFilenameDeterminer(); std::string download_url = test_server()->GetURL("slow?0").spec(); // Start downloading a file. scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Remove a determiner while waiting for it. RemoveFilenameDeterminer(host); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_IncognitoSplit) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); AddFilenameDeterminer(); GoOffTheRecord(); AddFilenameDeterminer(); // Start an on-record download. GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); // Wait for the onCreated and onDeterminingFilename events. ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"incognito\": false," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename events. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("42.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); // Start an incognito download for comparison. GoOffTheRecord(); result.reset(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller2(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": true," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); // On-Record renderers should not see events for off-record items. ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"incognito\": true," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. error = ""; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), false, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("5.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("5.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, DownloadExtensionTest_OnDeterminingFilename_IncognitoSpanning) { LoadExtension("downloads_spanning"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); AddFilenameDeterminer(); // There is a single extension renderer that sees both on-record and // off-record events. The extension functions see the on-record profile with // include_incognito=true. // Start an on-record download. GoOnTheRecord(); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); // Wait for the onCreated and onDeterminingFilename events. ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"incognito\": false," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename events. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), true, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("42.txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); // Start an incognito download for comparison. GoOffTheRecord(); result.reset(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", download_url.c_str()))); ASSERT_TRUE(result.get()); result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ScopedCancellingItem canceller2(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": true," " \"id\": %d," " \"mime\": \"text/plain\"," " \"paused\": false," " \"url\": \"%s\"}]", result_id, download_url.c_str()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"incognito\": true," " \"filename\":\"slow.txt\"}]", result_id))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); // Respond to the onDeterminingFilename. error = ""; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), true, GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", result_id, GetFilename("42 (1).txt").c_str()))); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", result_id))); } #if defined(OS_WIN) // This test is very flaky on Win XP and Aura. http://crbug.com/248438 #define MAYBE_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume \ DISABLED_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume #else #define MAYBE_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume \ DownloadExtensionTest_OnDeterminingFilename_InterruptedResume #endif // Test download interruption while extensions determining filename. Should not // re-dispatch onDeterminingFilename. IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, MAYBE_DownloadExtensionTest_OnDeterminingFilename_InterruptedResume) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); ASSERT_TRUE(test_server()->Start()); GoOnTheRecord(); content::RenderProcessHost* host = AddFilenameDeterminer(); // Start a download. DownloadItem* item = NULL; { DownloadManager* manager = GetCurrentManager(); scoped_ptr observer( new JustInProgressDownloadObserver(manager, 1)); ASSERT_EQ(0, manager->InProgressCount()); ASSERT_EQ(0, manager->NonMaliciousInProgressCount()); // Tabs created just for a download are automatically closed, invalidating // the download's WebContents. Downloads without WebContents cannot be // resumed. http://crbug.com/225901 ui_test_utils::NavigateToURLWithDisposition( current_browser(), GURL(URLRequestSlowDownloadJob::kUnknownSizeUrl), CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); observer->WaitForFinished(); EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::IN_PROGRESS)); DownloadManager::DownloadVector items; manager->GetAllDownloads(&items); for (DownloadManager::DownloadVector::iterator iter = items.begin(); iter != items.end(); ++iter) { if ((*iter)->GetState() == DownloadItem::IN_PROGRESS) { // There should be only one IN_PROGRESS item. EXPECT_EQ(NULL, item); item = *iter; } } ASSERT_TRUE(item); } ScopedCancellingItem canceller(item); // Wait for the onCreated and onDeterminingFilename event. ASSERT_TRUE(WaitFor(api::OnCreated::kEventName, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," " \"id\": %d," " \"mime\": \"application/octet-stream\"," " \"paused\": false}]", item->GetId()))); ASSERT_TRUE(WaitFor( api::OnDeterminingFilename::kEventName, base::StringPrintf("[{\"id\": %d," " \"incognito\": false," " \"filename\":\"download-unknown-size\"}]", item->GetId()))); ASSERT_TRUE(item->GetTargetFilePath().empty()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); ClearEvents(); ui_test_utils::NavigateToURLWithDisposition( current_browser(), GURL(URLRequestSlowDownloadJob::kErrorDownloadUrl), NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); // Errors caught before filename determination are delayed until after // filename determination. std::string error; ASSERT_TRUE(ExtensionDownloadsEventRouter::DetermineFilename( current_browser()->profile(), false, GetExtensionId(), item->GetId(), base::FilePath(FILE_PATH_LITERAL("42.txt")), api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)) << error; EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"filename\": {" " \"previous\": \"\"," " \"current\": \"%s\"}}]", item->GetId(), GetFilename("42.txt").c_str()))); content::DownloadUpdatedObserver interrupted(item, base::Bind( ItemIsInterrupted)); ASSERT_TRUE(interrupted.WaitForEvent()); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"error\":{\"current\":\"NETWORK_FAILED\"}," " \"state\":{" " \"previous\":\"in_progress\"," " \"current\":\"interrupted\"}}]", item->GetId()))); ClearEvents(); // Downloads that are restarted on resumption trigger another download target // determination. RemoveFilenameDeterminer(host); item->Resume(); // Errors caught before filename determination is complete are delayed until // after filename determination so that, on resumption, filename determination // does not need to be re-done. So, there will not be a second // onDeterminingFilename event. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"error\":{\"previous\":\"NETWORK_FAILED\"}," " \"state\":{" " \"previous\":\"interrupted\"," " \"current\":\"in_progress\"}}]", item->GetId()))); ClearEvents(); FinishPendingSlowDownloads(); // The download should complete successfully. ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf("[{\"id\": %d," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"complete\"}}]", item->GetId()))); } IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SetShelfEnabled) { LoadExtension("downloads_split"); EXPECT_TRUE(RunFunction(new DownloadsSetShelfEnabledFunction(), "[false]")); EXPECT_FALSE(DownloadServiceFactory::GetForBrowserContext( browser()->profile())->IsShelfEnabled()); EXPECT_TRUE(RunFunction(new DownloadsSetShelfEnabledFunction(), "[true]")); EXPECT_TRUE(DownloadServiceFactory::GetForBrowserContext( browser()->profile())->IsShelfEnabled()); // TODO(benjhayden) Test that existing shelves are hidden. // TODO(benjhayden) Test multiple extensions. // TODO(benjhayden) Test disabling extensions. // TODO(benjhayden) Test that browsers associated with other profiles are not // affected. // TODO(benjhayden) Test incognito. } // TODO(benjhayden) Figure out why DisableExtension() does not fire // OnListenerRemoved. // TODO(benjhayden) Test that the shelf is shown for download() both with and // without a WebContents. void OnDangerPromptCreated(DownloadDangerPrompt* prompt) { prompt->InvokeActionForTesting(DownloadDangerPrompt::ACCEPT); } IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_AcceptDanger) { // Download a file that will be marked dangerous; click the browser action // button; the browser action poup will call acceptDanger(); when the // DownloadDangerPrompt is created, pretend that the user clicks the Accept // button; wait until the download completes. LoadExtension("downloads_split"); scoped_ptr result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), "[{\"url\": \"data:,\", \"filename\": \"dangerous.swf\"}]")); ASSERT_TRUE(result.get()); int result_id = -1; ASSERT_TRUE(result->GetAsInteger(&result_id)); DownloadItem* item = GetCurrentManager()->GetDownload(result_id); ASSERT_TRUE(item); ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf( "[{\"id\": %d, " " \"danger\": {" " \"previous\": \"safe\"," " \"current\": \"file\"}}]", result_id))); ASSERT_TRUE(item->IsDangerous()); ScopedCancellingItem canceller(item); scoped_ptr observer( new content::DownloadTestObserverTerminal( GetCurrentManager(), 1, content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE)); DownloadsAcceptDangerFunction::OnPromptCreatedCallback callback = base::Bind(&OnDangerPromptCreated); DownloadsAcceptDangerFunction::OnPromptCreatedForTesting( &callback); BrowserActionTestUtil(browser()).Press(0); observer->WaitForFinished(); } class DownloadsApiTest : public ExtensionApiTest { public: DownloadsApiTest() {} virtual ~DownloadsApiTest() {} private: DISALLOW_COPY_AND_ASSIGN(DownloadsApiTest); }; IN_PROC_BROWSER_TEST_F(DownloadsApiTest, DownloadsApiTest) { ASSERT_TRUE(RunExtensionTest("downloads")) << message_; } TEST(DownloadInterruptReasonEnumsSynced, DownloadInterruptReasonEnumsSynced) { #define INTERRUPT_REASON(name, value) \ EXPECT_EQ(InterruptReasonContentToExtension( \ content::DOWNLOAD_INTERRUPT_REASON_##name), \ api::INTERRUPT_REASON_##name); \ EXPECT_EQ(InterruptReasonExtensionToContent( \ api::INTERRUPT_REASON_##name), \ content::DOWNLOAD_INTERRUPT_REASON_##name); #include "content/public/browser/download_interrupt_reason_values.h" #undef INTERRUPT_REASON } TEST(ExtensionDetermineDownloadFilenameInternal, ExtensionDetermineDownloadFilenameInternal) { std::string winner_id; base::FilePath filename; extensions::api::downloads::FilenameConflictAction conflict_action = api::FILENAME_CONFLICT_ACTION_UNIQUIFY; extensions::ExtensionWarningSet warnings; // Empty incumbent determiner warnings.clear(); ExtensionDownloadsEventRouter::DetermineFilenameInternal( base::FilePath(FILE_PATH_LITERAL("a")), api::FILENAME_CONFLICT_ACTION_OVERWRITE, "suggester", base::Time::Now(), "", base::Time(), &winner_id, &filename, &conflict_action, &warnings); EXPECT_EQ("suggester", winner_id); EXPECT_EQ(FILE_PATH_LITERAL("a"), filename.value()); EXPECT_EQ(api::FILENAME_CONFLICT_ACTION_OVERWRITE, conflict_action); EXPECT_TRUE(warnings.empty()); // Incumbent wins warnings.clear(); ExtensionDownloadsEventRouter::DetermineFilenameInternal( base::FilePath(FILE_PATH_LITERAL("b")), api::FILENAME_CONFLICT_ACTION_PROMPT, "suggester", base::Time::Now() - base::TimeDelta::FromDays(1), "incumbent", base::Time::Now(), &winner_id, &filename, &conflict_action, &warnings); EXPECT_EQ("incumbent", winner_id); EXPECT_EQ(FILE_PATH_LITERAL("a"), filename.value()); EXPECT_EQ(api::FILENAME_CONFLICT_ACTION_OVERWRITE, conflict_action); EXPECT_FALSE(warnings.empty()); EXPECT_EQ(extensions::ExtensionWarning::kDownloadFilenameConflict, warnings.begin()->warning_type()); EXPECT_EQ("suggester", warnings.begin()->extension_id()); // Suggester wins warnings.clear(); ExtensionDownloadsEventRouter::DetermineFilenameInternal( base::FilePath(FILE_PATH_LITERAL("b")), api::FILENAME_CONFLICT_ACTION_PROMPT, "suggester", base::Time::Now(), "incumbent", base::Time::Now() - base::TimeDelta::FromDays(1), &winner_id, &filename, &conflict_action, &warnings); EXPECT_EQ("suggester", winner_id); EXPECT_EQ(FILE_PATH_LITERAL("b"), filename.value()); EXPECT_EQ(api::FILENAME_CONFLICT_ACTION_PROMPT, conflict_action); EXPECT_FALSE(warnings.empty()); EXPECT_EQ(extensions::ExtensionWarning::kDownloadFilenameConflict, warnings.begin()->warning_type()); EXPECT_EQ("incumbent", warnings.begin()->extension_id()); } #endif // http://crbug.com/3061144