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