summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authorbenjhayden@chromium.org <benjhayden@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-28 18:36:09 +0000
committerbenjhayden@chromium.org <benjhayden@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-28 18:36:09 +0000
commitf1d784d6938b8fe8e0d257e41b26341992c2552c (patch)
treee8a57be7f78ffa26e886c5814d989d0a8da3893d /chrome/browser/extensions
parent3c919d6ac5b262904d6598fc2418d7c0358aad57 (diff)
downloadchromium_src-f1d784d6938b8fe8e0d257e41b26341992c2552c.zip
chromium_src-f1d784d6938b8fe8e0d257e41b26341992c2552c.tar.gz
chromium_src-f1d784d6938b8fe8e0d257e41b26341992c2552c.tar.bz2
A few minor changes to the chrome.downloads extension API
Reviewers: jhawkins: webui Done: asanka: */download/* jam: content/public/* asargent: */extensions/* Replaced the "Invalid operation" error message with more specific and helpful messages Add DownloadItem.referrer Add DownloadItem.estimatedEndTime Add DownloadItem.canResume Change DownloadQuery.limit to default to 1000 Change 'conflict_action' to 'conflictAction'. Change DownloadQuery.orderBy and DownloadQuery.query to arrays of strings Remove DownloadItem.dangerAccepted in favor of DownloadItem.danger == 'accepted' Add showDefaultFolder(). Disallow access to packaged_apps. download() now updates the Download.Sources histogram. http://crbug.com/240322 Document using startedAfter and limit to page through search() results. DownloadItem.error is now a string enum instead of mysterious numbers. Add downloads.removeFile(id) and DownloadItem::DeleteFile() so that the file may be deleted separately from the history entry. Calling open() for dangerous downloads has always been guaranteed to return kInvalidOperation because unaccepted dangerous downloads cannot transition to state='complete', and open() returns kInvalidOperation for incomplete items. We can wait until after launch to figure out whether there needs to be some mechanism to allow extensions to override the request header blacklist. This seems like an edge case for which we may never receive feature requests. Staged docs preview: http://basho.cam.corp.google.com:8000/extensions/downloads.html#type-DownloadItem BUG=240322 R=asanka@chromium.org, asargent@chromium.org, jam@chromium.org Review URL: https://codereview.chromium.org/16924017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@214133 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/api/downloads/downloads_api.cc489
-rw-r--r--chrome/browser/extensions/api/downloads/downloads_api.h74
-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.h2
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.
};