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