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