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