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