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