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