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