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