diff options
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/api/downloads/downloads_api.cc | 489 | ||||
-rw-r--r-- | chrome/browser/extensions/api/downloads/downloads_api.h | 74 | ||||
-rw-r--r-- | chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc (renamed from chrome/browser/extensions/api/downloads/downloads_api_unittest.cc) | 323 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_function_histogram_value.h | 2 |
4 files changed, 581 insertions, 307 deletions
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc index 7de85f4..0bfa228 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api.cc @@ -14,6 +14,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" +#include "base/file_util.h" #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" @@ -30,9 +31,11 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/download/download_danger_prompt.h" #include "chrome/browser/download/download_file_icon_extractor.h" +#include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_query.h" #include "chrome/browser/download/download_service.h" #include "chrome/browser/download/download_service_factory.h" +#include "chrome/browser/download/download_shelf.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/extensions/event_names.h" #include "chrome/browser/extensions/event_router.h" @@ -43,8 +46,10 @@ #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/icon_loader.h" #include "chrome/browser/icon_manager.h" +#include "chrome/browser/platform_util.h" #include "chrome/browser/renderer_host/chrome_render_message_filter.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/common/cancelable_task_tracker.h" #include "chrome/common/extensions/api/downloads.h" #include "chrome/common/extensions/permissions/permissions_data.h" @@ -75,24 +80,34 @@ using content::DownloadManager; namespace download_extension_errors { -// Error messages -const char kGenericError[] = "I'm afraid I can't do that"; -const char kIconNotFoundError[] = "Icon not found"; -const char kInvalidDangerTypeError[] = "Invalid danger type"; -const char kInvalidFilenameError[] = "Invalid filename"; -const char kInvalidFilterError[] = "Invalid query filter"; -const char kInvalidOperationError[] = "Invalid operation"; -const char kInvalidOrderByError[] = "Invalid orderBy field"; +const char kEmptyFile[] = "Filename not yet determined"; +const char kFileAlreadyDeleted[] = "Download file already deleted"; +const char kIconNotFound[] = "Icon not found"; +const char kInvalidDangerType[] = "Invalid danger type"; +const char kInvalidFilename[] = "Invalid filename"; +const char kInvalidFilter[] = "Invalid query filter"; +const char kInvalidHeader[] = "Invalid request header"; +const char kInvalidId[] = "Invalid downloadId"; +const char kInvalidOrderBy[] = "Invalid orderBy field"; const char kInvalidQueryLimit[] = "Invalid query limit"; -const char kInvalidStateError[] = "Invalid state"; -const char kInvalidURLError[] = "Invalid URL"; -const char kNotImplementedError[] = "NotImplemented"; -const char kTooManyListenersError[] = "Each extension may have at most one " +const char kInvalidState[] = "Invalid state"; +const char kInvalidURL[] = "Invalid URL"; +const char kInvisibleContext[] = "Javascript execution context is not visible " + "(tab, window, popup bubble)"; +const char kNotComplete[] = "Download must be complete"; +const char kNotDangerous[] = "Download must be dangerous"; +const char kNotInProgress[] = "Download must be in progress"; +const char kNotResumable[] = "DownloadItem.canResume must be true"; +const char kOpenPermission[] = "The \"downloads.open\" permission is required"; +const char kTooManyListeners[] = "Each extension may have at most one " "onDeterminingFilename listener between all of its renderer execution " "contexts."; +const char kUnexpectedDeterminer[] = "Unexpected determineFilename call"; } // namespace download_extension_errors +namespace errors = download_extension_errors; + namespace { // Default icon size for getFileIcon() in pixels. @@ -100,29 +115,31 @@ const int kDefaultIconSize = 32; // Parameter keys const char kBytesReceivedKey[] = "bytesReceived"; -const char kDangerAcceptedKey[] = "dangerAccepted"; +const char kCanResumeKey[] = "canResume"; +const char kDangerAccepted[] = "accepted"; const char kDangerContent[] = "content"; const char kDangerFile[] = "file"; +const char kDangerHost[] = "host"; const char kDangerKey[] = "danger"; const char kDangerSafe[] = "safe"; const char kDangerUncommon[] = "uncommon"; const char kDangerUnwanted[] = "unwanted"; -const char kDangerAccepted[] = "accepted"; -const char kDangerHost[] = "host"; const char kDangerUrl[] = "url"; const char kEndTimeKey[] = "endTime"; const char kEndedAfterKey[] = "endedAfter"; const char kEndedBeforeKey[] = "endedBefore"; const char kErrorKey[] = "error"; +const char kEstimatedEndTimeKey[] = "estimatedEndTime"; const char kExistsKey[] = "exists"; const char kFileSizeKey[] = "fileSize"; const char kFilenameKey[] = "filename"; const char kFilenameRegexKey[] = "filenameRegex"; const char kIdKey[] = "id"; -const char kIncognito[] = "incognito"; +const char kIncognitoKey[] = "incognito"; const char kMimeKey[] = "mime"; const char kPausedKey[] = "paused"; const char kQueryKey[] = "query"; +const char kReferrerUrlKey[] = "referrer"; const char kStartTimeKey[] = "startTime"; const char kStartedAfterKey[] = "startedAfter"; const char kStartedBeforeKey[] = "startedBefore"; @@ -216,30 +233,34 @@ scoped_ptr<base::DictionaryValue> DownloadItemToJSON( json->SetInteger(kIdKey, download_item->GetId()); const GURL& url = download_item->GetOriginalUrl(); json->SetString(kUrlKey, (url.is_valid() ? url.spec() : std::string())); + const GURL& referrer = download_item->GetReferrerUrl(); + json->SetString(kReferrerUrlKey, (referrer.is_valid() ? referrer.spec() + : std::string())); json->SetString(kFilenameKey, download_item->GetTargetFilePath().LossyDisplayName()); json->SetString(kDangerKey, DangerString(download_item->GetDangerType())); - if (download_item->GetDangerType() != - content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) - json->SetBoolean(kDangerAcceptedKey, - download_item->GetDangerType() == - content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED); json->SetString(kStateKey, StateString(download_item->GetState())); + json->SetBoolean(kCanResumeKey, download_item->CanResume()); json->SetBoolean(kPausedKey, download_item->IsPaused()); json->SetString(kMimeKey, download_item->GetMimeType()); json->SetString(kStartTimeKey, TimeToISO8601(download_item->GetStartTime())); json->SetInteger(kBytesReceivedKey, download_item->GetReceivedBytes()); json->SetInteger(kTotalBytesKey, download_item->GetTotalBytes()); - json->SetBoolean(kIncognito, incognito); + json->SetBoolean(kIncognitoKey, incognito); if (download_item->GetState() == DownloadItem::INTERRUPTED) { - json->SetInteger(kErrorKey, static_cast<int>( + json->SetString(kErrorKey, content::InterruptReasonDebugString( download_item->GetLastReason())); } else if (download_item->GetState() == DownloadItem::CANCELLED) { - json->SetInteger(kErrorKey, static_cast<int>( + json->SetString(kErrorKey, content::InterruptReasonDebugString( content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED)); } if (!download_item->GetEndTime().is_null()) json->SetString(kEndTimeKey, TimeToISO8601(download_item->GetEndTime())); + base::TimeDelta time_remaining; + if (download_item->TimeRemaining(&time_remaining)) { + base::Time now = base::Time::Now(); + json->SetString(kEstimatedEndTimeKey, TimeToISO8601(now + time_remaining)); + } // TODO(benjhayden): Implement fileSize. json->SetInteger(kFileSizeKey, download_item->GetTotalBytes()); return scoped_ptr<base::DictionaryValue>(json); @@ -300,7 +321,6 @@ typedef base::hash_map<std::string, DownloadQuery::FilterType> FilterTypeMap; void InitFilterTypeMap(FilterTypeMap& filter_types) { filter_types[kBytesReceivedKey] = DownloadQuery::FILTER_BYTES_RECEIVED; - filter_types[kDangerAcceptedKey] = DownloadQuery::FILTER_DANGER_ACCEPTED; filter_types[kExistsKey] = DownloadQuery::FILTER_EXISTS; filter_types[kFilenameKey] = DownloadQuery::FILTER_FILENAME; filter_types[kFilenameRegexKey] = DownloadQuery::FILTER_FILENAME_REGEX; @@ -326,7 +346,6 @@ typedef base::hash_map<std::string, DownloadQuery::SortType> SortTypeMap; void InitSortTypeMap(SortTypeMap& sorter_types) { sorter_types[kBytesReceivedKey] = DownloadQuery::SORT_BYTES_RECEIVED; sorter_types[kDangerKey] = DownloadQuery::SORT_DANGER; - sorter_types[kDangerAcceptedKey] = DownloadQuery::SORT_DANGER_ACCEPTED; sorter_types[kEndTimeKey] = DownloadQuery::SORT_END_TIME; sorter_types[kExistsKey] = DownloadQuery::SORT_EXISTS; sorter_types[kFilenameKey] = DownloadQuery::SORT_FILENAME; @@ -372,16 +391,6 @@ DownloadItem* GetDownload(Profile* profile, bool include_incognito, int id) { return download_item; } -DownloadItem* GetDownloadIfInProgress( - Profile* profile, - bool include_incognito, - int id) { - DownloadItem* download_item = GetDownload(profile, include_incognito, id); - if (download_item && (download_item->GetState() == DownloadItem::IN_PROGRESS)) - return download_item; - return NULL; -} - enum DownloadsFunctionName { DOWNLOADS_FUNCTION_DOWNLOAD = 0, DOWNLOADS_FUNCTION_SEARCH = 1, @@ -395,6 +404,8 @@ enum DownloadsFunctionName { DOWNLOADS_FUNCTION_DRAG = 9, DOWNLOADS_FUNCTION_GET_FILE_ICON = 10, DOWNLOADS_FUNCTION_OPEN = 11, + DOWNLOADS_FUNCTION_REMOVE_FILE = 12, + DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER = 13, // Insert new values here, not at the beginning. DOWNLOADS_FUNCTION_LAST }; @@ -406,7 +417,9 @@ void RecordApiFunctions(DownloadsFunctionName function) { } void CompileDownloadQueryOrderBy( - const std::string& order_by_str, std::string* error, DownloadQuery* query) { + const std::vector<std::string>& order_by_strs, + std::string* error, + DownloadQuery* query) { // TODO(benjhayden): Consider switching from LazyInstance to explicit string // comparisons. static base::LazyInstance<SortTypeMap> sorter_types = @@ -414,8 +427,6 @@ void CompileDownloadQueryOrderBy( if (sorter_types.Get().size() == 0) InitSortTypeMap(sorter_types.Get()); - std::vector<std::string> order_by_strs; - base::SplitString(order_by_str, ' ', &order_by_strs); for (std::vector<std::string>::const_iterator iter = order_by_strs.begin(); iter != order_by_strs.end(); ++iter) { std::string term_str = *iter; @@ -429,7 +440,7 @@ void CompileDownloadQueryOrderBy( SortTypeMap::const_iterator sorter_type = sorter_types.Get().find(term_str); if (sorter_type == sorter_types.Get().end()) { - *error = download_extension_errors::kInvalidOrderByError; + *error = errors::kInvalidOrderBy; return; } query->AddSorter(sorter_type->second, direction); @@ -451,19 +462,24 @@ void RunDownloadQuery( DownloadQuery query_out; + size_t limit = 1000; if (query_in.limit.get()) { if (*query_in.limit.get() < 0) { - *error = download_extension_errors::kInvalidQueryLimit; + *error = errors::kInvalidQueryLimit; return; } - query_out.Limit(*query_in.limit.get()); + limit = *query_in.limit.get(); } + if (limit > 0) { + query_out.Limit(limit); + } + std::string state_string = extensions::api::downloads::ToString(query_in.state); if (!state_string.empty()) { DownloadItem::DownloadState state = StateEnumFromString(state_string); if (state == DownloadItem::MAX_DOWNLOAD_STATE) { - *error = download_extension_errors::kInvalidStateError; + *error = errors::kInvalidState; return; } query_out.AddFilter(state); @@ -474,7 +490,7 @@ void RunDownloadQuery( content::DownloadDangerType danger_type = DangerEnumFromString( danger_string); if (danger_type == content::DOWNLOAD_DANGER_TYPE_MAX) { - *error = download_extension_errors::kInvalidDangerTypeError; + *error = errors::kInvalidDangerType; return; } query_out.AddFilter(danger_type); @@ -492,7 +508,7 @@ void RunDownloadQuery( filter_types.Get().find(query_json_field.key()); if (filter_type != filter_types.Get().end()) { if (!query_out.AddFilter(filter_type->second, query_json_field.value())) { - *error = download_extension_errors::kInvalidFilterError; + *error = errors::kInvalidFilter; return; } } @@ -514,6 +530,21 @@ void RunDownloadQuery( query_out.Search(all_items.begin(), all_items.end(), results); } +DownloadPathReservationTracker::FilenameConflictAction ConvertConflictAction( + extensions::api::downloads::FilenameConflictAction action) { + switch (action) { + case extensions::api::downloads::FILENAME_CONFLICT_ACTION_NONE: + case extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY: + return DownloadPathReservationTracker::UNIQUIFY; + case extensions::api::downloads::FILENAME_CONFLICT_ACTION_OVERWRITE: + return DownloadPathReservationTracker::OVERWRITE; + case extensions::api::downloads::FILENAME_CONFLICT_ACTION_PROMPT: + return DownloadPathReservationTracker::PROMPT; + } + NOTREACHED(); + return DownloadPathReservationTracker::UNIQUIFY; +} + class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { public: static ExtensionDownloadsEventRouterData* Get(DownloadItem* download_item) { @@ -532,6 +563,8 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { : updated_(0), changed_fired_(0), json_(json_item.Pass()), + creator_conflict_action_( + extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY), determined_conflict_action_( extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY) { download_item->SetUserData(kKey, this); @@ -557,6 +590,10 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { const ExtensionDownloadsEventRouter::FilenameChangedCallback& change) { filename_no_change_ = no_change; filename_change_ = change; + determined_filename_ = creator_suggested_filename_; + determined_conflict_action_ = creator_conflict_action_; + // determiner_.install_time should default to 0 so that creator suggestions + // should be lower priority than any actual onDeterminingFilename listeners. } void ClearPendingDeterminers() { @@ -604,6 +641,28 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { return false; } + void CreatorSuggestedFilename( + const base::FilePath& filename, + extensions::api::downloads::FilenameConflictAction conflict_action) { + creator_suggested_filename_ = filename; + creator_conflict_action_ = conflict_action; + } + + base::FilePath creator_suggested_filename() const { + return creator_suggested_filename_; + } + + extensions::api::downloads::FilenameConflictAction + creator_conflict_action() const { + return creator_conflict_action_; + } + + void ResetCreatorSuggestion() { + creator_suggested_filename_.clear(); + creator_conflict_action_ = + extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY; + } + // Returns false if this |extension_id| was not expected or if this // |extension_id| has already reported. The caller is responsible for // validating |filename|. @@ -665,22 +724,15 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { filename_no_change_.Run(); } else { if (!filename_change_.is_null()) { - DownloadPathReservationTracker::FilenameConflictAction conflict_action = - DownloadPathReservationTracker::UNIQUIFY; - if (determined_conflict_action_ == - extensions::api::downloads::FILENAME_CONFLICT_ACTION_OVERWRITE) - conflict_action = DownloadPathReservationTracker::OVERWRITE; - if (determined_conflict_action_ == - extensions::api::downloads::FILENAME_CONFLICT_ACTION_PROMPT) - conflict_action = DownloadPathReservationTracker::PROMPT; - filename_change_.Run(determined_filename_, conflict_action); + filename_change_.Run(determined_filename_, ConvertConflictAction( + determined_conflict_action_)); } } // Don't clear determiners_ immediately in case there's a second listener // for one of the extensions, so that DetermineFilename can return - // kTooManyListenersError. After a few seconds, DetermineFilename will - // return kInvalidOperationError instead of kTooManyListenersError so that - // determiners_ doesn't keep hogging memory. + // kTooManyListeners. After a few seconds, DetermineFilename will return + // kUnexpectedDeterminer instead of kTooManyListeners so that determiners_ + // doesn't keep hogging memory. weak_ptr_factory_.reset( new base::WeakPtrFactory<ExtensionDownloadsEventRouterData>(this)); base::MessageLoopForUI::current()->PostDelayedTask( @@ -699,6 +751,9 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { DeterminerInfoVector determiners_; + base::FilePath creator_suggested_filename_; + extensions::api::downloads::FilenameConflictAction + creator_conflict_action_; base::FilePath determined_filename_; extensions::api::downloads::FilenameConflictAction determined_conflict_action_; @@ -796,6 +851,35 @@ void OnDeterminingFilenameWillDispatchCallback( data->AddPendingDeterminer(extension->id(), installed); } +bool Fault(bool error, + const char* message_in, + std::string* message_out) { + if (!error) + return false; + *message_out = message_in; + return true; +} + +bool InvalidId(DownloadItem* valid_item, std::string* message_out) { + return Fault(!valid_item, errors::kInvalidId, message_out); +} + +bool IsDownloadDeltaField(const std::string& field) { + return ((field == kUrlKey) || + (field == kFilenameKey) || + (field == kDangerKey) || + (field == kMimeKey) || + (field == kStartTimeKey) || + (field == kEndTimeKey) || + (field == kStateKey) || + (field == kCanResumeKey) || + (field == kPausedKey) || + (field == kErrorKey) || + (field == kTotalBytesKey) || + (field == kFileSizeKey) || + (field == kExistsKey)); +} + } // namespace DownloadsDownloadFunction::DownloadsDownloadFunction() {} @@ -808,10 +892,8 @@ bool DownloadsDownloadFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(params.get()); const extensions::api::downloads::DownloadOptions& options = params->options; GURL download_url(options.url); - if (!download_url.is_valid()) { - error_ = download_extension_errors::kInvalidURLError; + if (Fault(!download_url.is_valid(), errors::kInvalidURL, &error_)) return false; - } Profile* current_profile = profile(); if (include_incognito() && profile()->HasOffTheRecordProfile()) @@ -824,28 +906,24 @@ bool DownloadsDownloadFunction::RunImpl() { render_view_host()->GetRoutingID(), current_profile->GetResourceContext())); + base::FilePath creator_suggested_filename; if (options.filename.get()) { - // TODO(benjhayden): Make json_schema_compiler generate string16s instead of - // std::strings. Can't get filename16 from options.ToValue() because that - // converts it from std::string. +#if defined(OS_WIN) + // Can't get filename16 from options.ToValue() because that converts it from + // std::string. base::DictionaryValue* options_value = NULL; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options_value)); - string16 filename16; + base::string16 filename16; EXTENSION_FUNCTION_VALIDATE(options_value->GetString( kFilenameKey, &filename16)); -#if defined(OS_WIN) - base::FilePath file_path(filename16); + creator_suggested_filename = base::FilePath(filename16); #elif defined(OS_POSIX) - base::FilePath file_path(*options.filename.get()); + creator_suggested_filename = base::FilePath(*options.filename.get()); #endif - if (!net::IsSafePortableBasename(file_path) || - (file_path.DirName().value() != base::FilePath::kCurrentDirectory)) { - error_ = download_extension_errors::kInvalidFilenameError; + if (!net::IsSafePortableRelativePath(creator_suggested_filename)) { + error_ = errors::kInvalidFilename; return false; } - // TODO(benjhayden) Ensure that this filename is interpreted as a path - // relative to the default downloads directory without allowing '..'. - download_params->set_suggested_name(filename16); } if (options.save_as.get()) @@ -859,7 +937,7 @@ bool DownloadsDownloadFunction::RunImpl() { ++iter) { const HeaderNameValuePair& name_value = **iter; if (!net::HttpUtil::IsSafeHeader(name_value.name)) { - error_ = download_extension_errors::kGenericError; + error_ = errors::kInvalidHeader; return false; } download_params->add_request_header(name_value.name, name_value.value); @@ -873,24 +951,40 @@ bool DownloadsDownloadFunction::RunImpl() { if (options.body.get()) download_params->set_post_body(*options.body.get()); download_params->set_callback(base::Bind( - &DownloadsDownloadFunction::OnStarted, this)); + &DownloadsDownloadFunction::OnStarted, this, + creator_suggested_filename, options.conflict_action)); // Prevent login prompts for 401/407 responses. download_params->set_load_flags(net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); DownloadManager* manager = BrowserContext::GetDownloadManager( current_profile); manager->DownloadUrl(download_params.Pass()); + download_util::RecordDownloadSource(download_util::INITIATED_BY_EXTENSION); RecordApiFunctions(DOWNLOADS_FUNCTION_DOWNLOAD); return true; } void DownloadsDownloadFunction::OnStarted( - DownloadItem* item, net::Error error) { + const base::FilePath& creator_suggested_filename, + extensions::api::downloads::FilenameConflictAction creator_conflict_action, + DownloadItem* item, + net::Error error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << __FUNCTION__ << " " << item << " " << error; if (item) { DCHECK_EQ(net::OK, error); SetResult(base::Value::CreateIntegerValue(item->GetId())); + if (!creator_suggested_filename.empty()) { + ExtensionDownloadsEventRouterData* data = + ExtensionDownloadsEventRouterData::Get(item); + if (!data) { + data = new ExtensionDownloadsEventRouterData( + item, + scoped_ptr<base::DictionaryValue>(new base::DictionaryValue())); + } + data->CreatorSuggestedFilename( + creator_suggested_filename, creator_conflict_action); + } } else { DCHECK_NE(net::OK, error); error_ = net::ErrorToString(error); @@ -944,20 +1038,17 @@ bool DownloadsPauseFunction::RunImpl() { scoped_ptr<extensions::api::downloads::Pause::Params> params( extensions::api::downloads::Pause::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); - DownloadItem* download_item = GetDownloadIfInProgress( + DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (download_item == NULL) { - // This could be due to an invalid download ID, or it could be due to the - // download not being currently active. - error_ = download_extension_errors::kInvalidOperationError; - } else { - // If the item is already paused, this is a no-op and the - // operation will silently succeed. - download_item->Pause(); - } - if (error_.empty()) - RecordApiFunctions(DOWNLOADS_FUNCTION_PAUSE); - return error_.empty(); + if (InvalidId(download_item, &error_) || + Fault(download_item->GetState() != DownloadItem::IN_PROGRESS, + errors::kNotInProgress, &error_)) + return false; + // If the item is already paused, this is a no-op and the operation will + // silently succeed. + download_item->Pause(); + RecordApiFunctions(DOWNLOADS_FUNCTION_PAUSE); + return true; } DownloadsResumeFunction::DownloadsResumeFunction() {} @@ -968,20 +1059,17 @@ bool DownloadsResumeFunction::RunImpl() { scoped_ptr<extensions::api::downloads::Resume::Params> params( extensions::api::downloads::Resume::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); - DownloadItem* download_item = GetDownloadIfInProgress( + DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (download_item == NULL) { - // This could be due to an invalid download ID, or it could be due to the - // download not being currently active. - error_ = download_extension_errors::kInvalidOperationError; - } else { - // Note that if the item isn't paused, this will be a no-op, and - // the extension call will seem successful. - download_item->Resume(); - } - if (error_.empty()) - RecordApiFunctions(DOWNLOADS_FUNCTION_RESUME); - return error_.empty(); + if (InvalidId(download_item, &error_) || + Fault(download_item->IsPaused() && !download_item->CanResume(), + errors::kNotResumable, &error_)) + return false; + // Note that if the item isn't paused, this will be a no-op, and the extension + // call will seem successful. + download_item->Resume(); + RecordApiFunctions(DOWNLOADS_FUNCTION_RESUME); + return true; } DownloadsCancelFunction::DownloadsCancelFunction() {} @@ -992,9 +1080,10 @@ bool DownloadsCancelFunction::RunImpl() { scoped_ptr<extensions::api::downloads::Resume::Params> params( extensions::api::downloads::Resume::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); - DownloadItem* download_item = GetDownloadIfInProgress( + DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (download_item != NULL) + if (download_item && + (download_item->GetState() == DownloadItem::IN_PROGRESS)) download_item->Cancel(true); // |download_item| can be NULL if the download ID was invalid or if the // download is not currently active. Either way, it's not a failure. @@ -1032,6 +1121,52 @@ bool DownloadsEraseFunction::RunImpl() { return true; } +DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {} + +DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() { + if (item_) { + item_->RemoveObserver(this); + item_ = NULL; + } +} + +bool DownloadsRemoveFileFunction::RunImpl() { + scoped_ptr<extensions::api::downloads::RemoveFile::Params> params( + extensions::api::downloads::RemoveFile::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + DownloadItem* download_item = GetDownload( + profile(), include_incognito(), params->download_id); + if (InvalidId(download_item, &error_) || + Fault((download_item->GetState() != DownloadItem::COMPLETE), + errors::kNotComplete, &error_) || + Fault(download_item->GetFileExternallyRemoved(), + errors::kFileAlreadyDeleted, &error_)) + return false; + item_ = download_item; + item_->AddObserver(this); + RecordApiFunctions(DOWNLOADS_FUNCTION_REMOVE_FILE); + download_item->DeleteFile(); + return true; +} + +void DownloadsRemoveFileFunction::OnDownloadUpdated(DownloadItem* download) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(item_, download); + if (!item_->GetFileExternallyRemoved()) + return; + item_->RemoveObserver(this); + item_ = NULL; + SendResponse(true); +} + +void DownloadsRemoveFileFunction::OnDownloadDestroyed(DownloadItem* download) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(item_, download); + item_->RemoveObserver(this); + item_ = NULL; + SendResponse(true); +} + DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {} DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {} @@ -1040,16 +1175,16 @@ bool DownloadsAcceptDangerFunction::RunImpl() { scoped_ptr<extensions::api::downloads::AcceptDanger::Params> params( extensions::api::downloads::AcceptDanger::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); - DownloadItem* download_item = GetDownloadIfInProgress( + DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); content::WebContents* web_contents = dispatcher()->delegate()->GetVisibleWebContents(); - if (!download_item || - !download_item->IsDangerous() || - !web_contents) { - error_ = download_extension_errors::kInvalidOperationError; + if (InvalidId(download_item, &error_) || + Fault(download_item->GetState() != DownloadItem::IN_PROGRESS, + errors::kNotInProgress, &error_) || + Fault(!download_item->IsDangerous(), errors::kNotDangerous, &error_) || + Fault(!web_contents, errors::kInvisibleContext, &error_)) return false; - } RecordApiFunctions(DOWNLOADS_FUNCTION_ACCEPT_DANGER); // DownloadDangerPrompt displays a modal dialog using native widgets that the // user must either accept or cancel. It cannot be scripted. @@ -1058,20 +1193,29 @@ bool DownloadsAcceptDangerFunction::RunImpl() { web_contents, true, base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback, - this, true, params->download_id), - base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback, - this, false, params->download_id)); + this, params->download_id)); // DownloadDangerPrompt deletes itself return true; } void DownloadsAcceptDangerFunction::DangerPromptCallback( - bool accept, int download_id) { - if (accept) { - DownloadItem* download_item = GetDownloadIfInProgress( - profile(), include_incognito(), download_id); - if (download_item) + int download_id, DownloadDangerPrompt::Action action) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DownloadItem* download_item = GetDownload( + profile(), include_incognito(), download_id); + if (InvalidId(download_item, &error_) || + Fault(download_item->GetState() != DownloadItem::IN_PROGRESS, + errors::kNotInProgress, &error_)) + return; + switch (action) { + case DownloadDangerPrompt::ACCEPT: download_item->ValidateDangerousDownload(); + break; + case DownloadDangerPrompt::CANCEL: + download_item->Remove(); + break; + case DownloadDangerPrompt::DISMISS: + break; } SendResponse(error_.empty()); } @@ -1086,15 +1230,27 @@ bool DownloadsShowFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(params.get()); DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (!download_item) { - error_ = download_extension_errors::kInvalidOperationError; + if (InvalidId(download_item, &error_)) return false; - } download_item->ShowDownloadInShell(); RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW); return true; } +DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {} + +DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {} + +bool DownloadsShowDefaultFolderFunction::RunImpl() { + DownloadManager* manager = NULL; + DownloadManager* incognito_manager = NULL; + GetManagers(profile(), include_incognito(), &manager, &incognito_manager); + platform_util::OpenItem(DownloadPrefs::FromDownloadManager( + manager)->DownloadPath()); + RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER); + return true; +} + DownloadsOpenFunction::DownloadsOpenFunction() {} DownloadsOpenFunction::~DownloadsOpenFunction() {} @@ -1105,12 +1261,13 @@ bool DownloadsOpenFunction::RunImpl() { EXTENSION_FUNCTION_VALIDATE(params.get()); DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (!download_item || download_item->GetState() != DownloadItem::COMPLETE || - !GetExtension()->HasAPIPermission( - extensions::APIPermission::kDownloadsOpen)) { - error_ = download_extension_errors::kInvalidOperationError; + if (InvalidId(download_item, &error_) || + Fault(download_item->GetState() != DownloadItem::COMPLETE, + errors::kNotComplete, &error_) || + Fault(!GetExtension()->HasAPIPermission( + extensions::APIPermission::kDownloadsOpen), + errors::kOpenPermission, &error_)) return false; - } download_item->OpenDownload(); RecordApiFunctions(DOWNLOADS_FUNCTION_OPEN); return true; @@ -1128,10 +1285,9 @@ bool DownloadsDragFunction::RunImpl() { profile(), include_incognito(), params->download_id); content::WebContents* web_contents = dispatcher()->delegate()->GetVisibleWebContents(); - if (!download_item || !web_contents) { - error_ = download_extension_errors::kInvalidOperationError; + if (InvalidId(download_item, &error_) || + Fault(!web_contents, errors::kInvisibleContext, &error_)) return false; - } RecordApiFunctions(DOWNLOADS_FUNCTION_DRAG); gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath( download_item->GetTargetFilePath(), IconLoader::NORMAL); @@ -1168,10 +1324,10 @@ bool DownloadsGetFileIconFunction::RunImpl() { icon_size = *options->size.get(); DownloadItem* download_item = GetDownload( profile(), include_incognito(), params->download_id); - if (!download_item || download_item->GetTargetFilePath().empty()) { - error_ = download_extension_errors::kInvalidOperationError; + if (InvalidId(download_item, &error_) || + Fault(download_item->GetTargetFilePath().empty(), + errors::kEmptyFile, &error_)) return false; - } // In-progress downloads return the intermediate filename for GetFullPath() // which doesn't have the final extension. Therefore a good file icon can't be // found, so use GetTargetFilePath() instead. @@ -1186,13 +1342,13 @@ bool DownloadsGetFileIconFunction::RunImpl() { void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (url.empty()) { - error_ = download_extension_errors::kIconNotFoundError; - } else { - RecordApiFunctions(DOWNLOADS_FUNCTION_GET_FILE_ICON); - SetResult(base::Value::CreateStringValue(url)); + if (Fault(url.empty(), errors::kIconNotFound, &error_)) { + SendResponse(false); + return; } - SendResponse(error_.empty()); + RecordApiFunctions(DOWNLOADS_FUNCTION_GET_FILE_ICON); + SetResult(base::Value::CreateStringValue(url)); + SendResponse(true); } ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter( @@ -1267,7 +1423,14 @@ void ExtensionDownloadsEventRouter::OnDeterminingFilename( json); if (!any_determiners) { data->ClearPendingDeterminers(); - no_change.Run(); + if (!data->creator_suggested_filename().empty()) { + change.Run(data->creator_suggested_filename(), + ConvertConflictAction(data->creator_conflict_action())); + // If all listeners are removed, don't keep |data| around. + data->ResetCreatorSuggestion(); + } else { + no_change.Run(); + } } } @@ -1281,28 +1444,19 @@ bool ExtensionDownloadsEventRouter::DetermineFilename( std::string* error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DownloadItem* item = GetDownload(profile, include_incognito, download_id); - if (!item) { - *error = download_extension_errors::kInvalidOperationError; - return false; - } ExtensionDownloadsEventRouterData* data = - ExtensionDownloadsEventRouterData::Get(item); - if (!data) { - *error = download_extension_errors::kInvalidOperationError; - return false; - } + item ? ExtensionDownloadsEventRouterData::Get(item) : NULL; // maxListeners=1 in downloads.idl and suggestCallback in // downloads_custom_bindings.js should prevent duplicate DeterminerCallback // calls from the same renderer, but an extension may have more than one // renderer, so don't DCHECK(!reported). - if (data->DeterminerAlreadyReported(ext_id)) { - *error = download_extension_errors::kTooManyListenersError; + if (InvalidId(item, error) || + Fault(item->GetState() != DownloadItem::IN_PROGRESS, + errors::kNotInProgress, error) || + Fault(!data, errors::kUnexpectedDeterminer, error) || + Fault(data->DeterminerAlreadyReported(ext_id), + errors::kTooManyListeners, error)) return false; - } - if (item->GetState() != DownloadItem::IN_PROGRESS) { - *error = download_extension_errors::kInvalidOperationError; - return false; - } base::FilePath::StringType filename_str(const_filename.value()); // Allow windows-style directory separators on all platforms. std::replace(filename_str.begin(), filename_str.end(), @@ -1311,17 +1465,13 @@ bool ExtensionDownloadsEventRouter::DetermineFilename( bool valid_filename = net::IsSafePortableRelativePath(filename); filename = (valid_filename ? filename.NormalizePathSeparators() : base::FilePath()); - if (!data->DeterminerCallback(ext_id, filename, conflict_action)) { - // Nobody expects this ext_id! - *error = download_extension_errors::kInvalidOperationError; + // If the invalid filename check is moved to before DeterminerCallback(), then + // it will block forever waiting for this ext_id to report. + if (Fault(!data->DeterminerCallback(ext_id, filename, conflict_action), + errors::kUnexpectedDeterminer, error) || + Fault((!const_filename.empty() && !valid_filename), + errors::kInvalidFilename, error)) return false; - } - if (!const_filename.empty() && !valid_filename) { - // If this is moved to before DeterminerCallback(), then it will block - // forever waiting for this ext_id to report. - *error = download_extension_errors::kInvalidFilenameError; - return false; - } return true; } @@ -1357,7 +1507,8 @@ void ExtensionDownloadsEventRouter::OnListenerRemoved( // should proceed. data->DeterminerRemoved(details.extension_id); } - if (!any_listeners) { + if (!any_listeners && + data->creator_suggested_filename().empty()) { ExtensionDownloadsEventRouterData::Remove(*iter); } } @@ -1427,7 +1578,7 @@ void ExtensionDownloadsEventRouter::OnDownloadUpdated( for (base::DictionaryValue::Iterator iter(*new_json.get()); !iter.IsAtEnd(); iter.Advance()) { new_fields.insert(iter.key()); - if (iter.key() != kBytesReceivedKey) { + if (IsDownloadDeltaField(iter.key())) { const base::Value* old_value = NULL; if (!data->json().HasKey(iter.key()) || (data->json().Get(iter.key(), &old_value) && @@ -1444,7 +1595,9 @@ void ExtensionDownloadsEventRouter::OnDownloadUpdated( // difference in |delta|. for (base::DictionaryValue::Iterator iter(data->json()); !iter.IsAtEnd(); iter.Advance()) { - if (new_fields.find(iter.key()) == new_fields.end()) { + if ((new_fields.find(iter.key()) == new_fields.end()) && + IsDownloadDeltaField(iter.key())) { + // estimatedEndTime disappears after completion, but bytesReceived stays. delta->Set(iter.key() + ".previous", iter.value().DeepCopy()); changed = true; } diff --git a/chrome/browser/extensions/api/downloads/downloads_api.h b/chrome/browser/extensions/api/downloads/downloads_api.h index 77b7777..465148c1 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api.h +++ b/chrome/browser/extensions/api/downloads/downloads_api.h @@ -12,6 +12,7 @@ #include "base/strings/string16.h" #include "base/values.h" #include "chrome/browser/download/all_download_item_notifier.h" +#include "chrome/browser/download/download_danger_prompt.h" #include "chrome/browser/download/download_path_reservation_tracker.h" #include "chrome/browser/extensions/event_router.h" #include "chrome/browser/extensions/extension_function.h" @@ -34,18 +35,26 @@ class ResourceDispatcherHost; namespace download_extension_errors { // Errors that can be returned through chrome.runtime.lastError.message. -extern const char kGenericError[]; -extern const char kIconNotFoundError[]; -extern const char kInvalidDangerTypeError[]; -extern const char kInvalidFilenameError[]; -extern const char kInvalidFilterError[]; -extern const char kInvalidOperationError[]; -extern const char kInvalidOrderByError[]; +extern const char kEmptyFile[]; +extern const char kFileAlreadyDeleted[]; +extern const char kIconNotFound[]; +extern const char kInvalidDangerType[]; +extern const char kInvalidFilename[]; +extern const char kInvalidFilter[]; +extern const char kInvalidHeader[]; +extern const char kInvalidId[]; +extern const char kInvalidOrderBy[]; extern const char kInvalidQueryLimit[]; -extern const char kInvalidStateError[]; -extern const char kInvalidURLError[]; -extern const char kNotImplementedError[]; -extern const char kTooManyListenersError[]; +extern const char kInvalidState[]; +extern const char kInvalidURL[]; +extern const char kInvisibleContext[]; +extern const char kNotComplete[]; +extern const char kNotDangerous[]; +extern const char kNotInProgress[]; +extern const char kNotResumable[]; +extern const char kOpenPermission[]; +extern const char kTooManyListeners[]; +extern const char kUnexpectedDeterminer[]; } // namespace download_extension_errors @@ -60,7 +69,12 @@ class DownloadsDownloadFunction : public AsyncExtensionFunction { virtual ~DownloadsDownloadFunction(); private: - void OnStarted(content::DownloadItem* item, net::Error error); + void OnStarted( + const base::FilePath& creator_suggested_filename, + extensions::api::downloads::FilenameConflictAction + creator_conflict_action, + content::DownloadItem* item, + net::Error error); DISALLOW_COPY_AND_ASSIGN(DownloadsDownloadFunction); }; @@ -130,6 +144,25 @@ class DownloadsEraseFunction : public SyncExtensionFunction { DISALLOW_COPY_AND_ASSIGN(DownloadsEraseFunction); }; +class DownloadsRemoveFileFunction : public AsyncExtensionFunction, + public content::DownloadItem::Observer { + public: + DECLARE_EXTENSION_FUNCTION("downloads.removeFile", DOWNLOADS_REMOVEFILE) + DownloadsRemoveFileFunction(); + virtual bool RunImpl() OVERRIDE; + + protected: + virtual ~DownloadsRemoveFileFunction(); + + private: + virtual void OnDownloadUpdated(content::DownloadItem* item) OVERRIDE; + virtual void OnDownloadDestroyed(content::DownloadItem* item) OVERRIDE; + + content::DownloadItem* item_; + + DISALLOW_COPY_AND_ASSIGN(DownloadsRemoveFileFunction); +}; + class DownloadsAcceptDangerFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("downloads.acceptDanger", DOWNLOADS_ACCEPTDANGER) @@ -138,7 +171,8 @@ class DownloadsAcceptDangerFunction : public AsyncExtensionFunction { protected: virtual ~DownloadsAcceptDangerFunction(); - void DangerPromptCallback(bool accept, int download_id); + void DangerPromptCallback(int download_id, + DownloadDangerPrompt::Action action); private: DISALLOW_COPY_AND_ASSIGN(DownloadsAcceptDangerFunction); @@ -157,6 +191,20 @@ class DownloadsShowFunction : public AsyncExtensionFunction { DISALLOW_COPY_AND_ASSIGN(DownloadsShowFunction); }; +class DownloadsShowDefaultFolderFunction : public AsyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "downloads.showDefaultFolder", DOWNLOADS_SHOWDEFAULTFOLDER) + DownloadsShowDefaultFolderFunction(); + virtual bool RunImpl() OVERRIDE; + + protected: + virtual ~DownloadsShowDefaultFolderFunction(); + + private: + DISALLOW_COPY_AND_ASSIGN(DownloadsShowDefaultFolderFunction); +}; + class DownloadsOpenFunction : public SyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("downloads.open", DOWNLOADS_OPEN) diff --git a/chrome/browser/extensions/api/downloads/downloads_api_unittest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index 93a40f2..5060617 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api_unittest.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc @@ -62,6 +62,10 @@ using content::URLRequestSlowDownloadJob; namespace events = extensions::event_names; +namespace errors = download_extension_errors; + +namespace api = extensions::api::downloads; + namespace { // Comparator that orders download items by their ID. Can be used with @@ -349,19 +353,22 @@ class DownloadExtensionTest : public ExtensionApiTest { current_browser()->profile(), event_name, json_args); } - bool WaitForInterruption(DownloadItem* item, int expected_error, - const std::string& on_created_event) { + bool WaitForInterruption( + DownloadItem* item, + content::DownloadInterruptReason expected_error, + const std::string& on_created_event) { if (!WaitFor(events::kOnDownloadCreated, on_created_event)) return false; // Now, onCreated is always fired before interruption. return WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," - " \"error\": {\"current\": %d}," - " \"state\": {" - " \"previous\": \"in_progress\"," - " \"current\": \"interrupted\"}}]", - item->GetId(), - expected_error)); + " \"error\": {\"current\": \"%s\"}," + " \"state\": {" + " \"previous\": \"in_progress\"," + " \"current\": \"interrupted\"}}]", + item->GetId(), + content::InterruptReasonDebugString( + expected_error).c_str())); } void ClearEvents() { @@ -904,12 +911,42 @@ 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(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kInvalidId, RunFunctionAndReturnError( new DownloadsOpenFunction(), "[-42]").c_str()); @@ -925,7 +962,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, " \"paused\": false," " \"url\": \"%s\"}]", download_item->GetURL().spec().c_str()))); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kNotComplete, RunFunctionAndReturnError( new DownloadsOpenFunction(), DownloadItemIdAsArgList(download_item)).c_str()); @@ -978,29 +1015,25 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadItemIdAsArgList(download_item))); EXPECT_EQ(DownloadItem::CANCELLED, download_item->GetState()); - // Calling paused on a non-active download yields kInvalidOperationError. + // Calling paused on a non-active download yields kInvalidId. std::string error = RunFunctionAndReturnError( new DownloadsPauseFunction(), DownloadItemIdAsArgList(download_item)); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kNotInProgress, error.c_str()); - // Calling resume on a non-active download yields kInvalidOperationError + // Calling resume on a non-active download yields kInvalidId error = RunFunctionAndReturnError( new DownloadsResumeFunction(), DownloadItemIdAsArgList(download_item)); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kNotResumable, error.c_str()); - // Calling paused on a non-existent download yields kInvalidOperationError. + // Calling paused on a non-existent download yields kInvalidId. error = RunFunctionAndReturnError( new DownloadsPauseFunction(), "[-42]"); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kInvalidId, error.c_str()); - // Calling resume on a non-existent download yields kInvalidOperationError + // Calling resume on a non-existent download yields kInvalidId error = RunFunctionAndReturnError( new DownloadsResumeFunction(), "[-42]"); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kInvalidId, error.c_str()); int id = download_item->GetId(); scoped_ptr<base::Value> result(RunFunctionAndReturnResult( @@ -1096,16 +1129,16 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, IconLoader::NORMAL, std::string()), args32); - EXPECT_STREQ(download_extension_errors::kIconNotFoundError, error.c_str()); + EXPECT_STREQ(errors::kIconNotFound, error.c_str()); - // Once the download item is deleted, we should return kInvalidOperationError. + // 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<DownloadItem*>(NULL), GetCurrentManager()->GetDownload(id)); error = RunFunctionAndReturnError(new DownloadsGetFileIconFunction(), args32); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kInvalidId, error.c_str()); } @@ -1146,8 +1179,6 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, &result_string)); EXPECT_STREQ("hello", result_string.c_str()); } - - // The temporary files should be cleaned up when the base::ScopedTempDir is removed. } // Test passing the empty query to search(). @@ -1244,7 +1275,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, &items)); scoped_ptr<base::Value> result(RunFunctionAndReturnResult( - new DownloadsSearchFunction(), "[{\"orderBy\": \"filename\"}]")); + new DownloadsSearchFunction(), "[{\"orderBy\": [\"filename\"]}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); @@ -1277,7 +1308,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, &items)); scoped_ptr<base::Value> result(RunFunctionAndReturnResult( - new DownloadsSearchFunction(), "[{\"orderBy\": \"\"}]")); + new DownloadsSearchFunction(), "[{\"orderBy\": []}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; ASSERT_TRUE(result->GetAsList(&result_list)); @@ -1354,15 +1385,15 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchInvalid) { std::string error = RunFunctionAndReturnError( new DownloadsSearchFunction(), "[{\"filenameRegex\": \"(\"}]"); - EXPECT_STREQ(download_extension_errors::kInvalidFilterError, + EXPECT_STREQ(errors::kInvalidFilter, error.c_str()); error = RunFunctionAndReturnError( - new DownloadsSearchFunction(), "[{\"orderBy\": \"goat\"}]"); - EXPECT_STREQ(download_extension_errors::kInvalidOrderByError, + new DownloadsSearchFunction(), "[{\"orderBy\": [\"goat\"]}]"); + EXPECT_STREQ(errors::kInvalidOrderBy, error.c_str()); error = RunFunctionAndReturnError( new DownloadsSearchFunction(), "[{\"limit\": -1}]"); - EXPECT_STREQ(download_extension_errors::kInvalidQueryLimit, + EXPECT_STREQ(errors::kInvalidQueryLimit, error.c_str()); } @@ -1388,7 +1419,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, new DownloadsSearchFunction(), "[{" "\"state\": \"complete\", " "\"danger\": \"content\", " - "\"orderBy\": \"filename\", " + "\"orderBy\": [\"filename\"], " "\"limit\": 1}]")); ASSERT_TRUE(result.get()); base::ListValue* result_list = NULL; @@ -1468,16 +1499,16 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, // 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(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kInvalidId, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), off_item_arg); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kInvalidId, error.c_str()); error = RunFunctionAndReturnError( new DownloadsGetFileIconFunction(), base::StringPrintf("[%d, {}]", off_item->GetId())); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, + EXPECT_STREQ(errors::kInvalidId, error.c_str()); GoOffTheRecord(); @@ -1509,11 +1540,9 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, EXPECT_TRUE(RunFunction(new DownloadsCancelFunction(), on_item_arg)); EXPECT_EQ(DownloadItem::CANCELLED, on_item->GetState()); error = RunFunctionAndReturnError(new DownloadsPauseFunction(), on_item_arg); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kNotInProgress, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), on_item_arg); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + 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)); @@ -1528,14 +1557,11 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, 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(download_extension_errors::kInvalidOperationError, - error.c_str()); + error = RunFunctionAndReturnError(new DownloadsPauseFunction(), off_item_arg); + EXPECT_STREQ(errors::kNotInProgress, error.c_str()); error = RunFunctionAndReturnError(new DownloadsResumeFunction(), off_item_arg); - EXPECT_STREQ(download_extension_errors::kInvalidOperationError, - error.c_str()); + EXPECT_STREQ(errors::kNotResumable, error.c_str()); } // Test that we can start a download and that the correct sequence of events is @@ -1673,7 +1699,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, for (size_t index = 0; index < arraysize(kUnsafeHeaders); ++index) { std::string download_url = test_server()->GetURL("slow?0").spec(); - EXPECT_STREQ(download_extension_errors::kGenericError, + EXPECT_STREQ(errors::kInvalidHeader, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," @@ -1687,8 +1713,6 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, } } -// Test that subdirectories (slashes) are disallowed in filenames. -// TODO(benjhayden) Update this when subdirectories are supported. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Download_Subdirectory) { LoadExtension("downloads_split"); @@ -1697,12 +1721,39 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, - RunFunctionAndReturnError(new DownloadsDownloadFunction(), - base::StringPrintf( - "[{\"url\": \"%s\"," - " \"filename\": \"sub/dir/ect/ory.txt\"}]", - download_url.c_str())).c_str()); + scoped_ptr<base::Value> 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(events::kOnDownloadCreated, + base::StringPrintf("[{\"danger\": \"safe\"," + " \"incognito\": false," + " \"mime\": \"text/plain\"," + " \"paused\": false," + " \"url\": \"%s\"}]", + download_url.c_str()))); + ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, + base::StringPrintf("[{\"id\": %d," + " \"filename\": {" + " \"previous\": \"\"," + " \"current\": \"%s\"}}]", + result_id, + GetFilename("sub/dir/ect/ory.txt").c_str()))); + ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, + base::StringPrintf("[{\"id\": %d," + " \"state\": {" + " \"previous\": \"in_progress\"," + " \"current\": \"complete\"}}]", + result_id))); } // Test that invalid filenames are disallowed. @@ -1714,7 +1765,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, std::string download_url = test_server()->GetURL("slow?0").spec(); GoOnTheRecord(); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, + EXPECT_STREQ(errors::kInvalidFilename, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"," @@ -1732,14 +1783,14 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, "foo bar", "../hello", "/hello", - "google.com/", "http://", "#frag", "foo/bar.html#frag", + "google.com/", }; for (size_t index = 0; index < arraysize(kInvalidURLs); ++index) { - EXPECT_STREQ(download_extension_errors::kInvalidURLError, + EXPECT_STREQ(errors::kInvalidURL, RunFunctionAndReturnError(new DownloadsDownloadFunction(), base::StringPrintf( "[{\"url\": \"%s\"}]", kInvalidURLs[index])).c_str()) @@ -1920,13 +1971,15 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); - ASSERT_TRUE(WaitForInterruption(item, 30, base::StringPrintf( - "[{\"danger\": \"safe\"," - " \"incognito\": false," - " \"mime\": \"text/html\"," - " \"paused\": false," - " \"url\": \"%s\"}]", - download_url.c_str()))); + 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. @@ -2003,14 +2056,16 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); - ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( - "[{\"danger\": \"safe\"," - " \"incognito\": false," - " \"bytesReceived\": 0," - " \"mime\": \"\"," - " \"paused\": false," - " \"url\": \"%s\"}]", - download_url.c_str()))); + 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 @@ -2129,15 +2184,17 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); - ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( - "[{\"danger\": \"safe\"," - " \"incognito\": false," - " \"mime\": \"\"," - " \"paused\": false," - " \"id\": %d," - " \"url\": \"%s\"}]", - result_id, - download_url.c_str()))); + 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 @@ -2168,15 +2225,17 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, ScopedCancellingItem canceller(item); ASSERT_EQ(download_url, item->GetOriginalUrl().spec()); - ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( - "[{\"danger\": \"safe\"," - " \"incognito\": false," - " \"mime\": \"\"," - " \"paused\": false," - " \"id\": %d," - " \"url\": \"%s\"}]", - result_id, - download_url.c_str()))); + 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 @@ -2215,7 +2274,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, item->Cancel(true); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," - " \"error\": {\"current\": 40}," + " \"error\": {\"current\":\"USER_CANCELED\"}," " \"state\": {" " \"previous\": \"in_progress\"," " \"current\": \"interrupted\"}}]", @@ -2324,7 +2383,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, GetExtensionId(), result_id, base::FilePath(), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -2391,7 +2450,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("overridden.swf")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -2399,17 +2458,15 @@ IN_PROC_BROWSER_TEST_F( base::StringPrintf("[{\"id\": %d," " \"danger\": {" " \"previous\":\"safe\"," - " \"current\":\"file\"}," - " \"dangerAccepted\": {" - " \"current\":false}}]", + " \"current\":\"file\"}}]", result_id))); item->ValidateDangerousDownload(); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," - " \"dangerAccepted\": {" - " \"previous\":false," - " \"current\":true}}]", + " \"danger\": {" + " \"previous\":\"file\"," + " \"current\":\"accepted\"}}]", result_id))); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," @@ -2468,9 +2525,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("sneaky/../../sneaky.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," " \"filename\": {" @@ -2533,9 +2590,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("<")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" @@ -2599,9 +2656,9 @@ IN_PROC_BROWSER_TEST_F( result_id, base::FilePath(FILE_PATH_LITERAL( "My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}/foo")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" @@ -2664,9 +2721,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("con.foo")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf( "[{\"id\": %d," " \"filename\": {" @@ -2729,9 +2786,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL(".")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," " \"filename\": {" @@ -2794,9 +2851,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("..")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," " \"filename\": {" @@ -2859,9 +2916,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, downloads_directory().Append(FILE_PATH_LITERAL("sneaky.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," @@ -2925,9 +2982,9 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("foo/")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); - EXPECT_STREQ(download_extension_errors::kInvalidFilenameError, error.c_str()); + EXPECT_STREQ(errors::kInvalidFilename, error.c_str()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," @@ -2990,7 +3047,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -3048,7 +3105,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("foo")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_OVERWRITE, + api::FILENAME_CONFLICT_ACTION_OVERWRITE, &error)); EXPECT_EQ("", error); @@ -3173,7 +3230,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -3232,7 +3289,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("5.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -3307,7 +3364,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -3365,7 +3422,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), result_id, base::FilePath(FILE_PATH_LITERAL("42.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)); EXPECT_EQ("", error); @@ -3471,7 +3528,7 @@ IN_PROC_BROWSER_TEST_F( GetExtensionId(), item->GetId(), base::FilePath(FILE_PATH_LITERAL("42.txt")), - extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY, + api::FILENAME_CONFLICT_ACTION_UNIQUIFY, &error)) << error; EXPECT_EQ("", error); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, @@ -3487,7 +3544,7 @@ IN_PROC_BROWSER_TEST_F( ASSERT_TRUE(interrupted.WaitForEvent()); ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," - " \"error\":{\"current\":20}," + " \"error\":{\"current\":\"NETWORK_FAILED\"}," " \"state\":{" " \"previous\":\"in_progress\"," " \"current\":\"interrupted\"}}]", @@ -3506,7 +3563,7 @@ IN_PROC_BROWSER_TEST_F( ASSERT_TRUE(WaitFor(events::kOnDownloadChanged, base::StringPrintf("[{\"id\": %d," - " \"error\":{\"previous\":20}," + " \"error\":{\"previous\":\"NETWORK_FAILED\"}," " \"state\":{" " \"previous\":\"interrupted\"," " \"current\":\"in_progress\"}}]", @@ -3542,3 +3599,17 @@ class DownloadsApiTest : public ExtensionApiTest { 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 +} diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h index 1723089..d3a6e2e 100644 --- a/chrome/browser/extensions/extension_function_histogram_value.h +++ b/chrome/browser/extensions/extension_function_histogram_value.h @@ -578,6 +578,8 @@ enum HistogramValue { SYSTEM_STORAGE_REMOVEAVAILABLECAPACITYWATCH, SYSTEM_STORAGE_GETALLAVAILABLECAPACITYWATCHES, SYSTEM_STORAGE_REMOVEALLAVAILABLECAPACITYWATCHES, + DOWNLOADS_REMOVEFILE, + DOWNLOADS_SHOWDEFAULTFOLDER, ENUM_BOUNDARY // Last entry: Add new entries above. }; |