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