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 <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 
     82 namespace events = extensions::event_names;
     83 
     84 using content::BrowserContext;
     85 using content::BrowserThread;
     86 using content::DownloadItem;
     87 using content::DownloadManager;
     88 
     89 namespace download_extension_errors {
     90 
     91 const char kEmptyFile[] = "Filename not yet determined";
     92 const char kFileAlreadyDeleted[] = "Download file already deleted";
     93 const char kIconNotFound[] = "Icon not found";
     94 const char kInvalidDangerType[] = "Invalid danger type";
     95 const char kInvalidFilename[] = "Invalid filename";
     96 const char kInvalidFilter[] = "Invalid query filter";
     97 const char kInvalidHeader[] = "Invalid request header";
     98 const char kInvalidId[] = "Invalid downloadId";
     99 const char kInvalidOrderBy[] = "Invalid orderBy field";
    100 const char kInvalidQueryLimit[] = "Invalid query limit";
    101 const char kInvalidState[] = "Invalid state";
    102 const char kInvalidURL[] = "Invalid URL";
    103 const char kInvisibleContext[] = "Javascript execution context is not visible "
    104   "(tab, window, popup bubble)";
    105 const char kNotComplete[] = "Download must be complete";
    106 const char kNotDangerous[] = "Download must be dangerous";
    107 const char kNotInProgress[] = "Download must be in progress";
    108 const char kNotResumable[] = "DownloadItem.canResume must be true";
    109 const char kOpenPermission[] = "The \"downloads.open\" permission is required";
    110 const char kShelfDisabled[] = "Another extension has disabled the shelf";
    111 const char kShelfPermission[] = "downloads.setShelfEnabled requires the "
    112   "\"downloads.shelf\" permission";
    113 const char kTooManyListeners[] = "Each extension may have at most one "
    114   "onDeterminingFilename listener between all of its renderer execution "
    115   "contexts.";
    116 const char kUnexpectedDeterminer[] = "Unexpected determineFilename call";
    117 
    118 }  // namespace download_extension_errors
    119 
    120 namespace errors = download_extension_errors;
    121 
    122 namespace {
    123 
    124 // Default icon size for getFileIcon() in pixels.
    125 const int  kDefaultIconSize = 32;
    126 
    127 // Parameter keys
    128 const char kByExtensionIdKey[] = "byExtensionId";
    129 const char kByExtensionNameKey[] = "byExtensionName";
    130 const char kBytesReceivedKey[] = "bytesReceived";
    131 const char kCanResumeKey[] = "canResume";
    132 const char kDangerAccepted[] = "accepted";
    133 const char kDangerContent[] = "content";
    134 const char kDangerFile[] = "file";
    135 const char kDangerHost[] = "host";
    136 const char kDangerKey[] = "danger";
    137 const char kDangerSafe[] = "safe";
    138 const char kDangerUncommon[] = "uncommon";
    139 const char kDangerUnwanted[] = "unwanted";
    140 const char kDangerUrl[] = "url";
    141 const char kEndTimeKey[] = "endTime";
    142 const char kEndedAfterKey[] = "endedAfter";
    143 const char kEndedBeforeKey[] = "endedBefore";
    144 const char kErrorKey[] = "error";
    145 const char kEstimatedEndTimeKey[] = "estimatedEndTime";
    146 const char kExistsKey[] = "exists";
    147 const char kFileSizeKey[] = "fileSize";
    148 const char kFilenameKey[] = "filename";
    149 const char kFilenameRegexKey[] = "filenameRegex";
    150 const char kIdKey[] = "id";
    151 const char kIncognitoKey[] = "incognito";
    152 const char kMimeKey[] = "mime";
    153 const char kPausedKey[] = "paused";
    154 const char kQueryKey[] = "query";
    155 const char kReferrerUrlKey[] = "referrer";
    156 const char kStartTimeKey[] = "startTime";
    157 const char kStartedAfterKey[] = "startedAfter";
    158 const char kStartedBeforeKey[] = "startedBefore";
    159 const char kStateComplete[] = "complete";
    160 const char kStateInProgress[] = "in_progress";
    161 const char kStateInterrupted[] = "interrupted";
    162 const char kStateKey[] = "state";
    163 const char kTotalBytesGreaterKey[] = "totalBytesGreater";
    164 const char kTotalBytesKey[] = "totalBytes";
    165 const char kTotalBytesLessKey[] = "totalBytesLess";
    166 const char kUrlKey[] = "url";
    167 const char kUrlRegexKey[] = "urlRegex";
    168 
    169 // Note: Any change to the danger type strings, should be accompanied by a
    170 // corresponding change to downloads.json.
    171 const char* kDangerStrings[] = {
    172   kDangerSafe,
    173   kDangerFile,
    174   kDangerUrl,
    175   kDangerContent,
    176   kDangerSafe,
    177   kDangerUncommon,
    178   kDangerAccepted,
    179   kDangerHost,
    180   kDangerUnwanted
    181 };
    182 COMPILE_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.
    187 const char* kStateStrings[] = {
    188   kStateInProgress,
    189   kStateComplete,
    190   kStateInterrupted,
    191   kStateInterrupted,
    192 };
    193 COMPILE_ASSERT(arraysize(kStateStrings) == DownloadItem::MAX_DOWNLOAD_STATE,
    194                download_item_state_enum_changed);
    195 
    196 const 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 
    206 content::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 
    214 const 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 
    224 DownloadItem::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 
    232 std::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 
    241 scoped_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 
    296 class 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 
    312 bool 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 
    329 void 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 
    337 IconLoader::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 
    347 typedef base::hash_map<std::string, DownloadQuery::FilterType> FilterTypeMap;
    348 
    349 void 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 
    371 typedef base::hash_map<std::string, DownloadQuery::SortType> SortTypeMap;
    372 
    373 void 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 
    387 bool 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.
    395 void 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 
    411 DownloadItem* 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 
    421 enum 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 
    441 void RecordApiFunctions(DownloadsFunctionName function) {
    442   UMA_HISTOGRAM_ENUMERATION("Download.ApiFunctions",
    443                             function,
    444                             DOWNLOADS_FUNCTION_LAST);
    445 }
    446 
    447 void 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 
    478 void 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 
    561 DownloadPathReservationTracker::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 
    576 class 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 
    819 ExtensionDownloadsEventRouterData::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 
    827 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo()
    828     : reported(false) {
    829 }
    830 
    831 ExtensionDownloadsEventRouterData::DeterminerInfo::~DeterminerInfo() {}
    832 
    833 const char ExtensionDownloadsEventRouterData::kKey[] =
    834   "DownloadItem ExtensionDownloadsEventRouterData";
    835 
    836 class 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 
    889 std::map<DownloadManager*, ManagerDestructionObserver*>*
    890   ManagerDestructionObserver::manager_file_existence_last_checked_ = NULL;
    891 
    892 void 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 
    905 bool 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 
    914 bool InvalidId(DownloadItem* valid_item, std::string* message_out) {
    915   return Fault(!valid_item, errors::kInvalidId, message_out);
    916 }
    917 
    918 bool 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 
    936 const char DownloadedByExtension::kKey[] =
    937   "DownloadItem DownloadedByExtension";
    938 
    939 DownloadedByExtension* 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 
    946 DownloadedByExtension::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 
    955 DownloadsDownloadFunction::DownloadsDownloadFunction() {}
    956 
    957 DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
    958 
    959 bool 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 
   1037 void 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 
   1068 DownloadsSearchFunction::DownloadsSearchFunction() {}
   1069 
   1070 DownloadsSearchFunction::~DownloadsSearchFunction() {}
   1071 
   1072 bool 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 
   1107 DownloadsPauseFunction::DownloadsPauseFunction() {}
   1108 
   1109 DownloadsPauseFunction::~DownloadsPauseFunction() {}
   1110 
   1111 bool 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 
   1128 DownloadsResumeFunction::DownloadsResumeFunction() {}
   1129 
   1130 DownloadsResumeFunction::~DownloadsResumeFunction() {}
   1131 
   1132 bool 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 
   1149 DownloadsCancelFunction::DownloadsCancelFunction() {}
   1150 
   1151 DownloadsCancelFunction::~DownloadsCancelFunction() {}
   1152 
   1153 bool 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 
   1168 DownloadsEraseFunction::DownloadsEraseFunction() {}
   1169 
   1170 DownloadsEraseFunction::~DownloadsEraseFunction() {}
   1171 
   1172 bool 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 
   1198 DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {}
   1199 
   1200 DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() {
   1201   if (item_) {
   1202     item_->RemoveObserver(this);
   1203     item_ = NULL;
   1204   }
   1205 }
   1206 
   1207 bool 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 
   1226 void 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 
   1236 void 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 
   1244 DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {}
   1245 
   1246 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
   1247 
   1248 bool 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 
   1275 void 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 
   1297 DownloadsShowFunction::DownloadsShowFunction() {}
   1298 
   1299 DownloadsShowFunction::~DownloadsShowFunction() {}
   1300 
   1301 bool 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 
   1314 DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {}
   1315 
   1316 DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {}
   1317 
   1318 bool 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 
   1328 DownloadsOpenFunction::DownloadsOpenFunction() {}
   1329 
   1330 DownloadsOpenFunction::~DownloadsOpenFunction() {}
   1331 
   1332 bool 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 
   1350 DownloadsDragFunction::DownloadsDragFunction() {}
   1351 
   1352 DownloadsDragFunction::~DownloadsDragFunction() {}
   1353 
   1354 bool 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 
   1378 DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
   1379 
   1380 DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
   1381 
   1382 bool 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 
   1436 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
   1437     : icon_extractor_(new DownloadFileIconExtractorImpl()) {
   1438 }
   1439 
   1440 DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
   1441 
   1442 void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
   1443     DownloadFileIconExtractor* extractor) {
   1444   DCHECK(extractor);
   1445   icon_extractor_.reset(extractor);
   1446 }
   1447 
   1448 bool 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 
   1475 void 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 
   1486 ExtensionDownloadsEventRouter::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 
   1501 ExtensionDownloadsEventRouter::~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 
   1509 void 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 
   1521 bool 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 
   1550 void 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 
   1587 void 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 
   1631 bool 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 
   1673 void 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 
   1715 void 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 
   1744 void 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 
   1814 void 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 
   1825 void 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 
   1860 void 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