downloads_api.cc revision 010d83a9304c5a91596085d917d248abff47903a
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
75using content::BrowserContext;
76using content::BrowserThread;
77using content::DownloadItem;
78using content::DownloadManager;
79
80namespace download_extension_errors {
81
82const char kEmptyFile[] = "Filename not yet determined";
83const char kFileAlreadyDeleted[] = "Download file already deleted";
84const char kFileNotRemoved[] = "Unable to remove file";
85const char kIconNotFound[] = "Icon not found";
86const char kInvalidDangerType[] = "Invalid danger type";
87const char kInvalidFilename[] = "Invalid filename";
88const char kInvalidFilter[] = "Invalid query filter";
89const char kInvalidHeader[] = "Invalid request header";
90const char kInvalidId[] = "Invalid downloadId";
91const char kInvalidOrderBy[] = "Invalid orderBy field";
92const char kInvalidQueryLimit[] = "Invalid query limit";
93const char kInvalidState[] = "Invalid state";
94const char kInvalidURL[] = "Invalid URL";
95const char kInvisibleContext[] = "Javascript execution context is not visible "
96  "(tab, window, popup bubble)";
97const char kNotComplete[] = "Download must be complete";
98const char kNotDangerous[] = "Download must be dangerous";
99const char kNotInProgress[] = "Download must be in progress";
100const char kNotResumable[] = "DownloadItem.canResume must be true";
101const char kOpenPermission[] = "The \"downloads.open\" permission is required";
102const char kShelfDisabled[] = "Another extension has disabled the shelf";
103const char kShelfPermission[] = "downloads.setShelfEnabled requires the "
104  "\"downloads.shelf\" permission";
105const char kTooManyListeners[] = "Each extension may have at most one "
106  "onDeterminingFilename listener between all of its renderer execution "
107  "contexts.";
108const char kUnexpectedDeterminer[] = "Unexpected determineFilename call";
109const char kUserGesture[] = "User gesture required";
110
111}  // namespace download_extension_errors
112
113namespace errors = download_extension_errors;
114
115namespace extensions {
116
117namespace {
118
119namespace downloads = extensions::api::downloads;
120
121// Default icon size for getFileIcon() in pixels.
122const int  kDefaultIconSize = 32;
123
124// Parameter keys
125const char kByExtensionIdKey[] = "byExtensionId";
126const char kByExtensionNameKey[] = "byExtensionName";
127const char kBytesReceivedKey[] = "bytesReceived";
128const char kCanResumeKey[] = "canResume";
129const char kDangerAccepted[] = "accepted";
130const char kDangerContent[] = "content";
131const char kDangerFile[] = "file";
132const char kDangerHost[] = "host";
133const char kDangerKey[] = "danger";
134const char kDangerSafe[] = "safe";
135const char kDangerUncommon[] = "uncommon";
136const char kDangerUnwanted[] = "unwanted";
137const char kDangerUrl[] = "url";
138const char kEndTimeKey[] = "endTime";
139const char kEndedAfterKey[] = "endedAfter";
140const char kEndedBeforeKey[] = "endedBefore";
141const char kErrorKey[] = "error";
142const char kEstimatedEndTimeKey[] = "estimatedEndTime";
143const char kExistsKey[] = "exists";
144const char kFileSizeKey[] = "fileSize";
145const char kFilenameKey[] = "filename";
146const char kFilenameRegexKey[] = "filenameRegex";
147const char kIdKey[] = "id";
148const char kIncognitoKey[] = "incognito";
149const char kMimeKey[] = "mime";
150const char kPausedKey[] = "paused";
151const char kQueryKey[] = "query";
152const char kReferrerUrlKey[] = "referrer";
153const char kStartTimeKey[] = "startTime";
154const char kStartedAfterKey[] = "startedAfter";
155const char kStartedBeforeKey[] = "startedBefore";
156const char kStateComplete[] = "complete";
157const char kStateInProgress[] = "in_progress";
158const char kStateInterrupted[] = "interrupted";
159const char kStateKey[] = "state";
160const char kTotalBytesGreaterKey[] = "totalBytesGreater";
161const char kTotalBytesKey[] = "totalBytes";
162const char kTotalBytesLessKey[] = "totalBytesLess";
163const char kUrlKey[] = "url";
164const char kUrlRegexKey[] = "urlRegex";
165
166// Note: Any change to the danger type strings, should be accompanied by a
167// corresponding change to downloads.json.
168const char* kDangerStrings[] = {
169  kDangerSafe,
170  kDangerFile,
171  kDangerUrl,
172  kDangerContent,
173  kDangerSafe,
174  kDangerUncommon,
175  kDangerAccepted,
176  kDangerHost,
177  kDangerUnwanted
178};
179COMPILE_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.
184const char* kStateStrings[] = {
185  kStateInProgress,
186  kStateComplete,
187  kStateInterrupted,
188  kStateInterrupted,
189};
190COMPILE_ASSERT(arraysize(kStateStrings) == DownloadItem::MAX_DOWNLOAD_STATE,
191               download_item_state_enum_changed);
192
193const 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
203content::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
211const 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
221DownloadItem::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
229std::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
238scoped_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
295class 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
312bool 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
329void 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
336IconLoader::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
346typedef base::hash_map<std::string, DownloadQuery::FilterType> FilterTypeMap;
347
348void 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
370typedef base::hash_map<std::string, DownloadQuery::SortType> SortTypeMap;
371
372void 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
386bool 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.
394void 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
410DownloadItem* 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
420enum 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
441void RecordApiFunctions(DownloadsFunctionName function) {
442  UMA_HISTOGRAM_ENUMERATION("Download.ApiFunctions",
443                            function,
444                            DOWNLOADS_FUNCTION_LAST);
445}
446
447void 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
478void 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
560DownloadPathReservationTracker::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
575class 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
850int ExtensionDownloadsEventRouterData::determine_filename_timeout_s_ = 15;
851
852ExtensionDownloadsEventRouterData::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
860ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo()
861    : reported(false) {
862}
863
864ExtensionDownloadsEventRouterData::DeterminerInfo::~DeterminerInfo() {}
865
866const char ExtensionDownloadsEventRouterData::kKey[] =
867  "DownloadItem ExtensionDownloadsEventRouterData";
868
869class 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
922std::map<DownloadManager*, ManagerDestructionObserver*>*
923  ManagerDestructionObserver::manager_file_existence_last_checked_ = NULL;
924
925void 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
937bool 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
946bool InvalidId(DownloadItem* valid_item, std::string* message_out) {
947  return Fault(!valid_item, errors::kInvalidId, message_out);
948}
949
950bool 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
968const char DownloadedByExtension::kKey[] =
969  "DownloadItem DownloadedByExtension";
970
971DownloadedByExtension* 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
978DownloadedByExtension::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
987DownloadsDownloadFunction::DownloadsDownloadFunction() {}
988
989DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
990
991bool 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
1069void 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
1102DownloadsSearchFunction::DownloadsSearchFunction() {}
1103
1104DownloadsSearchFunction::~DownloadsSearchFunction() {}
1105
1106bool 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
1142DownloadsPauseFunction::DownloadsPauseFunction() {}
1143
1144DownloadsPauseFunction::~DownloadsPauseFunction() {}
1145
1146bool 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
1163DownloadsResumeFunction::DownloadsResumeFunction() {}
1164
1165DownloadsResumeFunction::~DownloadsResumeFunction() {}
1166
1167bool 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
1184DownloadsCancelFunction::DownloadsCancelFunction() {}
1185
1186DownloadsCancelFunction::~DownloadsCancelFunction() {}
1187
1188bool 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
1203DownloadsEraseFunction::DownloadsEraseFunction() {}
1204
1205DownloadsEraseFunction::~DownloadsEraseFunction() {}
1206
1207bool 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
1234DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {
1235}
1236
1237DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() {
1238}
1239
1240bool 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
1258void DownloadsRemoveFileFunction::Done(bool success) {
1259  DCHECK_CURRENTLY_ON(BrowserThread::UI);
1260  if (!success) {
1261    error_ = errors::kFileNotRemoved;
1262  }
1263  SendResponse(error_.empty());
1264}
1265
1266DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {}
1267
1268DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
1269
1270DownloadsAcceptDangerFunction::OnPromptCreatedCallback*
1271    DownloadsAcceptDangerFunction::on_prompt_created_ = NULL;
1272
1273bool 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
1281void 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
1323void 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
1345DownloadsShowFunction::DownloadsShowFunction() {}
1346
1347DownloadsShowFunction::~DownloadsShowFunction() {}
1348
1349bool 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
1362DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {}
1363
1364DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {}
1365
1366bool 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
1377DownloadsOpenFunction::DownloadsOpenFunction() {}
1378
1379DownloadsOpenFunction::~DownloadsOpenFunction() {}
1380
1381bool 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
1400DownloadsDragFunction::DownloadsDragFunction() {}
1401
1402DownloadsDragFunction::~DownloadsDragFunction() {}
1403
1404bool 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
1428DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
1429
1430DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
1431
1432bool 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
1486DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
1487    : icon_extractor_(new DownloadFileIconExtractorImpl()) {
1488}
1489
1490DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
1491
1492void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
1493    DownloadFileIconExtractor* extractor) {
1494  DCHECK(extractor);
1495  icon_extractor_.reset(extractor);
1496}
1497
1498bool 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
1533void 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
1544ExtensionDownloadsEventRouter::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
1560ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
1561  DCHECK_CURRENTLY_ON(BrowserThread::UI);
1562  extensions::EventRouter* router = extensions::EventRouter::Get(profile_);
1563  if (router)
1564    router->UnregisterObserver(this);
1565}
1566
1567void ExtensionDownloadsEventRouter::
1568    SetDetermineFilenameTimeoutSecondsForTesting(int s) {
1569  ExtensionDownloadsEventRouterData::
1570      SetDetermineFilenameTimeoutSecondsForTesting(s);
1571}
1572
1573void 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
1585bool 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
1613void 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
1651void 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
1696bool 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
1739void 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
1781void 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
1812void 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
1882void 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
1894void 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
1928void 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
1940