Index: chrome/browser/extensions/api/downloads/downloads_api.cc |
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc |
index b831d52d88f5323f54b336e07b2496bdea7c990a..b8a8ad3bb4d8ac15e2f18051e7d31b53f1c53a82 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" |
@@ -29,9 +30,11 @@ |
#include "chrome/browser/browser_process.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" |
@@ -42,8 +45,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/chrome_notification_types.h" |
#include "chrome/common/extensions/api/downloads.h" |
@@ -77,16 +82,18 @@ 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 kInvalidHeaderError[] = "Invalid request header"; |
const char kInvalidOperationError[] = "Invalid operation"; |
const char kInvalidOrderByError[] = "Invalid orderBy field"; |
const char kInvalidQueryLimit[] = "Invalid query limit"; |
const char kInvalidStateError[] = "Invalid state"; |
const char kInvalidURLError[] = "Invalid URL"; |
+const char kNotPermittedURLError[] = "In order to access that URL, this " |
+ "extension must add the host to \"permissions\" in manifest.json"; |
const char kNotImplementedError[] = "NotImplemented"; |
const char kTooManyListenersError[] = "Each extension may have at most one " |
"onDeterminingFilename listener between all of its renderer execution " |
@@ -101,19 +108,20 @@ 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 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"; |
@@ -123,6 +131,7 @@ const char kIncognito[] = "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"; |
@@ -215,15 +224,14 @@ 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())); |
@@ -231,14 +239,19 @@ scoped_ptr<base::DictionaryValue> DownloadItemToJSON( |
json->SetInteger(kTotalBytesKey, download_item->GetTotalBytes()); |
json->SetBoolean(kIncognito, 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); |
@@ -299,7 +312,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; |
@@ -325,7 +337,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; |
@@ -394,6 +405,8 @@ enum DownloadsFunctionName { |
DOWNLOADS_FUNCTION_DRAG = 9, |
DOWNLOADS_FUNCTION_GET_FILE_ICON = 10, |
DOWNLOADS_FUNCTION_OPEN = 11, |
+ DOWNLOADS_FUNCTION_DELETE_FILE = 12, |
+ DOWNLOADS_FUNCTION_SET_SHELF_VISIBLE = 13, |
// Insert new values here, not at the beginning. |
DOWNLOADS_FUNCTION_LAST |
}; |
@@ -405,7 +418,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 = |
@@ -413,8 +428,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; |
@@ -450,13 +463,18 @@ 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; |
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()) { |
@@ -513,6 +531,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) { |
@@ -531,6 +564,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); |
@@ -556,6 +591,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() { |
@@ -603,6 +642,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|. |
@@ -664,15 +725,8 @@ 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 |
@@ -698,6 +752,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_; |
@@ -807,13 +864,15 @@ 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() || |
- (!download_url.SchemeIs("data") && |
- download_url.GetOrigin() != GetExtension()->url().GetOrigin() && |
- !extensions::PermissionsData::HasHostPermission(GetExtension(), |
- download_url))) { |
+ if (!download_url.is_valid()) { |
error_ = download_extension_errors::kInvalidURLError; |
return false; |
+ } else if (!download_url.SchemeIs("data") && |
+ download_url.GetOrigin() != GetExtension()->url().GetOrigin() && |
+ !extensions::PermissionsData::HasHostPermission(GetExtension(), |
+ download_url)) { |
+ error_ = download_extension_errors::kNotPermittedURLError; |
+ return false; |
} |
Profile* current_profile = profile(); |
@@ -827,28 +886,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)) { |
+ if (!net::IsSafePortableRelativePath(creator_suggested_filename)) { |
error_ = download_extension_errors::kInvalidFilenameError; |
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()) |
@@ -862,7 +917,7 @@ bool DownloadsDownloadFunction::RunImpl() { |
++iter) { |
const HeaderNameValuePair& name_value = **iter; |
if (!net::HttpUtil::IsSafeHeader(name_value.name)) { |
- error_ = download_extension_errors::kGenericError; |
+ error_ = download_extension_errors::kInvalidHeaderError; |
return false; |
} |
download_params->add_request_header(name_value.name, name_value.value); |
@@ -876,7 +931,8 @@ 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); |
@@ -888,12 +944,26 @@ bool DownloadsDownloadFunction::RunImpl() { |
} |
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); |
@@ -1035,6 +1105,53 @@ bool DownloadsEraseFunction::RunImpl() { |
return true; |
} |
+DownloadsDeleteFileFunction::DownloadsDeleteFileFunction() {} |
+ |
+DownloadsDeleteFileFunction::~DownloadsDeleteFileFunction() {} |
+ |
+bool DownloadsDeleteFileFunction::RunImpl() { |
+ scoped_ptr<extensions::api::downloads::DeleteFile::Params> params( |
+ extensions::api::downloads::DeleteFile::Params::Create(*args_)); |
+ EXTENSION_FUNCTION_VALIDATE(params.get()); |
+ DownloadItem* download_item = GetDownload( |
+ profile(), include_incognito(), params->download_id); |
+ if (!download_item || |
+ (download_item->GetState() == DownloadItem::IN_PROGRESS) || |
+ download_item->GetFileExternallyRemoved()) { |
+ error_ = download_extension_errors::kInvalidOperationError; |
+ return false; |
+ } |
+ |
+ RecordApiFunctions(DOWNLOADS_FUNCTION_DELETE_FILE); |
+ |
+ DownloadManager* manager = NULL; |
+ DownloadManager* incognito_manager = NULL; |
+ GetManagers(profile(), include_incognito(), &manager, &incognito_manager); |
+ ManagerDestructionObserver::CheckForHistoryFilesRemoval(manager); |
+ ManagerDestructionObserver::CheckForHistoryFilesRemoval(incognito_manager); |
+ |
+ BrowserThread::PostTaskAndReply( |
+ BrowserThread::FILE, FROM_HERE, |
+ base::Bind(&DownloadsDeleteFileFunction::DeleteOnFileThread, this, |
+ download_item->GetFullPath()), |
asanka
2013/07/16 19:57:52
I'd avoid deleting files unless the download is CO
benjhayden
2013/07/19 15:53:55
Done.
|
+ base::Bind(&DownloadsDeleteFileFunction::RespondOnUIThread, this)); |
+ return true; |
+} |
+ |
+void DownloadsDeleteFileFunction::DeleteOnFileThread( |
+ const base::FilePath& path) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ if (file_util::DirectoryExists(path)) { |
+ error_ = download_extension_errors::kInvalidOperationError; |
+ return; |
+ } |
+ base::Delete(path, false); |
+} |
+ |
+void DownloadsDeleteFileFunction::RespondOnUIThread() { |
+ SendResponse(error_.empty()); |
+} |
+ |
DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {} |
DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {} |
@@ -1061,20 +1178,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 = GetDownloadIfInProgress( |
+ profile(), include_incognito(), download_id); |
+ if (!download_item) { |
+ error_ = download_extension_errors::kInvalidOperationError; |
+ return; |
+ } |
+ switch (action) { |
+ case DownloadDangerPrompt::ACCEPT: |
download_item->ValidateDangerousDownload(); |
+ break; |
+ case DownloadDangerPrompt::CANCEL: |
+ download_item->Remove(); |
+ break; |
+ case DownloadDangerPrompt::DISMISS: |
+ break; |
} |
SendResponse(error_.empty()); |
} |
@@ -1087,13 +1213,21 @@ bool DownloadsShowFunction::RunImpl() { |
scoped_ptr<extensions::api::downloads::Show::Params> params( |
extensions::api::downloads::Show::Params::Create(*args_)); |
EXTENSION_FUNCTION_VALIDATE(params.get()); |
- DownloadItem* download_item = GetDownload( |
- profile(), include_incognito(), params->download_id); |
- if (!download_item) { |
- error_ = download_extension_errors::kInvalidOperationError; |
- return false; |
+ if (params->download_id) { |
+ DownloadItem* download_item = GetDownload( |
+ profile(), include_incognito(), *params->download_id); |
+ if (!download_item) { |
+ error_ = download_extension_errors::kInvalidOperationError; |
+ return false; |
+ } |
+ download_item->ShowDownloadInShell(); |
+ } else { |
+ DownloadManager* manager = NULL; |
+ DownloadManager* incognito_manager = NULL; |
+ GetManagers(profile(), include_incognito(), &manager, &incognito_manager); |
+ platform_util::OpenItem(DownloadPrefs::FromDownloadManager( |
+ manager)->DownloadPath()); |
} |
- download_item->ShowDownloadInShell(); |
RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW); |
return true; |
} |
@@ -1146,6 +1280,26 @@ bool DownloadsDragFunction::RunImpl() { |
return true; |
} |
+DownloadsSetShelfVisibleFunction::DownloadsSetShelfVisibleFunction() {} |
+ |
+DownloadsSetShelfVisibleFunction::~DownloadsSetShelfVisibleFunction() {} |
+ |
+bool DownloadsSetShelfVisibleFunction::RunImpl() { |
+ scoped_ptr<extensions::api::downloads::SetShelfVisible::Params> params( |
+ extensions::api::downloads::SetShelfVisible::Params::Create(*args_)); |
+ EXTENSION_FUNCTION_VALIDATE(params.get()); |
+ DownloadShelf* shelf = GetCurrentBrowser()->window()->GetDownloadShelf(); |
asanka
2013/07/16 19:57:52
Is it expected that this will operate on the forem
benjhayden
2013/07/19 15:53:55
Yep, it will probably primarily be called from bac
asanka
2013/07/23 21:13:48
The browser window for a download is determined by
asargent_no_longer_on_chrome
2013/07/23 21:36:57
Note that chrome can be running but with no curren
|
+ if (params->visible) { |
+ if (!shelf->IsShowing()) |
+ shelf->Show(); |
asanka
2013/07/16 19:57:52
What's supposed to happen if there are no download
benjhayden
2013/07/19 15:53:55
Done.
|
+ } else { |
+ if (shelf->IsShowing()) |
+ shelf->Close(DownloadShelf::USER_ACTION); |
+ } |
+ RecordApiFunctions(DOWNLOADS_FUNCTION_SET_SHELF_VISIBLE); |
+ return true; |
+} |
+ |
DownloadsGetFileIconFunction::DownloadsGetFileIconFunction() |
: icon_extractor_(new DownloadFileIconExtractorImpl()) { |
} |
@@ -1268,7 +1422,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(); |
+ } |
} |
} |
@@ -1358,7 +1519,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); |
} |
} |
@@ -1428,7 +1590,9 @@ 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 ((iter.key() != kBytesReceivedKey) && |
+ (iter.key() != kIdKey) && |
+ (iter.key() != kEstimatedEndTimeKey)) { |
const base::Value* old_value = NULL; |
if (!data->json().HasKey(iter.key()) || |
(data->json().Get(iter.key(), &old_value) && |
@@ -1445,7 +1609,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()) && |
+ (iter.key() != kEstimatedEndTimeKey)) { |
asanka
2013/07/16 19:57:52
Why exclude kEstimatedEndTimeKey? It can also disa
benjhayden
2013/07/19 15:53:55
estimatedEndTime is not in DownloadDelta because i
|
+ // estimatedEndTime disappears after completion, but bytesReceived stays. |
delta->Set(iter.key() + ".previous", iter.value().DeepCopy()); |
changed = true; |
} |