Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / downloads / downloads_api.cc
blob80e02ef16a5f734a5b8064a30434c7c1fbcf214a
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
7 #include <set>
8 #include <string>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/callback.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/json/json_writer.h"
17 #include "base/lazy_instance.h"
18 #include "base/location.h"
19 #include "base/logging.h"
20 #include "base/memory/weak_ptr.h"
21 #include "base/metrics/histogram.h"
22 #include "base/single_thread_task_runner.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_split.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/task/cancelable_task_tracker.h"
29 #include "base/thread_task_runner_handle.h"
30 #include "base/values.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/download/download_danger_prompt.h"
33 #include "chrome/browser/download/download_file_icon_extractor.h"
34 #include "chrome/browser/download/download_prefs.h"
35 #include "chrome/browser/download/download_query.h"
36 #include "chrome/browser/download/download_service.h"
37 #include "chrome/browser/download/download_service_factory.h"
38 #include "chrome/browser/download/download_shelf.h"
39 #include "chrome/browser/download/download_stats.h"
40 #include "chrome/browser/download/drag_download_item.h"
41 #include "chrome/browser/icon_loader.h"
42 #include "chrome/browser/icon_manager.h"
43 #include "chrome/browser/platform_util.h"
44 #include "chrome/browser/profiles/profile.h"
45 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
46 #include "chrome/browser/ui/browser.h"
47 #include "chrome/browser/ui/browser_list.h"
48 #include "chrome/browser/ui/browser_window.h"
49 #include "chrome/common/extensions/api/downloads.h"
50 #include "components/web_modal/web_contents_modal_dialog_manager.h"
51 #include "content/public/browser/download_interrupt_reasons.h"
52 #include "content/public/browser/download_item.h"
53 #include "content/public/browser/download_save_info.h"
54 #include "content/public/browser/download_url_parameters.h"
55 #include "content/public/browser/notification_details.h"
56 #include "content/public/browser/notification_service.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/render_frame_host.h"
59 #include "content/public/browser/render_process_host.h"
60 #include "content/public/browser/render_view_host.h"
61 #include "content/public/browser/render_widget_host_view.h"
62 #include "content/public/browser/resource_context.h"
63 #include "content/public/browser/resource_dispatcher_host.h"
64 #include "content/public/browser/web_contents.h"
65 #include "extensions/browser/event_router.h"
66 #include "extensions/browser/extension_function_dispatcher.h"
67 #include "extensions/browser/extension_prefs.h"
68 #include "extensions/browser/extension_registry.h"
69 #include "extensions/browser/notification_types.h"
70 #include "extensions/browser/warning_service.h"
71 #include "extensions/common/permissions/permissions_data.h"
72 #include "net/base/filename_util.h"
73 #include "net/base/load_flags.h"
74 #include "net/http/http_util.h"
75 #include "third_party/skia/include/core/SkBitmap.h"
76 #include "ui/base/webui/web_ui_util.h"
77 #include "ui/gfx/image/image_skia.h"
79 using content::BrowserContext;
80 using content::BrowserThread;
81 using content::DownloadItem;
82 using content::DownloadManager;
84 namespace download_extension_errors {
86 const char kEmptyFile[] = "Filename not yet determined";
87 const char kFileAlreadyDeleted[] = "Download file already deleted";
88 const char kFileNotRemoved[] = "Unable to remove file";
89 const char kIconNotFound[] = "Icon not found";
90 const char kInvalidDangerType[] = "Invalid danger type";
91 const char kInvalidFilename[] = "Invalid filename";
92 const char kInvalidFilter[] = "Invalid query filter";
93 const char kInvalidHeaderName[] = "Invalid request header name";
94 const char kInvalidHeaderUnsafe[] = "Unsafe request header name";
95 const char kInvalidHeaderValue[] = "Invalid request header value";
96 const char kInvalidId[] = "Invalid downloadId";
97 const char kInvalidOrderBy[] = "Invalid orderBy field";
98 const char kInvalidQueryLimit[] = "Invalid query limit";
99 const char kInvalidState[] = "Invalid state";
100 const char kInvalidURL[] = "Invalid URL";
101 const char kInvisibleContext[] = "Javascript execution context is not visible "
102 "(tab, window, popup bubble)";
103 const char kNotComplete[] = "Download must be complete";
104 const char kNotDangerous[] = "Download must be dangerous";
105 const char kNotInProgress[] = "Download must be in progress";
106 const char kNotResumable[] = "DownloadItem.canResume must be true";
107 const char kOpenPermission[] = "The \"downloads.open\" permission is required";
108 const char kShelfDisabled[] = "Another extension has disabled the shelf";
109 const char kShelfPermission[] = "downloads.setShelfEnabled requires the "
110 "\"downloads.shelf\" permission";
111 const char kTooManyListeners[] = "Each extension may have at most one "
112 "onDeterminingFilename listener between all of its renderer execution "
113 "contexts.";
114 const char kUnexpectedDeterminer[] = "Unexpected determineFilename call";
115 const char kUserGesture[] = "User gesture required";
117 } // namespace download_extension_errors
119 namespace errors = download_extension_errors;
121 namespace extensions {
123 namespace {
125 namespace downloads = api::downloads;
127 // Default icon size for getFileIcon() in pixels.
128 const int kDefaultIconSize = 32;
130 // Parameter keys
131 const char kByExtensionIdKey[] = "byExtensionId";
132 const char kByExtensionNameKey[] = "byExtensionName";
133 const char kBytesReceivedKey[] = "bytesReceived";
134 const char kCanResumeKey[] = "canResume";
135 const char kDangerAccepted[] = "accepted";
136 const char kDangerContent[] = "content";
137 const char kDangerFile[] = "file";
138 const char kDangerHost[] = "host";
139 const char kDangerKey[] = "danger";
140 const char kDangerSafe[] = "safe";
141 const char kDangerUncommon[] = "uncommon";
142 const char kDangerUnwanted[] = "unwanted";
143 const char kDangerUrl[] = "url";
144 const char kEndTimeKey[] = "endTime";
145 const char kEndedAfterKey[] = "endedAfter";
146 const char kEndedBeforeKey[] = "endedBefore";
147 const char kErrorKey[] = "error";
148 const char kEstimatedEndTimeKey[] = "estimatedEndTime";
149 const char kExistsKey[] = "exists";
150 const char kFileSizeKey[] = "fileSize";
151 const char kFilenameKey[] = "filename";
152 const char kFilenameRegexKey[] = "filenameRegex";
153 const char kIdKey[] = "id";
154 const char kIncognitoKey[] = "incognito";
155 const char kMimeKey[] = "mime";
156 const char kPausedKey[] = "paused";
157 const char kQueryKey[] = "query";
158 const char kReferrerUrlKey[] = "referrer";
159 const char kStartTimeKey[] = "startTime";
160 const char kStartedAfterKey[] = "startedAfter";
161 const char kStartedBeforeKey[] = "startedBefore";
162 const char kStateComplete[] = "complete";
163 const char kStateInProgress[] = "in_progress";
164 const char kStateInterrupted[] = "interrupted";
165 const char kStateKey[] = "state";
166 const char kTotalBytesGreaterKey[] = "totalBytesGreater";
167 const char kTotalBytesKey[] = "totalBytes";
168 const char kTotalBytesLessKey[] = "totalBytesLess";
169 const char kUrlKey[] = "url";
170 const char kUrlRegexKey[] = "urlRegex";
172 // Note: Any change to the danger type strings, should be accompanied by a
173 // corresponding change to downloads.json.
174 const char* const kDangerStrings[] = {
175 kDangerSafe,
176 kDangerFile,
177 kDangerUrl,
178 kDangerContent,
179 kDangerSafe,
180 kDangerUncommon,
181 kDangerAccepted,
182 kDangerHost,
183 kDangerUnwanted
185 static_assert(arraysize(kDangerStrings) == content::DOWNLOAD_DANGER_TYPE_MAX,
186 "kDangerStrings should have DOWNLOAD_DANGER_TYPE_MAX elements");
188 // Note: Any change to the state strings, should be accompanied by a
189 // corresponding change to downloads.json.
190 const char* const kStateStrings[] = {
191 kStateInProgress,
192 kStateComplete,
193 kStateInterrupted,
194 kStateInterrupted,
196 static_assert(arraysize(kStateStrings) == DownloadItem::MAX_DOWNLOAD_STATE,
197 "kStateStrings should have MAX_DOWNLOAD_STATE elements");
199 const char* DangerString(content::DownloadDangerType danger) {
200 DCHECK(danger >= 0);
201 DCHECK(danger < static_cast<content::DownloadDangerType>(
202 arraysize(kDangerStrings)));
203 if (danger < 0 || danger >= static_cast<content::DownloadDangerType>(
204 arraysize(kDangerStrings)))
205 return "";
206 return kDangerStrings[danger];
209 content::DownloadDangerType DangerEnumFromString(const std::string& danger) {
210 for (size_t i = 0; i < arraysize(kDangerStrings); ++i) {
211 if (danger == kDangerStrings[i])
212 return static_cast<content::DownloadDangerType>(i);
214 return content::DOWNLOAD_DANGER_TYPE_MAX;
217 const char* StateString(DownloadItem::DownloadState state) {
218 DCHECK(state >= 0);
219 DCHECK(state < static_cast<DownloadItem::DownloadState>(
220 arraysize(kStateStrings)));
221 if (state < 0 || state >= static_cast<DownloadItem::DownloadState>(
222 arraysize(kStateStrings)))
223 return "";
224 return kStateStrings[state];
227 DownloadItem::DownloadState StateEnumFromString(const std::string& state) {
228 for (size_t i = 0; i < arraysize(kStateStrings); ++i) {
229 if ((kStateStrings[i] != NULL) && (state == kStateStrings[i]))
230 return static_cast<DownloadItem::DownloadState>(i);
232 return DownloadItem::MAX_DOWNLOAD_STATE;
235 std::string TimeToISO8601(const base::Time& t) {
236 base::Time::Exploded exploded;
237 t.UTCExplode(&exploded);
238 return base::StringPrintf(
239 "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month,
240 exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
241 exploded.millisecond);
244 scoped_ptr<base::DictionaryValue> DownloadItemToJSON(
245 DownloadItem* download_item,
246 Profile* profile) {
247 base::DictionaryValue* json = new base::DictionaryValue();
248 json->SetBoolean(kExistsKey, !download_item->GetFileExternallyRemoved());
249 json->SetInteger(kIdKey, download_item->GetId());
250 const GURL& url = download_item->GetOriginalUrl();
251 json->SetString(kUrlKey, (url.is_valid() ? url.spec() : std::string()));
252 const GURL& referrer = download_item->GetReferrerUrl();
253 json->SetString(kReferrerUrlKey, (referrer.is_valid() ? referrer.spec()
254 : std::string()));
255 json->SetString(kFilenameKey,
256 download_item->GetTargetFilePath().LossyDisplayName());
257 json->SetString(kDangerKey, DangerString(download_item->GetDangerType()));
258 json->SetString(kStateKey, StateString(download_item->GetState()));
259 json->SetBoolean(kCanResumeKey, download_item->CanResume());
260 json->SetBoolean(kPausedKey, download_item->IsPaused());
261 json->SetString(kMimeKey, download_item->GetMimeType());
262 json->SetString(kStartTimeKey, TimeToISO8601(download_item->GetStartTime()));
263 json->SetDouble(kBytesReceivedKey, download_item->GetReceivedBytes());
264 json->SetDouble(kTotalBytesKey, download_item->GetTotalBytes());
265 json->SetBoolean(kIncognitoKey, profile->IsOffTheRecord());
266 if (download_item->GetState() == DownloadItem::INTERRUPTED) {
267 json->SetString(kErrorKey,
268 content::DownloadInterruptReasonToString(
269 download_item->GetLastReason()));
270 } else if (download_item->GetState() == DownloadItem::CANCELLED) {
271 json->SetString(kErrorKey,
272 content::DownloadInterruptReasonToString(
273 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED));
275 if (!download_item->GetEndTime().is_null())
276 json->SetString(kEndTimeKey, TimeToISO8601(download_item->GetEndTime()));
277 base::TimeDelta time_remaining;
278 if (download_item->TimeRemaining(&time_remaining)) {
279 base::Time now = base::Time::Now();
280 json->SetString(kEstimatedEndTimeKey, TimeToISO8601(now + time_remaining));
282 DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
283 if (by_ext) {
284 json->SetString(kByExtensionIdKey, by_ext->id());
285 json->SetString(kByExtensionNameKey, by_ext->name());
286 // Lookup the extension's current name() in case the user changed their
287 // language. This won't work if the extension was uninstalled, so the name
288 // might be the wrong language.
289 const Extension* extension =
290 ExtensionRegistry::Get(profile)
291 ->GetExtensionById(by_ext->id(), ExtensionRegistry::EVERYTHING);
292 if (extension)
293 json->SetString(kByExtensionNameKey, extension->name());
295 // TODO(benjhayden): Implement fileSize.
296 json->SetDouble(kFileSizeKey, download_item->GetTotalBytes());
297 return scoped_ptr<base::DictionaryValue>(json);
300 class DownloadFileIconExtractorImpl : public DownloadFileIconExtractor {
301 public:
302 DownloadFileIconExtractorImpl() {}
304 ~DownloadFileIconExtractorImpl() override {}
306 bool ExtractIconURLForPath(const base::FilePath& path,
307 float scale,
308 IconLoader::IconSize icon_size,
309 IconURLCallback callback) override;
311 private:
312 void OnIconLoadComplete(
313 float scale, const IconURLCallback& callback, gfx::Image* icon);
315 base::CancelableTaskTracker cancelable_task_tracker_;
318 bool DownloadFileIconExtractorImpl::ExtractIconURLForPath(
319 const base::FilePath& path,
320 float scale,
321 IconLoader::IconSize icon_size,
322 IconURLCallback callback) {
323 IconManager* im = g_browser_process->icon_manager();
324 // The contents of the file at |path| may have changed since a previous
325 // request, in which case the associated icon may also have changed.
326 // Therefore, always call LoadIcon instead of attempting a LookupIcon.
327 im->LoadIcon(path,
328 icon_size,
329 base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete,
330 base::Unretained(this), scale, callback),
331 &cancelable_task_tracker_);
332 return true;
335 void DownloadFileIconExtractorImpl::OnIconLoadComplete(
336 float scale, const IconURLCallback& callback, gfx::Image* icon) {
337 DCHECK_CURRENTLY_ON(BrowserThread::UI);
338 callback.Run(!icon ? std::string() : webui::GetBitmapDataUrl(
339 icon->ToImageSkia()->GetRepresentation(scale).sk_bitmap()));
342 IconLoader::IconSize IconLoaderSizeFromPixelSize(int pixel_size) {
343 switch (pixel_size) {
344 case 16: return IconLoader::SMALL;
345 case 32: return IconLoader::NORMAL;
346 default:
347 NOTREACHED();
348 return IconLoader::NORMAL;
352 typedef base::hash_map<std::string, DownloadQuery::FilterType> FilterTypeMap;
354 void InitFilterTypeMap(FilterTypeMap* filter_types_ptr) {
355 FilterTypeMap& filter_types = *filter_types_ptr;
356 filter_types[kBytesReceivedKey] = DownloadQuery::FILTER_BYTES_RECEIVED;
357 filter_types[kExistsKey] = DownloadQuery::FILTER_EXISTS;
358 filter_types[kFilenameKey] = DownloadQuery::FILTER_FILENAME;
359 filter_types[kFilenameRegexKey] = DownloadQuery::FILTER_FILENAME_REGEX;
360 filter_types[kMimeKey] = DownloadQuery::FILTER_MIME;
361 filter_types[kPausedKey] = DownloadQuery::FILTER_PAUSED;
362 filter_types[kQueryKey] = DownloadQuery::FILTER_QUERY;
363 filter_types[kEndedAfterKey] = DownloadQuery::FILTER_ENDED_AFTER;
364 filter_types[kEndedBeforeKey] = DownloadQuery::FILTER_ENDED_BEFORE;
365 filter_types[kEndTimeKey] = DownloadQuery::FILTER_END_TIME;
366 filter_types[kStartedAfterKey] = DownloadQuery::FILTER_STARTED_AFTER;
367 filter_types[kStartedBeforeKey] = DownloadQuery::FILTER_STARTED_BEFORE;
368 filter_types[kStartTimeKey] = DownloadQuery::FILTER_START_TIME;
369 filter_types[kTotalBytesKey] = DownloadQuery::FILTER_TOTAL_BYTES;
370 filter_types[kTotalBytesGreaterKey] =
371 DownloadQuery::FILTER_TOTAL_BYTES_GREATER;
372 filter_types[kTotalBytesLessKey] = DownloadQuery::FILTER_TOTAL_BYTES_LESS;
373 filter_types[kUrlKey] = DownloadQuery::FILTER_URL;
374 filter_types[kUrlRegexKey] = DownloadQuery::FILTER_URL_REGEX;
377 typedef base::hash_map<std::string, DownloadQuery::SortType> SortTypeMap;
379 void InitSortTypeMap(SortTypeMap* sorter_types_ptr) {
380 SortTypeMap& sorter_types = *sorter_types_ptr;
381 sorter_types[kBytesReceivedKey] = DownloadQuery::SORT_BYTES_RECEIVED;
382 sorter_types[kDangerKey] = DownloadQuery::SORT_DANGER;
383 sorter_types[kEndTimeKey] = DownloadQuery::SORT_END_TIME;
384 sorter_types[kExistsKey] = DownloadQuery::SORT_EXISTS;
385 sorter_types[kFilenameKey] = DownloadQuery::SORT_FILENAME;
386 sorter_types[kMimeKey] = DownloadQuery::SORT_MIME;
387 sorter_types[kPausedKey] = DownloadQuery::SORT_PAUSED;
388 sorter_types[kStartTimeKey] = DownloadQuery::SORT_START_TIME;
389 sorter_types[kStateKey] = DownloadQuery::SORT_STATE;
390 sorter_types[kTotalBytesKey] = DownloadQuery::SORT_TOTAL_BYTES;
391 sorter_types[kUrlKey] = DownloadQuery::SORT_URL;
394 bool IsNotTemporaryDownloadFilter(const DownloadItem& download_item) {
395 return !download_item.IsTemporary();
398 // Set |manager| to the on-record DownloadManager, and |incognito_manager| to
399 // the off-record DownloadManager if one exists and is requested via
400 // |include_incognito|. This should work regardless of whether |profile| is
401 // original or incognito.
402 void GetManagers(
403 Profile* profile,
404 bool include_incognito,
405 DownloadManager** manager,
406 DownloadManager** incognito_manager) {
407 *manager = BrowserContext::GetDownloadManager(profile->GetOriginalProfile());
408 if (profile->HasOffTheRecordProfile() &&
409 (include_incognito ||
410 profile->IsOffTheRecord())) {
411 *incognito_manager = BrowserContext::GetDownloadManager(
412 profile->GetOffTheRecordProfile());
413 } else {
414 *incognito_manager = NULL;
418 DownloadItem* GetDownload(Profile* profile, bool include_incognito, int id) {
419 DownloadManager* manager = NULL;
420 DownloadManager* incognito_manager = NULL;
421 GetManagers(profile, include_incognito, &manager, &incognito_manager);
422 DownloadItem* download_item = manager->GetDownload(id);
423 if (!download_item && incognito_manager)
424 download_item = incognito_manager->GetDownload(id);
425 return download_item;
428 enum DownloadsFunctionName {
429 DOWNLOADS_FUNCTION_DOWNLOAD = 0,
430 DOWNLOADS_FUNCTION_SEARCH = 1,
431 DOWNLOADS_FUNCTION_PAUSE = 2,
432 DOWNLOADS_FUNCTION_RESUME = 3,
433 DOWNLOADS_FUNCTION_CANCEL = 4,
434 DOWNLOADS_FUNCTION_ERASE = 5,
435 // 6 unused
436 DOWNLOADS_FUNCTION_ACCEPT_DANGER = 7,
437 DOWNLOADS_FUNCTION_SHOW = 8,
438 DOWNLOADS_FUNCTION_DRAG = 9,
439 DOWNLOADS_FUNCTION_GET_FILE_ICON = 10,
440 DOWNLOADS_FUNCTION_OPEN = 11,
441 DOWNLOADS_FUNCTION_REMOVE_FILE = 12,
442 DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER = 13,
443 DOWNLOADS_FUNCTION_SET_SHELF_ENABLED = 14,
444 DOWNLOADS_FUNCTION_DETERMINE_FILENAME = 15,
445 // Insert new values here, not at the beginning.
446 DOWNLOADS_FUNCTION_LAST
449 void RecordApiFunctions(DownloadsFunctionName function) {
450 UMA_HISTOGRAM_ENUMERATION("Download.ApiFunctions",
451 function,
452 DOWNLOADS_FUNCTION_LAST);
455 void CompileDownloadQueryOrderBy(
456 const std::vector<std::string>& order_by_strs,
457 std::string* error,
458 DownloadQuery* query) {
459 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
460 // comparisons.
461 static base::LazyInstance<SortTypeMap> sorter_types =
462 LAZY_INSTANCE_INITIALIZER;
463 if (sorter_types.Get().empty())
464 InitSortTypeMap(sorter_types.Pointer());
466 for (std::vector<std::string>::const_iterator iter = order_by_strs.begin();
467 iter != order_by_strs.end(); ++iter) {
468 std::string term_str = *iter;
469 if (term_str.empty())
470 continue;
471 DownloadQuery::SortDirection direction = DownloadQuery::ASCENDING;
472 if (term_str[0] == '-') {
473 direction = DownloadQuery::DESCENDING;
474 term_str = term_str.substr(1);
476 SortTypeMap::const_iterator sorter_type =
477 sorter_types.Get().find(term_str);
478 if (sorter_type == sorter_types.Get().end()) {
479 *error = errors::kInvalidOrderBy;
480 return;
482 query->AddSorter(sorter_type->second, direction);
486 void RunDownloadQuery(
487 const downloads::DownloadQuery& query_in,
488 DownloadManager* manager,
489 DownloadManager* incognito_manager,
490 std::string* error,
491 DownloadQuery::DownloadVector* results) {
492 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
493 // comparisons.
494 static base::LazyInstance<FilterTypeMap> filter_types =
495 LAZY_INSTANCE_INITIALIZER;
496 if (filter_types.Get().empty())
497 InitFilterTypeMap(filter_types.Pointer());
499 DownloadQuery query_out;
501 size_t limit = 1000;
502 if (query_in.limit.get()) {
503 if (*query_in.limit.get() < 0) {
504 *error = errors::kInvalidQueryLimit;
505 return;
507 limit = *query_in.limit.get();
509 if (limit > 0) {
510 query_out.Limit(limit);
513 std::string state_string = downloads::ToString(query_in.state);
514 if (!state_string.empty()) {
515 DownloadItem::DownloadState state = StateEnumFromString(state_string);
516 if (state == DownloadItem::MAX_DOWNLOAD_STATE) {
517 *error = errors::kInvalidState;
518 return;
520 query_out.AddFilter(state);
522 std::string danger_string =
523 downloads::ToString(query_in.danger);
524 if (!danger_string.empty()) {
525 content::DownloadDangerType danger_type = DangerEnumFromString(
526 danger_string);
527 if (danger_type == content::DOWNLOAD_DANGER_TYPE_MAX) {
528 *error = errors::kInvalidDangerType;
529 return;
531 query_out.AddFilter(danger_type);
533 if (query_in.order_by.get()) {
534 CompileDownloadQueryOrderBy(*query_in.order_by.get(), error, &query_out);
535 if (!error->empty())
536 return;
539 scoped_ptr<base::DictionaryValue> query_in_value(query_in.ToValue().Pass());
540 for (base::DictionaryValue::Iterator query_json_field(*query_in_value.get());
541 !query_json_field.IsAtEnd(); query_json_field.Advance()) {
542 FilterTypeMap::const_iterator filter_type =
543 filter_types.Get().find(query_json_field.key());
544 if (filter_type != filter_types.Get().end()) {
545 if (!query_out.AddFilter(filter_type->second, query_json_field.value())) {
546 *error = errors::kInvalidFilter;
547 return;
552 DownloadQuery::DownloadVector all_items;
553 if (query_in.id.get()) {
554 DownloadItem* download_item = manager->GetDownload(*query_in.id.get());
555 if (!download_item && incognito_manager)
556 download_item = incognito_manager->GetDownload(*query_in.id.get());
557 if (download_item)
558 all_items.push_back(download_item);
559 } else {
560 manager->GetAllDownloads(&all_items);
561 if (incognito_manager)
562 incognito_manager->GetAllDownloads(&all_items);
564 query_out.AddFilter(base::Bind(&IsNotTemporaryDownloadFilter));
565 query_out.Search(all_items.begin(), all_items.end(), results);
568 DownloadPathReservationTracker::FilenameConflictAction ConvertConflictAction(
569 downloads::FilenameConflictAction action) {
570 switch (action) {
571 case downloads::FILENAME_CONFLICT_ACTION_NONE:
572 case downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY:
573 return DownloadPathReservationTracker::UNIQUIFY;
574 case downloads::FILENAME_CONFLICT_ACTION_OVERWRITE:
575 return DownloadPathReservationTracker::OVERWRITE;
576 case downloads::FILENAME_CONFLICT_ACTION_PROMPT:
577 return DownloadPathReservationTracker::PROMPT;
579 NOTREACHED();
580 return DownloadPathReservationTracker::UNIQUIFY;
583 class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data {
584 public:
585 static ExtensionDownloadsEventRouterData* Get(DownloadItem* download_item) {
586 base::SupportsUserData::Data* data = download_item->GetUserData(kKey);
587 return (data == NULL) ? NULL :
588 static_cast<ExtensionDownloadsEventRouterData*>(data);
591 static void Remove(DownloadItem* download_item) {
592 download_item->RemoveUserData(kKey);
595 explicit ExtensionDownloadsEventRouterData(
596 DownloadItem* download_item,
597 scoped_ptr<base::DictionaryValue> json_item)
598 : updated_(0),
599 changed_fired_(0),
600 json_(json_item.Pass()),
601 creator_conflict_action_(
602 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY),
603 determined_conflict_action_(
604 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY) {
605 DCHECK_CURRENTLY_ON(BrowserThread::UI);
606 download_item->SetUserData(kKey, this);
609 ~ExtensionDownloadsEventRouterData() override {
610 if (updated_ > 0) {
611 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged",
612 (changed_fired_ * 100 / updated_));
616 const base::DictionaryValue& json() const { return *json_.get(); }
617 void set_json(scoped_ptr<base::DictionaryValue> json_item) {
618 json_ = json_item.Pass();
621 void OnItemUpdated() { ++updated_; }
622 void OnChangedFired() { ++changed_fired_; }
624 static void SetDetermineFilenameTimeoutSecondsForTesting(int s) {
625 determine_filename_timeout_s_ = s;
628 void BeginFilenameDetermination(
629 const base::Closure& no_change,
630 const ExtensionDownloadsEventRouter::FilenameChangedCallback& change) {
631 DCHECK_CURRENTLY_ON(BrowserThread::UI);
632 ClearPendingDeterminers();
633 filename_no_change_ = no_change;
634 filename_change_ = change;
635 determined_filename_ = creator_suggested_filename_;
636 determined_conflict_action_ = creator_conflict_action_;
637 // determiner_.install_time should default to 0 so that creator suggestions
638 // should be lower priority than any actual onDeterminingFilename listeners.
640 // Ensure that the callback is called within a time limit.
641 weak_ptr_factory_.reset(
642 new base::WeakPtrFactory<ExtensionDownloadsEventRouterData>(this));
643 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
644 FROM_HERE,
645 base::Bind(&ExtensionDownloadsEventRouterData::DetermineFilenameTimeout,
646 weak_ptr_factory_->GetWeakPtr()),
647 base::TimeDelta::FromSeconds(determine_filename_timeout_s_));
650 void DetermineFilenameTimeout() {
651 CallFilenameCallback();
654 void ClearPendingDeterminers() {
655 DCHECK_CURRENTLY_ON(BrowserThread::UI);
656 determined_filename_.clear();
657 determined_conflict_action_ =
658 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY;
659 determiner_ = DeterminerInfo();
660 filename_no_change_ = base::Closure();
661 filename_change_ = ExtensionDownloadsEventRouter::FilenameChangedCallback();
662 weak_ptr_factory_.reset();
663 determiners_.clear();
666 void DeterminerRemoved(const std::string& extension_id) {
667 DCHECK_CURRENTLY_ON(BrowserThread::UI);
668 for (DeterminerInfoVector::iterator iter = determiners_.begin();
669 iter != determiners_.end();) {
670 if (iter->extension_id == extension_id) {
671 iter = determiners_.erase(iter);
672 } else {
673 ++iter;
676 // If we just removed the last unreported determiner, then we need to call a
677 // callback.
678 CheckAllDeterminersCalled();
681 void AddPendingDeterminer(const std::string& extension_id,
682 const base::Time& installed) {
683 DCHECK_CURRENTLY_ON(BrowserThread::UI);
684 for (size_t index = 0; index < determiners_.size(); ++index) {
685 if (determiners_[index].extension_id == extension_id) {
686 DCHECK(false) << extension_id;
687 return;
690 determiners_.push_back(DeterminerInfo(extension_id, installed));
693 bool DeterminerAlreadyReported(const std::string& extension_id) {
694 DCHECK_CURRENTLY_ON(BrowserThread::UI);
695 for (size_t index = 0; index < determiners_.size(); ++index) {
696 if (determiners_[index].extension_id == extension_id) {
697 return determiners_[index].reported;
700 return false;
703 void CreatorSuggestedFilename(
704 const base::FilePath& filename,
705 downloads::FilenameConflictAction conflict_action) {
706 DCHECK_CURRENTLY_ON(BrowserThread::UI);
707 creator_suggested_filename_ = filename;
708 creator_conflict_action_ = conflict_action;
711 base::FilePath creator_suggested_filename() const {
712 return creator_suggested_filename_;
715 downloads::FilenameConflictAction
716 creator_conflict_action() const {
717 return creator_conflict_action_;
720 void ResetCreatorSuggestion() {
721 DCHECK_CURRENTLY_ON(BrowserThread::UI);
722 creator_suggested_filename_.clear();
723 creator_conflict_action_ =
724 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY;
727 // Returns false if this |extension_id| was not expected or if this
728 // |extension_id| has already reported. The caller is responsible for
729 // validating |filename|.
730 bool DeterminerCallback(
731 Profile* profile,
732 const std::string& extension_id,
733 const base::FilePath& filename,
734 downloads::FilenameConflictAction conflict_action) {
735 DCHECK_CURRENTLY_ON(BrowserThread::UI);
736 bool found_info = false;
737 for (size_t index = 0; index < determiners_.size(); ++index) {
738 if (determiners_[index].extension_id == extension_id) {
739 found_info = true;
740 if (determiners_[index].reported)
741 return false;
742 determiners_[index].reported = true;
743 // Do not use filename if another determiner has already overridden the
744 // filename and they take precedence. Extensions that were installed
745 // later take precedence over previous extensions.
746 if (!filename.empty() ||
747 (conflict_action != downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY)) {
748 WarningSet warnings;
749 std::string winner_extension_id;
750 ExtensionDownloadsEventRouter::DetermineFilenameInternal(
751 filename,
752 conflict_action,
753 determiners_[index].extension_id,
754 determiners_[index].install_time,
755 determiner_.extension_id,
756 determiner_.install_time,
757 &winner_extension_id,
758 &determined_filename_,
759 &determined_conflict_action_,
760 &warnings);
761 if (!warnings.empty())
762 WarningService::NotifyWarningsOnUI(profile, warnings);
763 if (winner_extension_id == determiners_[index].extension_id)
764 determiner_ = determiners_[index];
766 break;
769 if (!found_info)
770 return false;
771 CheckAllDeterminersCalled();
772 return true;
775 private:
776 static int determine_filename_timeout_s_;
778 struct DeterminerInfo {
779 DeterminerInfo();
780 DeterminerInfo(const std::string& e_id,
781 const base::Time& installed);
782 ~DeterminerInfo();
784 std::string extension_id;
785 base::Time install_time;
786 bool reported;
788 typedef std::vector<DeterminerInfo> DeterminerInfoVector;
790 static const char kKey[];
792 // This is safe to call even while not waiting for determiners to call back;
793 // in that case, the callbacks will be null so they won't be Run.
794 void CheckAllDeterminersCalled() {
795 for (DeterminerInfoVector::iterator iter = determiners_.begin();
796 iter != determiners_.end(); ++iter) {
797 if (!iter->reported)
798 return;
800 CallFilenameCallback();
802 // Don't clear determiners_ immediately in case there's a second listener
803 // for one of the extensions, so that DetermineFilename can return
804 // kTooManyListeners. After a few seconds, DetermineFilename will return
805 // kUnexpectedDeterminer instead of kTooManyListeners so that determiners_
806 // doesn't keep hogging memory.
807 weak_ptr_factory_.reset(
808 new base::WeakPtrFactory<ExtensionDownloadsEventRouterData>(this));
809 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
810 FROM_HERE,
811 base::Bind(&ExtensionDownloadsEventRouterData::ClearPendingDeterminers,
812 weak_ptr_factory_->GetWeakPtr()),
813 base::TimeDelta::FromSeconds(15));
816 void CallFilenameCallback() {
817 if (determined_filename_.empty() &&
818 (determined_conflict_action_ ==
819 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY)) {
820 if (!filename_no_change_.is_null())
821 filename_no_change_.Run();
822 } else {
823 if (!filename_change_.is_null()) {
824 filename_change_.Run(determined_filename_, ConvertConflictAction(
825 determined_conflict_action_));
828 // Clear the callbacks immediately in case they aren't idempotent.
829 filename_no_change_ = base::Closure();
830 filename_change_ = ExtensionDownloadsEventRouter::FilenameChangedCallback();
834 int updated_;
835 int changed_fired_;
836 scoped_ptr<base::DictionaryValue> json_;
838 base::Closure filename_no_change_;
839 ExtensionDownloadsEventRouter::FilenameChangedCallback filename_change_;
841 DeterminerInfoVector determiners_;
843 base::FilePath creator_suggested_filename_;
844 downloads::FilenameConflictAction
845 creator_conflict_action_;
846 base::FilePath determined_filename_;
847 downloads::FilenameConflictAction
848 determined_conflict_action_;
849 DeterminerInfo determiner_;
851 scoped_ptr<base::WeakPtrFactory<ExtensionDownloadsEventRouterData> >
852 weak_ptr_factory_;
854 DISALLOW_COPY_AND_ASSIGN(ExtensionDownloadsEventRouterData);
857 int ExtensionDownloadsEventRouterData::determine_filename_timeout_s_ = 15;
859 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo(
860 const std::string& e_id,
861 const base::Time& installed)
862 : extension_id(e_id),
863 install_time(installed),
864 reported(false) {
867 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo()
868 : reported(false) {
871 ExtensionDownloadsEventRouterData::DeterminerInfo::~DeterminerInfo() {}
873 const char ExtensionDownloadsEventRouterData::kKey[] =
874 "DownloadItem ExtensionDownloadsEventRouterData";
876 bool OnDeterminingFilenameWillDispatchCallback(
877 bool* any_determiners,
878 ExtensionDownloadsEventRouterData* data,
879 content::BrowserContext* context,
880 const Extension* extension,
881 base::ListValue* event_args,
882 const base::DictionaryValue* listener_filter) {
883 *any_determiners = true;
884 base::Time installed =
885 ExtensionPrefs::Get(context)->GetInstallTime(extension->id());
886 data->AddPendingDeterminer(extension->id(), installed);
887 return true;
890 bool Fault(bool error,
891 const char* message_in,
892 std::string* message_out) {
893 if (!error)
894 return false;
895 *message_out = message_in;
896 return true;
899 bool InvalidId(DownloadItem* valid_item, std::string* message_out) {
900 return Fault(!valid_item, errors::kInvalidId, message_out);
903 bool IsDownloadDeltaField(const std::string& field) {
904 return ((field == kUrlKey) ||
905 (field == kFilenameKey) ||
906 (field == kDangerKey) ||
907 (field == kMimeKey) ||
908 (field == kStartTimeKey) ||
909 (field == kEndTimeKey) ||
910 (field == kStateKey) ||
911 (field == kCanResumeKey) ||
912 (field == kPausedKey) ||
913 (field == kErrorKey) ||
914 (field == kTotalBytesKey) ||
915 (field == kFileSizeKey) ||
916 (field == kExistsKey));
919 } // namespace
921 const char DownloadedByExtension::kKey[] =
922 "DownloadItem DownloadedByExtension";
924 DownloadedByExtension* DownloadedByExtension::Get(
925 content::DownloadItem* item) {
926 base::SupportsUserData::Data* data = item->GetUserData(kKey);
927 return (data == NULL) ? NULL :
928 static_cast<DownloadedByExtension*>(data);
931 DownloadedByExtension::DownloadedByExtension(
932 content::DownloadItem* item,
933 const std::string& id,
934 const std::string& name)
935 : id_(id),
936 name_(name) {
937 item->SetUserData(kKey, this);
940 DownloadsDownloadFunction::DownloadsDownloadFunction() {}
942 DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
944 bool DownloadsDownloadFunction::RunAsync() {
945 scoped_ptr<downloads::Download::Params> params(
946 downloads::Download::Params::Create(*args_));
947 EXTENSION_FUNCTION_VALIDATE(params.get());
948 const downloads::DownloadOptions& options = params->options;
949 GURL download_url(options.url);
950 if (Fault(!download_url.is_valid(), errors::kInvalidURL, &error_))
951 return false;
953 Profile* current_profile = GetProfile();
954 if (include_incognito() && GetProfile()->HasOffTheRecordProfile())
955 current_profile = GetProfile()->GetOffTheRecordProfile();
957 scoped_ptr<content::DownloadUrlParameters> download_params(
958 new content::DownloadUrlParameters(
959 download_url, render_frame_host()->GetProcess()->GetID(),
960 render_view_host_do_not_use()->GetRoutingID(),
961 current_profile->GetResourceContext()));
963 base::FilePath creator_suggested_filename;
964 if (options.filename.get()) {
965 #if defined(OS_WIN)
966 // Can't get filename16 from options.ToValue() because that converts it from
967 // std::string.
968 base::DictionaryValue* options_value = NULL;
969 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options_value));
970 base::string16 filename16;
971 EXTENSION_FUNCTION_VALIDATE(options_value->GetString(
972 kFilenameKey, &filename16));
973 creator_suggested_filename = base::FilePath(filename16);
974 #elif defined(OS_POSIX)
975 creator_suggested_filename = base::FilePath(*options.filename.get());
976 #endif
977 if (!net::IsSafePortableRelativePath(creator_suggested_filename)) {
978 error_ = errors::kInvalidFilename;
979 return false;
983 if (options.save_as.get())
984 download_params->set_prompt(*options.save_as.get());
986 if (options.headers.get()) {
987 typedef downloads::HeaderNameValuePair HeaderNameValuePair;
988 for (std::vector<linked_ptr<HeaderNameValuePair> >::const_iterator iter =
989 options.headers->begin();
990 iter != options.headers->end();
991 ++iter) {
992 const HeaderNameValuePair& name_value = **iter;
993 if (!net::HttpUtil::IsValidHeaderName(name_value.name)) {
994 error_ = errors::kInvalidHeaderName;
995 return false;
997 if (!net::HttpUtil::IsSafeHeader(name_value.name)) {
998 error_ = errors::kInvalidHeaderUnsafe;
999 return false;
1001 if (!net::HttpUtil::IsValidHeaderValue(name_value.value)) {
1002 error_ = errors::kInvalidHeaderValue;
1003 return false;
1005 download_params->add_request_header(name_value.name, name_value.value);
1009 std::string method_string =
1010 downloads::ToString(options.method);
1011 if (!method_string.empty())
1012 download_params->set_method(method_string);
1013 if (options.body.get())
1014 download_params->set_post_body(*options.body.get());
1015 download_params->set_callback(base::Bind(
1016 &DownloadsDownloadFunction::OnStarted, this,
1017 creator_suggested_filename, options.conflict_action));
1018 // Prevent login prompts for 401/407 responses.
1019 download_params->set_do_not_prompt_for_login(true);
1021 DownloadManager* manager = BrowserContext::GetDownloadManager(
1022 current_profile);
1023 manager->DownloadUrl(download_params.Pass());
1024 RecordDownloadSource(DOWNLOAD_INITIATED_BY_EXTENSION);
1025 RecordApiFunctions(DOWNLOADS_FUNCTION_DOWNLOAD);
1026 return true;
1029 void DownloadsDownloadFunction::OnStarted(
1030 const base::FilePath& creator_suggested_filename,
1031 downloads::FilenameConflictAction creator_conflict_action,
1032 DownloadItem* item,
1033 content::DownloadInterruptReason interrupt_reason) {
1034 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1035 VLOG(1) << __FUNCTION__ << " " << item << " " << interrupt_reason;
1036 if (item) {
1037 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
1038 SetResult(new base::FundamentalValue(static_cast<int>(item->GetId())));
1039 if (!creator_suggested_filename.empty() ||
1040 (creator_conflict_action !=
1041 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY)) {
1042 ExtensionDownloadsEventRouterData* data =
1043 ExtensionDownloadsEventRouterData::Get(item);
1044 if (!data) {
1045 data = new ExtensionDownloadsEventRouterData(
1046 item,
1047 scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()));
1049 data->CreatorSuggestedFilename(
1050 creator_suggested_filename, creator_conflict_action);
1052 new DownloadedByExtension(item, extension()->id(), extension()->name());
1053 item->UpdateObservers();
1054 } else {
1055 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
1056 error_ = content::DownloadInterruptReasonToString(interrupt_reason);
1058 SendResponse(error_.empty());
1061 DownloadsSearchFunction::DownloadsSearchFunction() {}
1063 DownloadsSearchFunction::~DownloadsSearchFunction() {}
1065 bool DownloadsSearchFunction::RunSync() {
1066 scoped_ptr<downloads::Search::Params> params(
1067 downloads::Search::Params::Create(*args_));
1068 EXTENSION_FUNCTION_VALIDATE(params.get());
1069 DownloadManager* manager = NULL;
1070 DownloadManager* incognito_manager = NULL;
1071 GetManagers(GetProfile(), include_incognito(), &manager, &incognito_manager);
1072 ExtensionDownloadsEventRouter* router =
1073 DownloadServiceFactory::GetForBrowserContext(
1074 manager->GetBrowserContext())->GetExtensionEventRouter();
1075 router->CheckForHistoryFilesRemoval();
1076 if (incognito_manager) {
1077 ExtensionDownloadsEventRouter* incognito_router =
1078 DownloadServiceFactory::GetForBrowserContext(
1079 incognito_manager->GetBrowserContext())->GetExtensionEventRouter();
1080 incognito_router->CheckForHistoryFilesRemoval();
1082 DownloadQuery::DownloadVector results;
1083 RunDownloadQuery(params->query,
1084 manager,
1085 incognito_manager,
1086 &error_,
1087 &results);
1088 if (!error_.empty())
1089 return false;
1091 base::ListValue* json_results = new base::ListValue();
1092 for (DownloadManager::DownloadVector::const_iterator it = results.begin();
1093 it != results.end(); ++it) {
1094 DownloadItem* download_item = *it;
1095 uint32 download_id = download_item->GetId();
1096 bool off_record = ((incognito_manager != NULL) &&
1097 (incognito_manager->GetDownload(download_id) != NULL));
1098 scoped_ptr<base::DictionaryValue> json_item(
1099 DownloadItemToJSON(*it,
1100 off_record ? GetProfile()->GetOffTheRecordProfile()
1101 : GetProfile()->GetOriginalProfile()));
1102 json_results->Append(json_item.release());
1104 SetResult(json_results);
1105 RecordApiFunctions(DOWNLOADS_FUNCTION_SEARCH);
1106 return true;
1109 DownloadsPauseFunction::DownloadsPauseFunction() {}
1111 DownloadsPauseFunction::~DownloadsPauseFunction() {}
1113 bool DownloadsPauseFunction::RunSync() {
1114 scoped_ptr<downloads::Pause::Params> params(
1115 downloads::Pause::Params::Create(*args_));
1116 EXTENSION_FUNCTION_VALIDATE(params.get());
1117 DownloadItem* download_item =
1118 GetDownload(GetProfile(), include_incognito(), params->download_id);
1119 if (InvalidId(download_item, &error_) ||
1120 Fault(download_item->GetState() != DownloadItem::IN_PROGRESS,
1121 errors::kNotInProgress, &error_))
1122 return false;
1123 // If the item is already paused, this is a no-op and the operation will
1124 // silently succeed.
1125 download_item->Pause();
1126 RecordApiFunctions(DOWNLOADS_FUNCTION_PAUSE);
1127 return true;
1130 DownloadsResumeFunction::DownloadsResumeFunction() {}
1132 DownloadsResumeFunction::~DownloadsResumeFunction() {}
1134 bool DownloadsResumeFunction::RunSync() {
1135 scoped_ptr<downloads::Resume::Params> params(
1136 downloads::Resume::Params::Create(*args_));
1137 EXTENSION_FUNCTION_VALIDATE(params.get());
1138 DownloadItem* download_item =
1139 GetDownload(GetProfile(), include_incognito(), params->download_id);
1140 if (InvalidId(download_item, &error_) ||
1141 Fault(download_item->IsPaused() && !download_item->CanResume(),
1142 errors::kNotResumable, &error_))
1143 return false;
1144 // Note that if the item isn't paused, this will be a no-op, and the extension
1145 // call will seem successful.
1146 download_item->Resume();
1147 RecordApiFunctions(DOWNLOADS_FUNCTION_RESUME);
1148 return true;
1151 DownloadsCancelFunction::DownloadsCancelFunction() {}
1153 DownloadsCancelFunction::~DownloadsCancelFunction() {}
1155 bool DownloadsCancelFunction::RunSync() {
1156 scoped_ptr<downloads::Resume::Params> params(
1157 downloads::Resume::Params::Create(*args_));
1158 EXTENSION_FUNCTION_VALIDATE(params.get());
1159 DownloadItem* download_item =
1160 GetDownload(GetProfile(), include_incognito(), params->download_id);
1161 if (download_item &&
1162 (download_item->GetState() == DownloadItem::IN_PROGRESS))
1163 download_item->Cancel(true);
1164 // |download_item| can be NULL if the download ID was invalid or if the
1165 // download is not currently active. Either way, it's not a failure.
1166 RecordApiFunctions(DOWNLOADS_FUNCTION_CANCEL);
1167 return true;
1170 DownloadsEraseFunction::DownloadsEraseFunction() {}
1172 DownloadsEraseFunction::~DownloadsEraseFunction() {}
1174 bool DownloadsEraseFunction::RunSync() {
1175 scoped_ptr<downloads::Erase::Params> params(
1176 downloads::Erase::Params::Create(*args_));
1177 EXTENSION_FUNCTION_VALIDATE(params.get());
1178 DownloadManager* manager = NULL;
1179 DownloadManager* incognito_manager = NULL;
1180 GetManagers(GetProfile(), include_incognito(), &manager, &incognito_manager);
1181 DownloadQuery::DownloadVector results;
1182 RunDownloadQuery(params->query,
1183 manager,
1184 incognito_manager,
1185 &error_,
1186 &results);
1187 if (!error_.empty())
1188 return false;
1189 base::ListValue* json_results = new base::ListValue();
1190 for (DownloadManager::DownloadVector::const_iterator it = results.begin();
1191 it != results.end(); ++it) {
1192 json_results->Append(
1193 new base::FundamentalValue(static_cast<int>((*it)->GetId())));
1194 (*it)->Remove();
1196 SetResult(json_results);
1197 RecordApiFunctions(DOWNLOADS_FUNCTION_ERASE);
1198 return true;
1201 DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {
1204 DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() {
1207 bool DownloadsRemoveFileFunction::RunAsync() {
1208 scoped_ptr<downloads::RemoveFile::Params> params(
1209 downloads::RemoveFile::Params::Create(*args_));
1210 EXTENSION_FUNCTION_VALIDATE(params.get());
1211 DownloadItem* download_item =
1212 GetDownload(GetProfile(), include_incognito(), params->download_id);
1213 if (InvalidId(download_item, &error_) ||
1214 Fault((download_item->GetState() != DownloadItem::COMPLETE),
1215 errors::kNotComplete, &error_) ||
1216 Fault(download_item->GetFileExternallyRemoved(),
1217 errors::kFileAlreadyDeleted, &error_))
1218 return false;
1219 RecordApiFunctions(DOWNLOADS_FUNCTION_REMOVE_FILE);
1220 download_item->DeleteFile(
1221 base::Bind(&DownloadsRemoveFileFunction::Done, this));
1222 return true;
1225 void DownloadsRemoveFileFunction::Done(bool success) {
1226 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1227 if (!success) {
1228 error_ = errors::kFileNotRemoved;
1230 SendResponse(error_.empty());
1233 DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {}
1235 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
1237 DownloadsAcceptDangerFunction::OnPromptCreatedCallback*
1238 DownloadsAcceptDangerFunction::on_prompt_created_ = NULL;
1240 bool DownloadsAcceptDangerFunction::RunAsync() {
1241 scoped_ptr<downloads::AcceptDanger::Params> params(
1242 downloads::AcceptDanger::Params::Create(*args_));
1243 EXTENSION_FUNCTION_VALIDATE(params.get());
1244 PromptOrWait(params->download_id, 10);
1245 return true;
1248 void DownloadsAcceptDangerFunction::PromptOrWait(int download_id, int retries) {
1249 DownloadItem* download_item =
1250 GetDownload(GetProfile(), include_incognito(), download_id);
1251 content::WebContents* web_contents = dispatcher()->GetVisibleWebContents();
1252 if (InvalidId(download_item, &error_) ||
1253 Fault(download_item->GetState() != DownloadItem::IN_PROGRESS,
1254 errors::kNotInProgress, &error_) ||
1255 Fault(!download_item->IsDangerous(), errors::kNotDangerous, &error_) ||
1256 Fault(!web_contents, errors::kInvisibleContext, &error_)) {
1257 SendResponse(error_.empty());
1258 return;
1260 bool visible = platform_util::IsVisible(web_contents->GetNativeView());
1261 if (!visible) {
1262 if (retries > 0) {
1263 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1264 FROM_HERE, base::Bind(&DownloadsAcceptDangerFunction::PromptOrWait,
1265 this, download_id, retries - 1),
1266 base::TimeDelta::FromMilliseconds(100));
1267 return;
1269 error_ = errors::kInvisibleContext;
1270 SendResponse(error_.empty());
1271 return;
1273 RecordApiFunctions(DOWNLOADS_FUNCTION_ACCEPT_DANGER);
1274 // DownloadDangerPrompt displays a modal dialog using native widgets that the
1275 // user must either accept or cancel. It cannot be scripted.
1276 DownloadDangerPrompt* prompt = DownloadDangerPrompt::Create(
1277 download_item,
1278 web_contents,
1279 true,
1280 base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback,
1281 this, download_id));
1282 // DownloadDangerPrompt deletes itself
1283 if (on_prompt_created_ && !on_prompt_created_->is_null())
1284 on_prompt_created_->Run(prompt);
1285 SendResponse(error_.empty());
1288 void DownloadsAcceptDangerFunction::DangerPromptCallback(
1289 int download_id, DownloadDangerPrompt::Action action) {
1290 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1291 DownloadItem* download_item =
1292 GetDownload(GetProfile(), include_incognito(), download_id);
1293 if (InvalidId(download_item, &error_) ||
1294 Fault(download_item->GetState() != DownloadItem::IN_PROGRESS,
1295 errors::kNotInProgress, &error_))
1296 return;
1297 switch (action) {
1298 case DownloadDangerPrompt::ACCEPT:
1299 download_item->ValidateDangerousDownload();
1300 break;
1301 case DownloadDangerPrompt::CANCEL:
1302 download_item->Remove();
1303 break;
1304 case DownloadDangerPrompt::DISMISS:
1305 break;
1307 SendResponse(error_.empty());
1310 DownloadsShowFunction::DownloadsShowFunction() {}
1312 DownloadsShowFunction::~DownloadsShowFunction() {}
1314 bool DownloadsShowFunction::RunAsync() {
1315 scoped_ptr<downloads::Show::Params> params(
1316 downloads::Show::Params::Create(*args_));
1317 EXTENSION_FUNCTION_VALIDATE(params.get());
1318 DownloadItem* download_item =
1319 GetDownload(GetProfile(), include_incognito(), params->download_id);
1320 if (InvalidId(download_item, &error_))
1321 return false;
1322 download_item->ShowDownloadInShell();
1323 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW);
1324 return true;
1327 DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {}
1329 DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {}
1331 bool DownloadsShowDefaultFolderFunction::RunAsync() {
1332 DownloadManager* manager = NULL;
1333 DownloadManager* incognito_manager = NULL;
1334 GetManagers(GetProfile(), include_incognito(), &manager, &incognito_manager);
1335 platform_util::OpenItem(
1336 GetProfile(), DownloadPrefs::FromDownloadManager(manager)->DownloadPath(),
1337 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback());
1338 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER);
1339 return true;
1342 DownloadsOpenFunction::DownloadsOpenFunction() {}
1344 DownloadsOpenFunction::~DownloadsOpenFunction() {}
1346 bool DownloadsOpenFunction::RunSync() {
1347 scoped_ptr<downloads::Open::Params> params(
1348 downloads::Open::Params::Create(*args_));
1349 EXTENSION_FUNCTION_VALIDATE(params.get());
1350 DownloadItem* download_item =
1351 GetDownload(GetProfile(), include_incognito(), params->download_id);
1352 if (InvalidId(download_item, &error_) ||
1353 Fault(!user_gesture(), errors::kUserGesture, &error_) ||
1354 Fault(download_item->GetState() != DownloadItem::COMPLETE,
1355 errors::kNotComplete,
1356 &error_) ||
1357 Fault(!extension()->permissions_data()->HasAPIPermission(
1358 APIPermission::kDownloadsOpen),
1359 errors::kOpenPermission,
1360 &error_))
1361 return false;
1362 download_item->OpenDownload();
1363 RecordApiFunctions(DOWNLOADS_FUNCTION_OPEN);
1364 return true;
1367 DownloadsDragFunction::DownloadsDragFunction() {}
1369 DownloadsDragFunction::~DownloadsDragFunction() {}
1371 bool DownloadsDragFunction::RunAsync() {
1372 scoped_ptr<downloads::Drag::Params> params(
1373 downloads::Drag::Params::Create(*args_));
1374 EXTENSION_FUNCTION_VALIDATE(params.get());
1375 DownloadItem* download_item =
1376 GetDownload(GetProfile(), include_incognito(), params->download_id);
1377 content::WebContents* web_contents =
1378 dispatcher()->GetVisibleWebContents();
1379 if (InvalidId(download_item, &error_) ||
1380 Fault(!web_contents, errors::kInvisibleContext, &error_))
1381 return false;
1382 RecordApiFunctions(DOWNLOADS_FUNCTION_DRAG);
1383 gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
1384 download_item->GetTargetFilePath(), IconLoader::NORMAL);
1385 gfx::NativeView view = web_contents->GetNativeView();
1387 // Enable nested tasks during DnD, while |DragDownload()| blocks.
1388 base::MessageLoop::ScopedNestableTaskAllower allow(
1389 base::MessageLoop::current());
1390 DragDownloadItem(download_item, icon, view);
1392 return true;
1395 DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
1397 DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
1399 bool DownloadsSetShelfEnabledFunction::RunSync() {
1400 scoped_ptr<downloads::SetShelfEnabled::Params> params(
1401 downloads::SetShelfEnabled::Params::Create(*args_));
1402 EXTENSION_FUNCTION_VALIDATE(params.get());
1403 if (!extension()->permissions_data()->HasAPIPermission(
1404 APIPermission::kDownloadsShelf)) {
1405 error_ = download_extension_errors::kShelfPermission;
1406 return false;
1409 RecordApiFunctions(DOWNLOADS_FUNCTION_SET_SHELF_ENABLED);
1410 DownloadManager* manager = NULL;
1411 DownloadManager* incognito_manager = NULL;
1412 GetManagers(GetProfile(), include_incognito(), &manager, &incognito_manager);
1413 DownloadService* service = NULL;
1414 DownloadService* incognito_service = NULL;
1415 if (manager) {
1416 service = DownloadServiceFactory::GetForBrowserContext(
1417 manager->GetBrowserContext());
1418 service->GetExtensionEventRouter()->SetShelfEnabled(extension(),
1419 params->enabled);
1421 if (incognito_manager) {
1422 incognito_service = DownloadServiceFactory::GetForBrowserContext(
1423 incognito_manager->GetBrowserContext());
1424 incognito_service->GetExtensionEventRouter()->SetShelfEnabled(
1425 extension(), params->enabled);
1428 BrowserList* browsers = BrowserList::GetInstance(chrome::GetActiveDesktop());
1429 if (browsers) {
1430 for (BrowserList::const_iterator iter = browsers->begin();
1431 iter != browsers->end(); ++iter) {
1432 const Browser* browser = *iter;
1433 DownloadService* current_service =
1434 DownloadServiceFactory::GetForBrowserContext(browser->profile());
1435 if (((current_service == service) ||
1436 (current_service == incognito_service)) &&
1437 browser->window()->IsDownloadShelfVisible() &&
1438 !current_service->IsShelfEnabled())
1439 browser->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC);
1443 if (params->enabled &&
1444 ((manager && !service->IsShelfEnabled()) ||
1445 (incognito_manager && !incognito_service->IsShelfEnabled()))) {
1446 error_ = download_extension_errors::kShelfDisabled;
1447 return false;
1450 return true;
1453 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
1454 : icon_extractor_(new DownloadFileIconExtractorImpl()) {
1457 DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
1459 void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
1460 DownloadFileIconExtractor* extractor) {
1461 DCHECK(extractor);
1462 icon_extractor_.reset(extractor);
1465 bool DownloadsGetFileIconFunction::RunAsync() {
1466 scoped_ptr<downloads::GetFileIcon::Params> params(
1467 downloads::GetFileIcon::Params::Create(*args_));
1468 EXTENSION_FUNCTION_VALIDATE(params.get());
1469 const downloads::GetFileIconOptions* options =
1470 params->options.get();
1471 int icon_size = kDefaultIconSize;
1472 if (options && options->size.get())
1473 icon_size = *options->size.get();
1474 DownloadItem* download_item =
1475 GetDownload(GetProfile(), include_incognito(), params->download_id);
1476 if (InvalidId(download_item, &error_) ||
1477 Fault(download_item->GetTargetFilePath().empty(),
1478 errors::kEmptyFile, &error_))
1479 return false;
1480 // In-progress downloads return the intermediate filename for GetFullPath()
1481 // which doesn't have the final extension. Therefore a good file icon can't be
1482 // found, so use GetTargetFilePath() instead.
1483 DCHECK(icon_extractor_.get());
1484 DCHECK(icon_size == 16 || icon_size == 32);
1485 float scale = 1.0;
1486 content::WebContents* web_contents =
1487 dispatcher()->GetVisibleWebContents();
1488 if (web_contents) {
1489 scale = ui::GetScaleFactorForNativeView(
1490 web_contents->GetRenderWidgetHostView()->GetNativeView());
1492 EXTENSION_FUNCTION_VALIDATE(icon_extractor_->ExtractIconURLForPath(
1493 download_item->GetTargetFilePath(),
1494 scale,
1495 IconLoaderSizeFromPixelSize(icon_size),
1496 base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted, this)));
1497 return true;
1500 void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string& url) {
1501 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1502 if (Fault(url.empty(), errors::kIconNotFound, &error_)) {
1503 SendResponse(false);
1504 return;
1506 RecordApiFunctions(DOWNLOADS_FUNCTION_GET_FILE_ICON);
1507 SetResult(new base::StringValue(url));
1508 SendResponse(true);
1511 ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(
1512 Profile* profile,
1513 DownloadManager* manager)
1514 : profile_(profile),
1515 notifier_(manager, this),
1516 extension_registry_observer_(this) {
1517 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1518 DCHECK(profile_);
1519 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
1520 EventRouter* router = EventRouter::Get(profile_);
1521 if (router)
1522 router->RegisterObserver(this,
1523 downloads::OnDeterminingFilename::kEventName);
1526 ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
1527 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1528 EventRouter* router = EventRouter::Get(profile_);
1529 if (router)
1530 router->UnregisterObserver(this);
1533 void ExtensionDownloadsEventRouter::
1534 SetDetermineFilenameTimeoutSecondsForTesting(int s) {
1535 ExtensionDownloadsEventRouterData::
1536 SetDetermineFilenameTimeoutSecondsForTesting(s);
1539 void ExtensionDownloadsEventRouter::SetShelfEnabled(const Extension* extension,
1540 bool enabled) {
1541 std::set<const Extension*>::iterator iter =
1542 shelf_disabling_extensions_.find(extension);
1543 if (iter == shelf_disabling_extensions_.end()) {
1544 if (!enabled)
1545 shelf_disabling_extensions_.insert(extension);
1546 } else if (enabled) {
1547 shelf_disabling_extensions_.erase(extension);
1551 bool ExtensionDownloadsEventRouter::IsShelfEnabled() const {
1552 return shelf_disabling_extensions_.empty();
1555 // The method by which extensions hook into the filename determination process
1556 // is based on the method by which the omnibox API allows extensions to hook
1557 // into the omnibox autocompletion process. Extensions that wish to play a part
1558 // in the filename determination process call
1559 // chrome.downloads.onDeterminingFilename.addListener, which adds an
1560 // EventListener object to ExtensionEventRouter::listeners().
1562 // When a download's filename is being determined, DownloadTargetDeterminer (via
1563 // ChromeDownloadManagerDelegate (CDMD) ::NotifyExtensions()) passes 2 callbacks
1564 // to ExtensionDownloadsEventRouter::OnDeterminingFilename (ODF), which stores
1565 // the callbacks in the item's ExtensionDownloadsEventRouterData (EDERD) along
1566 // with all of the extension IDs that are listening for onDeterminingFilename
1567 // events. ODF dispatches chrome.downloads.onDeterminingFilename.
1569 // When the extension's event handler calls |suggestCallback|,
1570 // downloads_custom_bindings.js calls
1571 // DownloadsInternalDetermineFilenameFunction::RunAsync, which calls
1572 // EDER::DetermineFilename, which notifies the item's EDERD.
1574 // When the last extension's event handler returns, EDERD calls one of the two
1575 // callbacks that CDMD passed to ODF, allowing DownloadTargetDeterminer to
1576 // continue the filename determination process. If multiple extensions wish to
1577 // override the filename, then the extension that was last installed wins.
1579 void ExtensionDownloadsEventRouter::OnDeterminingFilename(
1580 DownloadItem* item,
1581 const base::FilePath& suggested_path,
1582 const base::Closure& no_change,
1583 const FilenameChangedCallback& change) {
1584 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1585 ExtensionDownloadsEventRouterData* data =
1586 ExtensionDownloadsEventRouterData::Get(item);
1587 if (!data) {
1588 no_change.Run();
1589 return;
1591 data->BeginFilenameDetermination(no_change, change);
1592 bool any_determiners = false;
1593 base::DictionaryValue* json = DownloadItemToJSON(
1594 item, profile_).release();
1595 json->SetString(kFilenameKey, suggested_path.LossyDisplayName());
1596 DispatchEvent(events::DOWNLOADS_ON_DETERMINING_FILENAME,
1597 downloads::OnDeterminingFilename::kEventName, false,
1598 base::Bind(&OnDeterminingFilenameWillDispatchCallback,
1599 &any_determiners, data),
1600 json);
1601 if (!any_determiners) {
1602 data->ClearPendingDeterminers();
1603 if (!data->creator_suggested_filename().empty() ||
1604 (data->creator_conflict_action() !=
1605 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY)) {
1606 change.Run(data->creator_suggested_filename(),
1607 ConvertConflictAction(data->creator_conflict_action()));
1608 // If all listeners are removed, don't keep |data| around.
1609 data->ResetCreatorSuggestion();
1610 } else {
1611 no_change.Run();
1616 void ExtensionDownloadsEventRouter::DetermineFilenameInternal(
1617 const base::FilePath& filename,
1618 downloads::FilenameConflictAction conflict_action,
1619 const std::string& suggesting_extension_id,
1620 const base::Time& suggesting_install_time,
1621 const std::string& incumbent_extension_id,
1622 const base::Time& incumbent_install_time,
1623 std::string* winner_extension_id,
1624 base::FilePath* determined_filename,
1625 downloads::FilenameConflictAction* determined_conflict_action,
1626 WarningSet* warnings) {
1627 DCHECK(!filename.empty() ||
1628 (conflict_action != downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY));
1629 DCHECK(!suggesting_extension_id.empty());
1631 if (incumbent_extension_id.empty()) {
1632 *winner_extension_id = suggesting_extension_id;
1633 *determined_filename = filename;
1634 *determined_conflict_action = conflict_action;
1635 return;
1638 if (suggesting_install_time < incumbent_install_time) {
1639 *winner_extension_id = incumbent_extension_id;
1640 warnings->insert(Warning::CreateDownloadFilenameConflictWarning(
1641 suggesting_extension_id,
1642 incumbent_extension_id,
1643 filename,
1644 *determined_filename));
1645 return;
1648 *winner_extension_id = suggesting_extension_id;
1649 warnings->insert(Warning::CreateDownloadFilenameConflictWarning(
1650 incumbent_extension_id,
1651 suggesting_extension_id,
1652 *determined_filename,
1653 filename));
1654 *determined_filename = filename;
1655 *determined_conflict_action = conflict_action;
1658 bool ExtensionDownloadsEventRouter::DetermineFilename(
1659 Profile* profile,
1660 bool include_incognito,
1661 const std::string& ext_id,
1662 int download_id,
1663 const base::FilePath& const_filename,
1664 downloads::FilenameConflictAction conflict_action,
1665 std::string* error) {
1666 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1667 RecordApiFunctions(DOWNLOADS_FUNCTION_DETERMINE_FILENAME);
1668 DownloadItem* item = GetDownload(profile, include_incognito, download_id);
1669 ExtensionDownloadsEventRouterData* data =
1670 item ? ExtensionDownloadsEventRouterData::Get(item) : NULL;
1671 // maxListeners=1 in downloads.idl and suggestCallback in
1672 // downloads_custom_bindings.js should prevent duplicate DeterminerCallback
1673 // calls from the same renderer, but an extension may have more than one
1674 // renderer, so don't DCHECK(!reported).
1675 if (InvalidId(item, error) ||
1676 Fault(item->GetState() != DownloadItem::IN_PROGRESS,
1677 errors::kNotInProgress, error) ||
1678 Fault(!data, errors::kUnexpectedDeterminer, error) ||
1679 Fault(data->DeterminerAlreadyReported(ext_id),
1680 errors::kTooManyListeners, error))
1681 return false;
1682 base::FilePath::StringType filename_str(const_filename.value());
1683 // Allow windows-style directory separators on all platforms.
1684 std::replace(filename_str.begin(), filename_str.end(),
1685 FILE_PATH_LITERAL('\\'), FILE_PATH_LITERAL('/'));
1686 base::FilePath filename(filename_str);
1687 bool valid_filename = net::IsSafePortableRelativePath(filename);
1688 filename = (valid_filename ? filename.NormalizePathSeparators() :
1689 base::FilePath());
1690 // If the invalid filename check is moved to before DeterminerCallback(), then
1691 // it will block forever waiting for this ext_id to report.
1692 if (Fault(!data->DeterminerCallback(
1693 profile, ext_id, filename, conflict_action),
1694 errors::kUnexpectedDeterminer, error) ||
1695 Fault((!const_filename.empty() && !valid_filename),
1696 errors::kInvalidFilename, error))
1697 return false;
1698 return true;
1701 void ExtensionDownloadsEventRouter::OnListenerRemoved(
1702 const EventListenerInfo& details) {
1703 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1704 DownloadManager* manager = notifier_.GetManager();
1705 if (!manager)
1706 return;
1707 bool determiner_removed = (
1708 details.event_name == downloads::OnDeterminingFilename::kEventName);
1709 EventRouter* router = EventRouter::Get(profile_);
1710 bool any_listeners =
1711 router->HasEventListener(downloads::OnChanged::kEventName) ||
1712 router->HasEventListener(downloads::OnDeterminingFilename::kEventName);
1713 if (!determiner_removed && any_listeners)
1714 return;
1715 DownloadManager::DownloadVector items;
1716 manager->GetAllDownloads(&items);
1717 for (DownloadManager::DownloadVector::const_iterator iter =
1718 items.begin();
1719 iter != items.end(); ++iter) {
1720 ExtensionDownloadsEventRouterData* data =
1721 ExtensionDownloadsEventRouterData::Get(*iter);
1722 if (!data)
1723 continue;
1724 if (determiner_removed) {
1725 // Notify any items that may be waiting for callbacks from this
1726 // extension/determiner. This will almost always be a no-op, however, it
1727 // is possible for an extension renderer to be unloaded while a download
1728 // item is waiting for a determiner. In that case, the download item
1729 // should proceed.
1730 data->DeterminerRemoved(details.extension_id);
1732 if (!any_listeners &&
1733 data->creator_suggested_filename().empty()) {
1734 ExtensionDownloadsEventRouterData::Remove(*iter);
1739 // That's all the methods that have to do with filename determination. The rest
1740 // have to do with the other, less special events.
1742 void ExtensionDownloadsEventRouter::OnDownloadCreated(
1743 DownloadManager* manager, DownloadItem* download_item) {
1744 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1745 if (download_item->IsTemporary())
1746 return;
1748 EventRouter* router = EventRouter::Get(profile_);
1749 // Avoid allocating a bunch of memory in DownloadItemToJSON if it isn't going
1750 // to be used.
1751 if (!router ||
1752 (!router->HasEventListener(downloads::OnCreated::kEventName) &&
1753 !router->HasEventListener(downloads::OnChanged::kEventName) &&
1754 !router->HasEventListener(
1755 downloads::OnDeterminingFilename::kEventName))) {
1756 return;
1758 scoped_ptr<base::DictionaryValue> json_item(
1759 DownloadItemToJSON(download_item, profile_));
1760 DispatchEvent(events::DOWNLOADS_ON_CREATED, downloads::OnCreated::kEventName,
1761 true, Event::WillDispatchCallback(), json_item->DeepCopy());
1762 if (!ExtensionDownloadsEventRouterData::Get(download_item) &&
1763 (router->HasEventListener(downloads::OnChanged::kEventName) ||
1764 router->HasEventListener(
1765 downloads::OnDeterminingFilename::kEventName))) {
1766 new ExtensionDownloadsEventRouterData(download_item, json_item.Pass());
1770 void ExtensionDownloadsEventRouter::OnDownloadUpdated(
1771 DownloadManager* manager, DownloadItem* download_item) {
1772 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1773 EventRouter* router = EventRouter::Get(profile_);
1774 ExtensionDownloadsEventRouterData* data =
1775 ExtensionDownloadsEventRouterData::Get(download_item);
1776 if (download_item->IsTemporary() ||
1777 !router->HasEventListener(downloads::OnChanged::kEventName)) {
1778 return;
1780 if (!data) {
1781 // The download_item probably transitioned from temporary to not temporary,
1782 // or else an event listener was added.
1783 data = new ExtensionDownloadsEventRouterData(
1784 download_item,
1785 scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()));
1787 scoped_ptr<base::DictionaryValue> new_json(DownloadItemToJSON(
1788 download_item, profile_));
1789 scoped_ptr<base::DictionaryValue> delta(new base::DictionaryValue());
1790 delta->SetInteger(kIdKey, download_item->GetId());
1791 std::set<std::string> new_fields;
1792 bool changed = false;
1794 // For each field in the new json representation of the download_item except
1795 // the bytesReceived field, if the field has changed from the previous old
1796 // json, set the differences in the |delta| object and remember that something
1797 // significant changed.
1798 for (base::DictionaryValue::Iterator iter(*new_json.get());
1799 !iter.IsAtEnd(); iter.Advance()) {
1800 new_fields.insert(iter.key());
1801 if (IsDownloadDeltaField(iter.key())) {
1802 const base::Value* old_value = NULL;
1803 if (!data->json().HasKey(iter.key()) ||
1804 (data->json().Get(iter.key(), &old_value) &&
1805 !iter.value().Equals(old_value))) {
1806 delta->Set(iter.key() + ".current", iter.value().DeepCopy());
1807 if (old_value)
1808 delta->Set(iter.key() + ".previous", old_value->DeepCopy());
1809 changed = true;
1814 // If a field was in the previous json but is not in the new json, set the
1815 // difference in |delta|.
1816 for (base::DictionaryValue::Iterator iter(data->json());
1817 !iter.IsAtEnd(); iter.Advance()) {
1818 if ((new_fields.find(iter.key()) == new_fields.end()) &&
1819 IsDownloadDeltaField(iter.key())) {
1820 // estimatedEndTime disappears after completion, but bytesReceived stays.
1821 delta->Set(iter.key() + ".previous", iter.value().DeepCopy());
1822 changed = true;
1826 // Update the OnChangedStat and dispatch the event if something significant
1827 // changed. Replace the stored json with the new json.
1828 data->OnItemUpdated();
1829 if (changed) {
1830 DispatchEvent(events::DOWNLOADS_ON_CHANGED,
1831 downloads::OnChanged::kEventName, true,
1832 Event::WillDispatchCallback(), delta.release());
1833 data->OnChangedFired();
1835 data->set_json(new_json.Pass());
1838 void ExtensionDownloadsEventRouter::OnDownloadRemoved(
1839 DownloadManager* manager, DownloadItem* download_item) {
1840 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1841 if (download_item->IsTemporary())
1842 return;
1843 DispatchEvent(
1844 events::DOWNLOADS_ON_ERASED, downloads::OnErased::kEventName, true,
1845 Event::WillDispatchCallback(),
1846 new base::FundamentalValue(static_cast<int>(download_item->GetId())));
1849 void ExtensionDownloadsEventRouter::DispatchEvent(
1850 events::HistogramValue histogram_value,
1851 const std::string& event_name,
1852 bool include_incognito,
1853 const Event::WillDispatchCallback& will_dispatch_callback,
1854 base::Value* arg) {
1855 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1856 if (!EventRouter::Get(profile_))
1857 return;
1858 scoped_ptr<base::ListValue> args(new base::ListValue());
1859 args->Append(arg);
1860 std::string json_args;
1861 base::JSONWriter::Write(*args, &json_args);
1862 scoped_ptr<Event> event(new Event(histogram_value, event_name, args.Pass()));
1863 // The downloads system wants to share on-record events with off-record
1864 // extension renderers even in incognito_split_mode because that's how
1865 // chrome://downloads works. The "restrict_to_profile" mechanism does not
1866 // anticipate this, so it does not automatically prevent sharing off-record
1867 // events with on-record extension renderers.
1868 event->restrict_to_browser_context =
1869 (include_incognito && !profile_->IsOffTheRecord()) ? NULL : profile_;
1870 event->will_dispatch_callback = will_dispatch_callback;
1871 EventRouter::Get(profile_)->BroadcastEvent(event.Pass());
1872 DownloadsNotificationSource notification_source;
1873 notification_source.event_name = event_name;
1874 notification_source.profile = profile_;
1875 content::Source<DownloadsNotificationSource> content_source(
1876 &notification_source);
1877 content::NotificationService::current()->Notify(
1878 extensions::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT,
1879 content_source,
1880 content::Details<std::string>(&json_args));
1883 void ExtensionDownloadsEventRouter::OnExtensionUnloaded(
1884 content::BrowserContext* browser_context,
1885 const Extension* extension,
1886 UnloadedExtensionInfo::Reason reason) {
1887 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1888 std::set<const Extension*>::iterator iter =
1889 shelf_disabling_extensions_.find(extension);
1890 if (iter != shelf_disabling_extensions_.end())
1891 shelf_disabling_extensions_.erase(iter);
1894 void ExtensionDownloadsEventRouter::CheckForHistoryFilesRemoval() {
1895 static const int kFileExistenceRateLimitSeconds = 10;
1896 DownloadManager* manager = notifier_.GetManager();
1897 if (!manager)
1898 return;
1899 base::Time now(base::Time::Now());
1900 int delta = now.ToTimeT() - last_checked_removal_.ToTimeT();
1901 if (delta <= kFileExistenceRateLimitSeconds)
1902 return;
1903 last_checked_removal_ = now;
1904 manager->CheckForHistoryFilesRemoval();
1907 } // namespace extensions