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