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