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 ¬ification_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