| OLD | NEW |
| (Empty) |
| 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/download/download_extension_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/json/json_writer.h" | |
| 18 #include "base/lazy_instance.h" | |
| 19 #include "base/logging.h" | |
| 20 #include "base/metrics/histogram.h" | |
| 21 #include "base/stl_util.h" | |
| 22 #include "base/string16.h" | |
| 23 #include "base/string_split.h" | |
| 24 #include "base/string_util.h" | |
| 25 #include "base/stringprintf.h" | |
| 26 #include "base/values.h" | |
| 27 #include "chrome/browser/browser_process.h" | |
| 28 #include "chrome/browser/download/download_file_icon_extractor.h" | |
| 29 #include "chrome/browser/download/download_query.h" | |
| 30 #include "chrome/browser/download/download_service.h" | |
| 31 #include "chrome/browser/download/download_service_factory.h" | |
| 32 #include "chrome/browser/download/download_util.h" | |
| 33 #include "chrome/browser/extensions/extension_event_names.h" | |
| 34 #include "chrome/browser/extensions/extension_event_router.h" | |
| 35 #include "chrome/browser/icon_loader.h" | |
| 36 #include "chrome/browser/icon_manager.h" | |
| 37 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" | |
| 38 #include "chrome/browser/ui/browser_list.h" | |
| 39 #include "chrome/browser/ui/webui/web_ui_util.h" | |
| 40 #include "content/public/browser/download_interrupt_reasons.h" | |
| 41 #include "content/public/browser/download_item.h" | |
| 42 #include "content/public/browser/download_save_info.h" | |
| 43 #include "content/public/browser/render_process_host.h" | |
| 44 #include "content/public/browser/render_view_host.h" | |
| 45 #include "content/public/browser/resource_dispatcher_host.h" | |
| 46 #include "net/base/load_flags.h" | |
| 47 #include "net/http/http_util.h" | |
| 48 #include "net/url_request/url_request.h" | |
| 49 | |
| 50 using content::BrowserThread; | |
| 51 using content::DownloadId; | |
| 52 using content::DownloadItem; | |
| 53 using content::DownloadManager; | |
| 54 | |
| 55 namespace download_extension_errors { | |
| 56 | |
| 57 // Error messages | |
| 58 const char kGenericError[] = "I'm afraid I can't do that."; | |
| 59 const char kIconNotFoundError[] = "Icon not found."; | |
| 60 const char kInvalidDangerTypeError[] = "Invalid danger type"; | |
| 61 const char kInvalidFilterError[] = "Invalid query filter"; | |
| 62 const char kInvalidOperationError[] = "Invalid operation."; | |
| 63 const char kInvalidOrderByError[] = "Invalid orderBy field"; | |
| 64 const char kInvalidQueryLimit[] = "Invalid query limit"; | |
| 65 const char kInvalidStateError[] = "Invalid state"; | |
| 66 const char kInvalidURLError[] = "Invalid URL."; | |
| 67 const char kNotImplementedError[] = "NotImplemented."; | |
| 68 | |
| 69 } // namespace download_extension_errors | |
| 70 | |
| 71 namespace { | |
| 72 | |
| 73 // Default icon size for getFileIcon() in pixels. | |
| 74 const int kDefaultIconSize = 32; | |
| 75 | |
| 76 // Parameter keys | |
| 77 const char kBodyKey[] = "body"; | |
| 78 const char kBytesReceivedKey[] = "bytesReceived"; | |
| 79 const char kDangerAcceptedKey[] = "dangerAccepted"; | |
| 80 const char kDangerContent[] = "content"; | |
| 81 const char kDangerFile[] = "file"; | |
| 82 const char kDangerKey[] = "danger"; | |
| 83 const char kDangerSafe[] = "safe"; | |
| 84 const char kDangerUncommon[] = "uncommon"; | |
| 85 const char kDangerUrl[] = "url"; | |
| 86 const char kEndTimeKey[] = "endTime"; | |
| 87 const char kErrorKey[] = "error"; | |
| 88 const char kFileSizeKey[] = "fileSize"; | |
| 89 const char kFilenameKey[] = "filename"; | |
| 90 const char kFilenameRegexKey[] = "filenameRegex"; | |
| 91 const char kHeaderNameKey[] = "name"; | |
| 92 const char kHeaderValueKey[] = "value"; | |
| 93 const char kHeaderBinaryValueKey[] = "binaryValue"; | |
| 94 const char kHeadersKey[] = "headers"; | |
| 95 const char kIdKey[] = "id"; | |
| 96 const char kIncognito[] = "incognito"; | |
| 97 const char kLimitKey[] = "limit"; | |
| 98 const char kMethodKey[] = "method"; | |
| 99 const char kMimeKey[] = "mime"; | |
| 100 const char kOrderByKey[] = "orderBy"; | |
| 101 const char kPausedKey[] = "paused"; | |
| 102 const char kQueryKey[] = "query"; | |
| 103 const char kSaveAsKey[] = "saveAs"; | |
| 104 const char kSizeKey[] = "size"; | |
| 105 const char kStartTimeKey[] = "startTime"; | |
| 106 const char kStartedAfterKey[] = "startedAfter"; | |
| 107 const char kStartedBeforeKey[] = "startedBefore"; | |
| 108 const char kStateComplete[] = "complete"; | |
| 109 const char kStateInProgress[] = "in_progress"; | |
| 110 const char kStateInterrupted[] = "interrupted"; | |
| 111 const char kStateKey[] = "state"; | |
| 112 const char kTotalBytesKey[] = "totalBytes"; | |
| 113 const char kTotalBytesGreaterKey[] = "totalBytesGreater"; | |
| 114 const char kTotalBytesLessKey[] = "totalBytesLess"; | |
| 115 const char kUrlKey[] = "url"; | |
| 116 const char kUrlRegexKey[] = "urlRegex"; | |
| 117 | |
| 118 // Note: Any change to the danger type strings, should be accompanied by a | |
| 119 // corresponding change to {experimental.}downloads.json. | |
| 120 const char* kDangerStrings[] = { | |
| 121 kDangerSafe, | |
| 122 kDangerFile, | |
| 123 kDangerUrl, | |
| 124 kDangerContent, | |
| 125 kDangerSafe, | |
| 126 kDangerUncommon, | |
| 127 }; | |
| 128 COMPILE_ASSERT(arraysize(kDangerStrings) == content::DOWNLOAD_DANGER_TYPE_MAX, | |
| 129 download_danger_type_enum_changed); | |
| 130 | |
| 131 // Note: Any change to the state strings, should be accompanied by a | |
| 132 // corresponding change to {experimental.}downloads.json. | |
| 133 const char* kStateStrings[] = { | |
| 134 kStateInProgress, | |
| 135 kStateComplete, | |
| 136 kStateInterrupted, | |
| 137 NULL, | |
| 138 kStateInterrupted, | |
| 139 }; | |
| 140 COMPILE_ASSERT(arraysize(kStateStrings) == DownloadItem::MAX_DOWNLOAD_STATE, | |
| 141 download_item_state_enum_changed); | |
| 142 | |
| 143 const char* DangerString(content::DownloadDangerType danger) { | |
| 144 DCHECK(danger >= 0); | |
| 145 DCHECK(danger < static_cast<content::DownloadDangerType>( | |
| 146 arraysize(kDangerStrings))); | |
| 147 return kDangerStrings[danger]; | |
| 148 } | |
| 149 | |
| 150 content::DownloadDangerType DangerEnumFromString(const std::string& danger) { | |
| 151 for (size_t i = 0; i < arraysize(kDangerStrings); ++i) { | |
| 152 if (danger == kDangerStrings[i]) | |
| 153 return static_cast<content::DownloadDangerType>(i); | |
| 154 } | |
| 155 return content::DOWNLOAD_DANGER_TYPE_MAX; | |
| 156 } | |
| 157 | |
| 158 const char* StateString(DownloadItem::DownloadState state) { | |
| 159 DCHECK(state >= 0); | |
| 160 DCHECK(state < static_cast<DownloadItem::DownloadState>( | |
| 161 arraysize(kStateStrings))); | |
| 162 DCHECK(state != DownloadItem::REMOVING); | |
| 163 return kStateStrings[state]; | |
| 164 } | |
| 165 | |
| 166 DownloadItem::DownloadState StateEnumFromString(const std::string& state) { | |
| 167 for (size_t i = 0; i < arraysize(kStateStrings); ++i) { | |
| 168 if ((kStateStrings[i] != NULL) && (state == kStateStrings[i])) | |
| 169 return static_cast<DownloadItem::DownloadState>(i); | |
| 170 } | |
| 171 return DownloadItem::MAX_DOWNLOAD_STATE; | |
| 172 } | |
| 173 | |
| 174 bool ValidateFilename(const string16& filename) { | |
| 175 // TODO(benjhayden): More robust validation of filename. | |
| 176 if ((filename.find('/') != string16::npos) || | |
| 177 (filename.find('\\') != string16::npos)) | |
| 178 return false; | |
| 179 if (filename.size() >= 2u && filename[0] == L'.' && filename[1] == L'.') | |
| 180 return false; | |
| 181 return true; | |
| 182 } | |
| 183 | |
| 184 scoped_ptr<base::DictionaryValue> DownloadItemToJSON(DownloadItem* item) { | |
| 185 base::DictionaryValue* json = new base::DictionaryValue(); | |
| 186 json->SetInteger(kIdKey, item->GetId()); | |
| 187 json->SetString(kUrlKey, item->GetOriginalUrl().spec()); | |
| 188 json->SetString(kFilenameKey, item->GetFullPath().LossyDisplayName()); | |
| 189 json->SetString(kDangerKey, DangerString(item->GetDangerType())); | |
| 190 if (item->GetDangerType() != content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) | |
| 191 json->SetBoolean(kDangerAcceptedKey, | |
| 192 item->GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED); | |
| 193 json->SetString(kStateKey, StateString(item->GetState())); | |
| 194 json->SetBoolean(kPausedKey, item->IsPaused()); | |
| 195 json->SetString(kMimeKey, item->GetMimeType()); | |
| 196 json->SetInteger(kStartTimeKey, | |
| 197 (item->GetStartTime() - base::Time::UnixEpoch()).InMilliseconds()); | |
| 198 json->SetInteger(kBytesReceivedKey, item->GetReceivedBytes()); | |
| 199 json->SetInteger(kTotalBytesKey, item->GetTotalBytes()); | |
| 200 json->SetBoolean(kIncognito, item->IsOtr()); | |
| 201 if (item->GetState() == DownloadItem::INTERRUPTED) { | |
| 202 json->SetInteger(kErrorKey, static_cast<int>(item->GetLastReason())); | |
| 203 } else if (item->GetState() == DownloadItem::CANCELLED) { | |
| 204 json->SetInteger(kErrorKey, static_cast<int>( | |
| 205 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED)); | |
| 206 } | |
| 207 // TODO(benjhayden): Implement endTime and fileSize. | |
| 208 // json->SetInteger(kEndTimeKey, -1); | |
| 209 json->SetInteger(kFileSizeKey, item->GetTotalBytes()); | |
| 210 return scoped_ptr<base::DictionaryValue>(json); | |
| 211 } | |
| 212 | |
| 213 class DownloadFileIconExtractorImpl : public DownloadFileIconExtractor { | |
| 214 public: | |
| 215 DownloadFileIconExtractorImpl() {} | |
| 216 | |
| 217 ~DownloadFileIconExtractorImpl() {} | |
| 218 | |
| 219 virtual bool ExtractIconURLForPath(const FilePath& path, | |
| 220 IconLoader::IconSize icon_size, | |
| 221 IconURLCallback callback) OVERRIDE; | |
| 222 private: | |
| 223 void OnIconLoadComplete(IconManager::Handle handle, gfx::Image* icon); | |
| 224 | |
| 225 CancelableRequestConsumer cancelable_consumer_; | |
| 226 IconURLCallback callback_; | |
| 227 }; | |
| 228 | |
| 229 bool DownloadFileIconExtractorImpl::ExtractIconURLForPath( | |
| 230 const FilePath& path, | |
| 231 IconLoader::IconSize icon_size, | |
| 232 IconURLCallback callback) { | |
| 233 callback_ = callback; | |
| 234 IconManager* im = g_browser_process->icon_manager(); | |
| 235 // The contents of the file at |path| may have changed since a previous | |
| 236 // request, in which case the associated icon may also have changed. | |
| 237 // Therefore, for the moment we always call LoadIcon instead of attempting | |
| 238 // a LookupIcon. | |
| 239 im->LoadIcon( | |
| 240 path, icon_size, &cancelable_consumer_, | |
| 241 base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete, | |
| 242 base::Unretained(this))); | |
| 243 return true; | |
| 244 } | |
| 245 | |
| 246 void DownloadFileIconExtractorImpl::OnIconLoadComplete( | |
| 247 IconManager::Handle handle, | |
| 248 gfx::Image* icon) { | |
| 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 250 std::string url; | |
| 251 if (icon) | |
| 252 url = web_ui_util::GetImageDataUrl(*icon->ToSkBitmap()); | |
| 253 callback_.Run(url); | |
| 254 } | |
| 255 | |
| 256 IconLoader::IconSize IconLoaderSizeFromPixelSize(int pixel_size) { | |
| 257 switch (pixel_size) { | |
| 258 case 16: return IconLoader::SMALL; | |
| 259 case 32: return IconLoader::NORMAL; | |
| 260 default: | |
| 261 NOTREACHED(); | |
| 262 return IconLoader::NORMAL; | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 typedef base::hash_map<std::string, DownloadQuery::FilterType> FilterTypeMap; | |
| 267 | |
| 268 void InitFilterTypeMap(FilterTypeMap& filter_types) { | |
| 269 filter_types[kBytesReceivedKey] = | |
| 270 DownloadQuery::FILTER_BYTES_RECEIVED; | |
| 271 filter_types[kDangerAcceptedKey] = | |
| 272 DownloadQuery::FILTER_DANGER_ACCEPTED; | |
| 273 filter_types[kFilenameKey] = DownloadQuery::FILTER_FILENAME; | |
| 274 filter_types[kFilenameRegexKey] = | |
| 275 DownloadQuery::FILTER_FILENAME_REGEX; | |
| 276 filter_types[kMimeKey] = DownloadQuery::FILTER_MIME; | |
| 277 filter_types[kPausedKey] = DownloadQuery::FILTER_PAUSED; | |
| 278 filter_types[kQueryKey] = DownloadQuery::FILTER_QUERY; | |
| 279 filter_types[kStartedAfterKey] = DownloadQuery::FILTER_STARTED_AFTER; | |
| 280 filter_types[kStartedBeforeKey] = | |
| 281 DownloadQuery::FILTER_STARTED_BEFORE; | |
| 282 filter_types[kStartTimeKey] = DownloadQuery::FILTER_START_TIME; | |
| 283 filter_types[kTotalBytesKey] = DownloadQuery::FILTER_TOTAL_BYTES; | |
| 284 filter_types[kTotalBytesGreaterKey] = | |
| 285 DownloadQuery::FILTER_TOTAL_BYTES_GREATER; | |
| 286 filter_types[kTotalBytesLessKey] = | |
| 287 DownloadQuery::FILTER_TOTAL_BYTES_LESS; | |
| 288 filter_types[kUrlKey] = DownloadQuery::FILTER_URL; | |
| 289 filter_types[kUrlRegexKey] = DownloadQuery::FILTER_URL_REGEX; | |
| 290 } | |
| 291 | |
| 292 typedef base::hash_map<std::string, DownloadQuery::SortType> SortTypeMap; | |
| 293 | |
| 294 void InitSortTypeMap(SortTypeMap& sorter_types) { | |
| 295 sorter_types[kBytesReceivedKey] = DownloadQuery::SORT_BYTES_RECEIVED; | |
| 296 sorter_types[kDangerKey] = DownloadQuery::SORT_DANGER; | |
| 297 sorter_types[kDangerAcceptedKey] = | |
| 298 DownloadQuery::SORT_DANGER_ACCEPTED; | |
| 299 sorter_types[kFilenameKey] = DownloadQuery::SORT_FILENAME; | |
| 300 sorter_types[kMimeKey] = DownloadQuery::SORT_MIME; | |
| 301 sorter_types[kPausedKey] = DownloadQuery::SORT_PAUSED; | |
| 302 sorter_types[kStartTimeKey] = DownloadQuery::SORT_START_TIME; | |
| 303 sorter_types[kStateKey] = DownloadQuery::SORT_STATE; | |
| 304 sorter_types[kTotalBytesKey] = DownloadQuery::SORT_TOTAL_BYTES; | |
| 305 sorter_types[kUrlKey] = DownloadQuery::SORT_URL; | |
| 306 } | |
| 307 | |
| 308 bool IsNotTemporaryDownloadFilter(const DownloadItem& item) { | |
| 309 return !item.IsTemporary(); | |
| 310 } | |
| 311 | |
| 312 void GetManagers( | |
| 313 Profile* profile, | |
| 314 bool include_incognito, | |
| 315 DownloadManager** manager, DownloadManager** incognito_manager) { | |
| 316 *manager = DownloadServiceFactory::GetForProfile(profile)-> | |
| 317 GetDownloadManager(); | |
| 318 *incognito_manager = NULL; | |
| 319 if (include_incognito && profile->HasOffTheRecordProfile()) | |
| 320 *incognito_manager = DownloadServiceFactory::GetForProfile(profile-> | |
| 321 GetOffTheRecordProfile())->GetDownloadManager(); | |
| 322 } | |
| 323 | |
| 324 DownloadItem* GetActiveItemInternal( | |
| 325 Profile* profile, | |
| 326 bool include_incognito, | |
| 327 int id) { | |
| 328 DownloadManager* manager = NULL; | |
| 329 DownloadManager* incognito_manager = NULL; | |
| 330 GetManagers(profile, include_incognito, &manager, &incognito_manager); | |
| 331 DownloadItem* download_item = manager->GetActiveDownloadItem(id); | |
| 332 if (!download_item && incognito_manager) | |
| 333 download_item = incognito_manager->GetActiveDownloadItem(id); | |
| 334 return download_item; | |
| 335 } | |
| 336 | |
| 337 } // namespace | |
| 338 | |
| 339 bool DownloadsFunctionInterface::RunImplImpl( | |
| 340 DownloadsFunctionInterface* pimpl) { | |
| 341 CHECK(pimpl); | |
| 342 if (!pimpl->ParseArgs()) return false; | |
| 343 UMA_HISTOGRAM_ENUMERATION( | |
| 344 "Download.ApiFunctions", pimpl->function(), DOWNLOADS_FUNCTION_LAST); | |
| 345 return pimpl->RunInternal(); | |
| 346 } | |
| 347 | |
| 348 SyncDownloadsFunction::SyncDownloadsFunction( | |
| 349 DownloadsFunctionInterface::DownloadsFunctionName function) | |
| 350 : function_(function) { | |
| 351 } | |
| 352 | |
| 353 SyncDownloadsFunction::~SyncDownloadsFunction() {} | |
| 354 | |
| 355 bool SyncDownloadsFunction::RunImpl() { | |
| 356 return DownloadsFunctionInterface::RunImplImpl(this); | |
| 357 } | |
| 358 | |
| 359 DownloadsFunctionInterface::DownloadsFunctionName | |
| 360 SyncDownloadsFunction::function() const { | |
| 361 return function_; | |
| 362 } | |
| 363 | |
| 364 DownloadItem* SyncDownloadsFunction::GetActiveItem(int download_id) { | |
| 365 return GetActiveItemInternal(profile(), include_incognito(), download_id); | |
| 366 } | |
| 367 | |
| 368 AsyncDownloadsFunction::AsyncDownloadsFunction( | |
| 369 DownloadsFunctionInterface::DownloadsFunctionName function) | |
| 370 : function_(function) { | |
| 371 } | |
| 372 | |
| 373 AsyncDownloadsFunction::~AsyncDownloadsFunction() {} | |
| 374 | |
| 375 bool AsyncDownloadsFunction::RunImpl() { | |
| 376 return DownloadsFunctionInterface::RunImplImpl(this); | |
| 377 } | |
| 378 | |
| 379 DownloadsFunctionInterface::DownloadsFunctionName | |
| 380 AsyncDownloadsFunction::function() const { | |
| 381 return function_; | |
| 382 } | |
| 383 | |
| 384 DownloadItem* AsyncDownloadsFunction::GetActiveItem(int download_id) { | |
| 385 return GetActiveItemInternal(profile(), include_incognito(), download_id); | |
| 386 } | |
| 387 | |
| 388 DownloadsDownloadFunction::DownloadsDownloadFunction() | |
| 389 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DOWNLOAD) { | |
| 390 } | |
| 391 | |
| 392 DownloadsDownloadFunction::~DownloadsDownloadFunction() {} | |
| 393 | |
| 394 DownloadsDownloadFunction::IOData::IOData() | |
| 395 : save_as(false), | |
| 396 extra_headers(NULL), | |
| 397 method("GET"), | |
| 398 rdh(NULL), | |
| 399 resource_context(NULL), | |
| 400 render_process_host_id(0), | |
| 401 render_view_host_routing_id(0) { | |
| 402 } | |
| 403 | |
| 404 DownloadsDownloadFunction::IOData::~IOData() {} | |
| 405 | |
| 406 bool DownloadsDownloadFunction::ParseArgs() { | |
| 407 base::DictionaryValue* options = NULL; | |
| 408 std::string url; | |
| 409 iodata_.reset(new IOData()); | |
| 410 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options)); | |
| 411 EXTENSION_FUNCTION_VALIDATE(options->GetString(kUrlKey, &url)); | |
| 412 iodata_->url = GURL(url); | |
| 413 if (!iodata_->url.is_valid()) { | |
| 414 error_ = download_extension_errors::kInvalidURLError; | |
| 415 return false; | |
| 416 } | |
| 417 | |
| 418 if (!iodata_->url.SchemeIs("data") && | |
| 419 iodata_->url.GetOrigin() != GetExtension()->url().GetOrigin() && | |
| 420 !GetExtension()->HasHostPermission(iodata_->url)) { | |
| 421 error_ = download_extension_errors::kInvalidURLError; | |
| 422 return false; | |
| 423 } | |
| 424 | |
| 425 if (options->HasKey(kFilenameKey)) { | |
| 426 EXTENSION_FUNCTION_VALIDATE(options->GetString( | |
| 427 kFilenameKey, &iodata_->filename)); | |
| 428 if (!ValidateFilename(iodata_->filename)) { | |
| 429 error_ = download_extension_errors::kGenericError; | |
| 430 return false; | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 if (options->HasKey(kSaveAsKey)) { | |
| 435 EXTENSION_FUNCTION_VALIDATE(options->GetBoolean( | |
| 436 kSaveAsKey, &iodata_->save_as)); | |
| 437 } | |
| 438 | |
| 439 if (options->HasKey(kMethodKey)) { | |
| 440 EXTENSION_FUNCTION_VALIDATE(options->GetString( | |
| 441 kMethodKey, &iodata_->method)); | |
| 442 } | |
| 443 | |
| 444 // It's ok to use a pointer to extra_headers without DeepCopy()ing because | |
| 445 // |args_| (which owns *extra_headers) is guaranteed to live as long as | |
| 446 // |this|. | |
| 447 if (options->HasKey(kHeadersKey)) { | |
| 448 EXTENSION_FUNCTION_VALIDATE(options->GetList( | |
| 449 kHeadersKey, &iodata_->extra_headers)); | |
| 450 } | |
| 451 | |
| 452 if (options->HasKey(kBodyKey)) { | |
| 453 EXTENSION_FUNCTION_VALIDATE(options->GetString( | |
| 454 kBodyKey, &iodata_->post_body)); | |
| 455 } | |
| 456 | |
| 457 if (iodata_->extra_headers != NULL) { | |
| 458 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) { | |
| 459 base::DictionaryValue* header = NULL; | |
| 460 std::string name; | |
| 461 EXTENSION_FUNCTION_VALIDATE(iodata_->extra_headers->GetDictionary( | |
| 462 index, &header)); | |
| 463 EXTENSION_FUNCTION_VALIDATE(header->GetString( | |
| 464 kHeaderNameKey, &name)); | |
| 465 if (header->HasKey(kHeaderBinaryValueKey)) { | |
| 466 base::ListValue* binary_value = NULL; | |
| 467 EXTENSION_FUNCTION_VALIDATE(header->GetList( | |
| 468 kHeaderBinaryValueKey, &binary_value)); | |
| 469 for (size_t char_i = 0; char_i < binary_value->GetSize(); ++char_i) { | |
| 470 int char_value = 0; | |
| 471 EXTENSION_FUNCTION_VALIDATE(binary_value->GetInteger( | |
| 472 char_i, &char_value)); | |
| 473 } | |
| 474 } else if (header->HasKey(kHeaderValueKey)) { | |
| 475 std::string value; | |
| 476 EXTENSION_FUNCTION_VALIDATE(header->GetString( | |
| 477 kHeaderValueKey, &value)); | |
| 478 } | |
| 479 if (!net::HttpUtil::IsSafeHeader(name)) { | |
| 480 error_ = download_extension_errors::kGenericError; | |
| 481 return false; | |
| 482 } | |
| 483 } | |
| 484 } | |
| 485 iodata_->rdh = content::ResourceDispatcherHost::Get(); | |
| 486 iodata_->resource_context = profile()->GetResourceContext(); | |
| 487 iodata_->render_process_host_id = render_view_host()->GetProcess()->GetID(); | |
| 488 iodata_->render_view_host_routing_id = render_view_host()->GetRoutingID(); | |
| 489 return true; | |
| 490 } | |
| 491 | |
| 492 bool DownloadsDownloadFunction::RunInternal() { | |
| 493 VLOG(1) << __FUNCTION__ << " " << iodata_->url.spec(); | |
| 494 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( | |
| 495 &DownloadsDownloadFunction::BeginDownloadOnIOThread, this))) { | |
| 496 error_ = download_extension_errors::kGenericError; | |
| 497 return false; | |
| 498 } | |
| 499 return true; | |
| 500 } | |
| 501 | |
| 502 void DownloadsDownloadFunction::BeginDownloadOnIOThread() { | |
| 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 504 DVLOG(1) << __FUNCTION__ << " " << iodata_->url.spec(); | |
| 505 content::DownloadSaveInfo save_info; | |
| 506 // TODO(benjhayden) Ensure that this filename is interpreted as a path | |
| 507 // relative to the default downloads directory without allowing '..'. | |
| 508 save_info.suggested_name = iodata_->filename; | |
| 509 save_info.prompt_for_save_location = iodata_->save_as; | |
| 510 | |
| 511 scoped_ptr<net::URLRequest> request(new net::URLRequest(iodata_->url, NULL)); | |
| 512 request->set_method(iodata_->method); | |
| 513 if (iodata_->extra_headers != NULL) { | |
| 514 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) { | |
| 515 base::DictionaryValue* header = NULL; | |
| 516 std::string name, value; | |
| 517 CHECK(iodata_->extra_headers->GetDictionary(index, &header)); | |
| 518 CHECK(header->GetString(kHeaderNameKey, &name)); | |
| 519 if (header->HasKey(kHeaderBinaryValueKey)) { | |
| 520 base::ListValue* binary_value = NULL; | |
| 521 CHECK(header->GetList(kHeaderBinaryValueKey, &binary_value)); | |
| 522 for (size_t char_i = 0; char_i < binary_value->GetSize(); ++char_i) { | |
| 523 int char_value = 0; | |
| 524 CHECK(binary_value->GetInteger(char_i, &char_value)); | |
| 525 if ((0 <= char_value) && | |
| 526 (char_value <= 0xff)) { | |
| 527 value.push_back(char_value); | |
| 528 } | |
| 529 } | |
| 530 } else if (header->HasKey(kHeaderValueKey)) { | |
| 531 CHECK(header->GetString(kHeaderValueKey, &value)); | |
| 532 } | |
| 533 request->SetExtraRequestHeaderByName(name, value, false/*overwrite*/); | |
| 534 } | |
| 535 } | |
| 536 if (!iodata_->post_body.empty()) { | |
| 537 request->AppendBytesToUpload(iodata_->post_body.data(), | |
| 538 iodata_->post_body.size()); | |
| 539 } | |
| 540 | |
| 541 // Prevent login prompts for 401/407 responses. | |
| 542 request->set_load_flags(request->load_flags() | | |
| 543 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); | |
| 544 | |
| 545 net::Error error = iodata_->rdh->BeginDownload( | |
| 546 request.Pass(), | |
| 547 false, // is_content_initiated | |
| 548 iodata_->resource_context, | |
| 549 iodata_->render_process_host_id, | |
| 550 iodata_->render_view_host_routing_id, | |
| 551 false, // prefer_cache | |
| 552 save_info, | |
| 553 base::Bind(&DownloadsDownloadFunction::OnStarted, this)); | |
| 554 iodata_.reset(); | |
| 555 | |
| 556 if (error != net::OK) { | |
| 557 BrowserThread::PostTask( | |
| 558 BrowserThread::UI, FROM_HERE, | |
| 559 base::Bind(&DownloadsDownloadFunction::OnStarted, this, | |
| 560 DownloadId::Invalid(), error)); | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 void DownloadsDownloadFunction::OnStarted(DownloadId dl_id, net::Error error) { | |
| 565 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 566 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << error; | |
| 567 if (dl_id.local() >= 0) { | |
| 568 result_.reset(base::Value::CreateIntegerValue(dl_id.local())); | |
| 569 } else { | |
| 570 error_ = net::ErrorToString(error); | |
| 571 } | |
| 572 SendResponse(error_.empty()); | |
| 573 } | |
| 574 | |
| 575 DownloadsSearchFunction::DownloadsSearchFunction() | |
| 576 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_SEARCH), | |
| 577 query_(new DownloadQuery()), | |
| 578 get_id_(0), | |
| 579 has_get_id_(false) { | |
| 580 } | |
| 581 | |
| 582 DownloadsSearchFunction::~DownloadsSearchFunction() {} | |
| 583 | |
| 584 bool DownloadsSearchFunction::ParseArgs() { | |
| 585 static base::LazyInstance<FilterTypeMap> filter_types = | |
| 586 LAZY_INSTANCE_INITIALIZER; | |
| 587 if (filter_types.Get().size() == 0) | |
| 588 InitFilterTypeMap(filter_types.Get()); | |
| 589 | |
| 590 base::DictionaryValue* query_json = NULL; | |
| 591 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json)); | |
| 592 for (base::DictionaryValue::Iterator query_json_field(*query_json); | |
| 593 query_json_field.HasNext(); query_json_field.Advance()) { | |
| 594 FilterTypeMap::const_iterator filter_type = | |
| 595 filter_types.Get().find(query_json_field.key()); | |
| 596 | |
| 597 if (filter_type != filter_types.Get().end()) { | |
| 598 if (!query_->AddFilter(filter_type->second, query_json_field.value())) { | |
| 599 error_ = download_extension_errors::kInvalidFilterError; | |
| 600 return false; | |
| 601 } | |
| 602 } else if (query_json_field.key() == kIdKey) { | |
| 603 EXTENSION_FUNCTION_VALIDATE(query_json_field.value().GetAsInteger( | |
| 604 &get_id_)); | |
| 605 has_get_id_ = true; | |
| 606 } else if (query_json_field.key() == kOrderByKey) { | |
| 607 if (!ParseOrderBy(query_json_field.value())) | |
| 608 return false; | |
| 609 } else if (query_json_field.key() == kDangerKey) { | |
| 610 std::string danger_str; | |
| 611 EXTENSION_FUNCTION_VALIDATE(query_json_field.value().GetAsString( | |
| 612 &danger_str)); | |
| 613 content::DownloadDangerType danger_type = | |
| 614 DangerEnumFromString(danger_str); | |
| 615 if (danger_type == content::DOWNLOAD_DANGER_TYPE_MAX) { | |
| 616 error_ = download_extension_errors::kInvalidDangerTypeError; | |
| 617 return false; | |
| 618 } | |
| 619 query_->AddFilter(danger_type); | |
| 620 } else if (query_json_field.key() == kStateKey) { | |
| 621 std::string state_str; | |
| 622 EXTENSION_FUNCTION_VALIDATE(query_json_field.value().GetAsString( | |
| 623 &state_str)); | |
| 624 DownloadItem::DownloadState state = StateEnumFromString(state_str); | |
| 625 if (state == DownloadItem::MAX_DOWNLOAD_STATE) { | |
| 626 error_ = download_extension_errors::kInvalidStateError; | |
| 627 return false; | |
| 628 } | |
| 629 query_->AddFilter(state); | |
| 630 } else if (query_json_field.key() == kLimitKey) { | |
| 631 int limit = 0; | |
| 632 EXTENSION_FUNCTION_VALIDATE(query_json_field.value().GetAsInteger( | |
| 633 &limit)); | |
| 634 if (limit < 0) { | |
| 635 error_ = download_extension_errors::kInvalidQueryLimit; | |
| 636 return false; | |
| 637 } | |
| 638 query_->Limit(limit); | |
| 639 } else { | |
| 640 EXTENSION_FUNCTION_VALIDATE(false); | |
| 641 } | |
| 642 } | |
| 643 return true; | |
| 644 } | |
| 645 | |
| 646 bool DownloadsSearchFunction::ParseOrderBy(const base::Value& order_by_value) { | |
| 647 static base::LazyInstance<SortTypeMap> sorter_types = | |
| 648 LAZY_INSTANCE_INITIALIZER; | |
| 649 if (sorter_types.Get().size() == 0) | |
| 650 InitSortTypeMap(sorter_types.Get()); | |
| 651 | |
| 652 std::string order_by_str; | |
| 653 EXTENSION_FUNCTION_VALIDATE(order_by_value.GetAsString(&order_by_str)); | |
| 654 std::vector<std::string> order_by_strs; | |
| 655 base::SplitString(order_by_str, ' ', &order_by_strs); | |
| 656 for (std::vector<std::string>::const_iterator iter = order_by_strs.begin(); | |
| 657 iter != order_by_strs.end(); ++iter) { | |
| 658 std::string term_str = *iter; | |
| 659 if (term_str.empty()) | |
| 660 continue; | |
| 661 DownloadQuery::SortDirection direction = DownloadQuery::ASCENDING; | |
| 662 if (term_str[0] == '-') { | |
| 663 direction = DownloadQuery::DESCENDING; | |
| 664 term_str = term_str.substr(1); | |
| 665 } | |
| 666 SortTypeMap::const_iterator sorter_type = | |
| 667 sorter_types.Get().find(term_str); | |
| 668 if (sorter_type == sorter_types.Get().end()) { | |
| 669 error_ = download_extension_errors::kInvalidOrderByError; | |
| 670 return false; | |
| 671 } | |
| 672 query_->AddSorter(sorter_type->second, direction); | |
| 673 } | |
| 674 return true; | |
| 675 } | |
| 676 | |
| 677 bool DownloadsSearchFunction::RunInternal() { | |
| 678 DownloadManager* manager = NULL; | |
| 679 DownloadManager* incognito_manager = NULL; | |
| 680 GetManagers(profile(), include_incognito(), &manager, &incognito_manager); | |
| 681 DownloadQuery::DownloadVector all_items, cpp_results; | |
| 682 if (has_get_id_) { | |
| 683 DownloadItem* item = manager->GetDownloadItem(get_id_); | |
| 684 if (!item && incognito_manager) | |
| 685 item = incognito_manager->GetDownloadItem(get_id_); | |
| 686 if (item) | |
| 687 all_items.push_back(item); | |
| 688 } else { | |
| 689 manager->GetAllDownloads(FilePath(FILE_PATH_LITERAL("")), &all_items); | |
| 690 if (incognito_manager) | |
| 691 incognito_manager->GetAllDownloads( | |
| 692 FilePath(FILE_PATH_LITERAL("")), &all_items); | |
| 693 } | |
| 694 query_->Search(all_items.begin(), all_items.end(), &cpp_results); | |
| 695 base::ListValue* json_results = new base::ListValue(); | |
| 696 for (DownloadManager::DownloadVector::const_iterator it = cpp_results.begin(); | |
| 697 it != cpp_results.end(); ++it) { | |
| 698 scoped_ptr<base::DictionaryValue> item(DownloadItemToJSON(*it)); | |
| 699 json_results->Append(item.release()); | |
| 700 } | |
| 701 result_.reset(json_results); | |
| 702 return true; | |
| 703 } | |
| 704 | |
| 705 DownloadsPauseFunction::DownloadsPauseFunction() | |
| 706 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_PAUSE), | |
| 707 download_id_(DownloadId::Invalid().local()) { | |
| 708 } | |
| 709 | |
| 710 DownloadsPauseFunction::~DownloadsPauseFunction() {} | |
| 711 | |
| 712 bool DownloadsPauseFunction::ParseArgs() { | |
| 713 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_)); | |
| 714 return true; | |
| 715 } | |
| 716 | |
| 717 bool DownloadsPauseFunction::RunInternal() { | |
| 718 DownloadItem* download_item = GetActiveItem(download_id_); | |
| 719 if ((download_item == NULL) || !download_item->IsInProgress()) { | |
| 720 // This could be due to an invalid download ID, or it could be due to the | |
| 721 // download not being currently active. | |
| 722 error_ = download_extension_errors::kInvalidOperationError; | |
| 723 } else if (!download_item->IsPaused()) { | |
| 724 // If download_item->IsPaused() already then we treat it as a success. | |
| 725 download_item->TogglePause(); | |
| 726 } | |
| 727 return error_.empty(); | |
| 728 } | |
| 729 | |
| 730 DownloadsResumeFunction::DownloadsResumeFunction() | |
| 731 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_RESUME), | |
| 732 download_id_(DownloadId::Invalid().local()) { | |
| 733 } | |
| 734 | |
| 735 DownloadsResumeFunction::~DownloadsResumeFunction() {} | |
| 736 | |
| 737 bool DownloadsResumeFunction::ParseArgs() { | |
| 738 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_)); | |
| 739 return true; | |
| 740 } | |
| 741 | |
| 742 bool DownloadsResumeFunction::RunInternal() { | |
| 743 DownloadItem* download_item = GetActiveItem(download_id_); | |
| 744 if (download_item == NULL) { | |
| 745 // This could be due to an invalid download ID, or it could be due to the | |
| 746 // download not being currently active. | |
| 747 error_ = download_extension_errors::kInvalidOperationError; | |
| 748 } else if (download_item->IsPaused()) { | |
| 749 // If !download_item->IsPaused() already, then we treat it as a success. | |
| 750 download_item->TogglePause(); | |
| 751 } | |
| 752 return error_.empty(); | |
| 753 } | |
| 754 | |
| 755 DownloadsCancelFunction::DownloadsCancelFunction() | |
| 756 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_CANCEL), | |
| 757 download_id_(DownloadId::Invalid().local()) { | |
| 758 } | |
| 759 | |
| 760 DownloadsCancelFunction::~DownloadsCancelFunction() {} | |
| 761 | |
| 762 bool DownloadsCancelFunction::ParseArgs() { | |
| 763 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_)); | |
| 764 return true; | |
| 765 } | |
| 766 | |
| 767 bool DownloadsCancelFunction::RunInternal() { | |
| 768 DownloadItem* download_item = GetActiveItem(download_id_); | |
| 769 if (download_item != NULL) | |
| 770 download_item->Cancel(true); | |
| 771 // |download_item| can be NULL if the download ID was invalid or if the | |
| 772 // download is not currently active. Either way, we don't consider it a | |
| 773 // failure. | |
| 774 return error_.empty(); | |
| 775 } | |
| 776 | |
| 777 DownloadsEraseFunction::DownloadsEraseFunction() | |
| 778 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ERASE) { | |
| 779 } | |
| 780 | |
| 781 DownloadsEraseFunction::~DownloadsEraseFunction() {} | |
| 782 | |
| 783 bool DownloadsEraseFunction::ParseArgs() { | |
| 784 base::DictionaryValue* query_json = NULL; | |
| 785 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json)); | |
| 786 error_ = download_extension_errors::kNotImplementedError; | |
| 787 return false; | |
| 788 } | |
| 789 | |
| 790 bool DownloadsEraseFunction::RunInternal() { | |
| 791 NOTIMPLEMENTED(); | |
| 792 return false; | |
| 793 } | |
| 794 | |
| 795 DownloadsSetDestinationFunction::DownloadsSetDestinationFunction() | |
| 796 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SET_DESTINATION) { | |
| 797 } | |
| 798 | |
| 799 DownloadsSetDestinationFunction::~DownloadsSetDestinationFunction() {} | |
| 800 | |
| 801 bool DownloadsSetDestinationFunction::ParseArgs() { | |
| 802 int dl_id = 0; | |
| 803 std::string path; | |
| 804 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id)); | |
| 805 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &path)); | |
| 806 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << &path; | |
| 807 error_ = download_extension_errors::kNotImplementedError; | |
| 808 return false; | |
| 809 } | |
| 810 | |
| 811 bool DownloadsSetDestinationFunction::RunInternal() { | |
| 812 NOTIMPLEMENTED(); | |
| 813 return false; | |
| 814 } | |
| 815 | |
| 816 DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() | |
| 817 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ACCEPT_DANGER) { | |
| 818 } | |
| 819 | |
| 820 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {} | |
| 821 | |
| 822 bool DownloadsAcceptDangerFunction::ParseArgs() { | |
| 823 int dl_id = 0; | |
| 824 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id)); | |
| 825 VLOG(1) << __FUNCTION__ << " " << dl_id; | |
| 826 error_ = download_extension_errors::kNotImplementedError; | |
| 827 return false; | |
| 828 } | |
| 829 | |
| 830 bool DownloadsAcceptDangerFunction::RunInternal() { | |
| 831 NOTIMPLEMENTED(); | |
| 832 return false; | |
| 833 } | |
| 834 | |
| 835 DownloadsShowFunction::DownloadsShowFunction() | |
| 836 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SHOW) { | |
| 837 } | |
| 838 | |
| 839 DownloadsShowFunction::~DownloadsShowFunction() {} | |
| 840 | |
| 841 bool DownloadsShowFunction::ParseArgs() { | |
| 842 int dl_id = 0; | |
| 843 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id)); | |
| 844 VLOG(1) << __FUNCTION__ << " " << dl_id; | |
| 845 error_ = download_extension_errors::kNotImplementedError; | |
| 846 return false; | |
| 847 } | |
| 848 | |
| 849 bool DownloadsShowFunction::RunInternal() { | |
| 850 NOTIMPLEMENTED(); | |
| 851 return false; | |
| 852 } | |
| 853 | |
| 854 DownloadsDragFunction::DownloadsDragFunction() | |
| 855 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DRAG) { | |
| 856 } | |
| 857 | |
| 858 DownloadsDragFunction::~DownloadsDragFunction() {} | |
| 859 | |
| 860 bool DownloadsDragFunction::ParseArgs() { | |
| 861 int dl_id = 0; | |
| 862 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id)); | |
| 863 VLOG(1) << __FUNCTION__ << " " << dl_id; | |
| 864 error_ = download_extension_errors::kNotImplementedError; | |
| 865 return false; | |
| 866 } | |
| 867 | |
| 868 bool DownloadsDragFunction::RunInternal() { | |
| 869 NOTIMPLEMENTED(); | |
| 870 return false; | |
| 871 } | |
| 872 | |
| 873 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction() | |
| 874 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_GET_FILE_ICON), | |
| 875 icon_size_(kDefaultIconSize), | |
| 876 icon_extractor_(new DownloadFileIconExtractorImpl()) { | |
| 877 } | |
| 878 | |
| 879 DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {} | |
| 880 | |
| 881 void DownloadsGetFileIconFunction::SetIconExtractorForTesting( | |
| 882 DownloadFileIconExtractor* extractor) { | |
| 883 DCHECK(extractor); | |
| 884 icon_extractor_.reset(extractor); | |
| 885 } | |
| 886 | |
| 887 bool DownloadsGetFileIconFunction::ParseArgs() { | |
| 888 int dl_id = 0; | |
| 889 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id)); | |
| 890 | |
| 891 base::DictionaryValue* options = NULL; | |
| 892 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options)); | |
| 893 if (options->HasKey(kSizeKey)) { | |
| 894 EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSizeKey, &icon_size_)); | |
| 895 // We only support 16px and 32px icons. This is enforced in | |
| 896 // experimental.downloads.json. | |
| 897 DCHECK(icon_size_ == 16 || icon_size_ == 32); | |
| 898 } | |
| 899 | |
| 900 DownloadManager* manager = NULL; | |
| 901 DownloadManager* incognito_manager = NULL; | |
| 902 GetManagers(profile(), include_incognito(), &manager, &incognito_manager); | |
| 903 DownloadItem* download_item = manager->GetDownloadItem(dl_id); | |
| 904 if (!download_item && incognito_manager) | |
| 905 download_item = incognito_manager->GetDownloadItem(dl_id); | |
| 906 if (!download_item) { | |
| 907 // The DownloadItem is is added to history when the path is determined. If | |
| 908 // the download is not in history, then we don't have a path / final | |
| 909 // filename and no icon. | |
| 910 error_ = download_extension_errors::kInvalidOperationError; | |
| 911 return false; | |
| 912 } | |
| 913 // In-progress downloads return the intermediate filename for GetFullPath() | |
| 914 // which doesn't have the final extension. Therefore we won't be able to | |
| 915 // derive a good file icon for it. So we use GetTargetFilePath() instead. | |
| 916 path_ = download_item->GetTargetFilePath(); | |
| 917 DCHECK(!path_.empty()); | |
| 918 return true; | |
| 919 } | |
| 920 | |
| 921 bool DownloadsGetFileIconFunction::RunInternal() { | |
| 922 DCHECK(!path_.empty()); | |
| 923 DCHECK(icon_extractor_.get()); | |
| 924 EXTENSION_FUNCTION_VALIDATE(icon_extractor_->ExtractIconURLForPath( | |
| 925 path_, IconLoaderSizeFromPixelSize(icon_size_), | |
| 926 base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted, this))); | |
| 927 return true; | |
| 928 } | |
| 929 | |
| 930 void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string& url) { | |
| 931 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 932 if (url.empty()) | |
| 933 error_ = download_extension_errors::kIconNotFoundError; | |
| 934 else | |
| 935 result_.reset(base::Value::CreateStringValue(url)); | |
| 936 SendResponse(error_.empty()); | |
| 937 } | |
| 938 | |
| 939 ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(Profile* profile) | |
| 940 : profile_(profile), | |
| 941 manager_(NULL), | |
| 942 delete_item_jsons_(&item_jsons_), | |
| 943 delete_on_changed_stats_(&on_changed_stats_) { | |
| 944 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 945 DCHECK(profile_); | |
| 946 // Register a callback with the DownloadService for this profile to be called | |
| 947 // when it creates the DownloadManager, or now if the manager already exists. | |
| 948 DownloadServiceFactory::GetForProfile(profile)->OnManagerCreated(base::Bind( | |
| 949 &ExtensionDownloadsEventRouter::Init, base::Unretained(this))); | |
| 950 } | |
| 951 | |
| 952 // The only public methods on this class are ModelChanged() and | |
| 953 // ManagerGoingDown(), and they are only called by DownloadManager, so | |
| 954 // there's no way for any methods on this class to be called before | |
| 955 // DownloadService calls Init() via the OnManagerCreated Callback above. | |
| 956 void ExtensionDownloadsEventRouter::Init(DownloadManager* manager) { | |
| 957 DCHECK(manager_ == NULL); | |
| 958 manager_ = manager; | |
| 959 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 960 manager_->AddObserver(this); | |
| 961 } | |
| 962 | |
| 963 ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() { | |
| 964 if (manager_ != NULL) | |
| 965 manager_->RemoveObserver(this); | |
| 966 for (ItemMap::const_iterator iter = downloads_.begin(); | |
| 967 iter != downloads_.end(); ++iter) { | |
| 968 if (iter->second != NULL) | |
| 969 iter->second->RemoveObserver(this); | |
| 970 } | |
| 971 } | |
| 972 | |
| 973 ExtensionDownloadsEventRouter::OnChangedStat::OnChangedStat() | |
| 974 : fires(0), | |
| 975 total(0) { | |
| 976 } | |
| 977 | |
| 978 ExtensionDownloadsEventRouter::OnChangedStat::~OnChangedStat() { | |
| 979 if (total > 0) | |
| 980 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged", (fires * 100 / total)); | |
| 981 } | |
| 982 | |
| 983 void ExtensionDownloadsEventRouter::OnDownloadUpdated(DownloadItem* item) { | |
| 984 int download_id = item->GetId(); | |
| 985 if (item->GetState() == DownloadItem::REMOVING) { | |
| 986 // The REMOVING state indicates that this item is being erased. | |
| 987 // Let's unregister as an observer so that we don't see any more updates | |
| 988 // from it, dispatch the onErased event, and remove its json and is | |
| 989 // OnChangedStat from our maps. | |
| 990 downloads_.erase(download_id); | |
| 991 item->RemoveObserver(this); | |
| 992 DispatchEvent(extension_event_names::kOnDownloadErased, | |
| 993 base::Value::CreateIntegerValue(download_id)); | |
| 994 delete item_jsons_[download_id]; | |
| 995 item_jsons_.erase(download_id); | |
| 996 delete on_changed_stats_[download_id]; | |
| 997 on_changed_stats_.erase(download_id); | |
| 998 return; | |
| 999 } | |
| 1000 | |
| 1001 base::DictionaryValue* old_json = item_jsons_[download_id]; | |
| 1002 scoped_ptr<base::DictionaryValue> new_json(DownloadItemToJSON(item)); | |
| 1003 scoped_ptr<base::DictionaryValue> delta(new base::DictionaryValue()); | |
| 1004 delta->SetInteger(kIdKey, download_id); | |
| 1005 std::set<std::string> new_fields; | |
| 1006 bool changed = false; | |
| 1007 | |
| 1008 // For each field in the new json representation of the item except the | |
| 1009 // bytesReceived field, if the field has changed from the previous old json, | |
| 1010 // set the differences in the |delta| object and remember that something | |
| 1011 // significant changed. | |
| 1012 for (base::DictionaryValue::Iterator iter(*new_json.get()); | |
| 1013 iter.HasNext(); iter.Advance()) { | |
| 1014 new_fields.insert(iter.key()); | |
| 1015 if (iter.key() != kBytesReceivedKey) { | |
| 1016 base::Value* old_value = NULL; | |
| 1017 if (!old_json->HasKey(iter.key()) || | |
| 1018 (old_json->Get(iter.key(), &old_value) && | |
| 1019 !iter.value().Equals(old_value))) { | |
| 1020 delta->Set(iter.key() + ".new", iter.value().DeepCopy()); | |
| 1021 if (old_value) | |
| 1022 delta->Set(iter.key() + ".old", old_value->DeepCopy()); | |
| 1023 changed = true; | |
| 1024 } | |
| 1025 } | |
| 1026 } | |
| 1027 | |
| 1028 // If a field was in the previous json but is not in the new json, set the | |
| 1029 // difference in |delta|. | |
| 1030 for (base::DictionaryValue::Iterator iter(*old_json); | |
| 1031 iter.HasNext(); iter.Advance()) { | |
| 1032 if (new_fields.find(iter.key()) == new_fields.end()) { | |
| 1033 delta->Set(iter.key() + ".old", iter.value().DeepCopy()); | |
| 1034 changed = true; | |
| 1035 } | |
| 1036 } | |
| 1037 | |
| 1038 // Update the OnChangedStat and dispatch the event if something significant | |
| 1039 // changed. Replace the stored json with the new json. | |
| 1040 ++(on_changed_stats_[download_id]->total); | |
| 1041 if (changed) { | |
| 1042 DispatchEvent(extension_event_names::kOnDownloadChanged, delta.release()); | |
| 1043 ++(on_changed_stats_[download_id]->fires); | |
| 1044 } | |
| 1045 item_jsons_[download_id]->Swap(new_json.get()); | |
| 1046 } | |
| 1047 | |
| 1048 void ExtensionDownloadsEventRouter::OnDownloadOpened(DownloadItem* item) { | |
| 1049 } | |
| 1050 | |
| 1051 void ExtensionDownloadsEventRouter::ModelChanged(DownloadManager* manager) { | |
| 1052 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1053 DCHECK(manager_ == manager); | |
| 1054 typedef std::set<int> DownloadIdSet; | |
| 1055 | |
| 1056 // Get all the download items. | |
| 1057 DownloadManager::DownloadVector current_vec; | |
| 1058 manager_->SearchDownloads(string16(), ¤t_vec); | |
| 1059 | |
| 1060 // Populate set<>s of download item identifiers so that we can find | |
| 1061 // differences between the old and the new set of download items. | |
| 1062 DownloadIdSet current_set, prev_set; | |
| 1063 for (ItemMap::const_iterator iter = downloads_.begin(); | |
| 1064 iter != downloads_.end(); ++iter) { | |
| 1065 prev_set.insert(iter->first); | |
| 1066 } | |
| 1067 ItemMap current_map; | |
| 1068 for (DownloadManager::DownloadVector::const_iterator iter = | |
| 1069 current_vec.begin(); | |
| 1070 iter != current_vec.end(); ++iter) { | |
| 1071 DownloadItem* item = *iter; | |
| 1072 int item_id = item->GetId(); | |
| 1073 CHECK(item_id >= 0); | |
| 1074 DCHECK(current_map.find(item_id) == current_map.end()); | |
| 1075 current_set.insert(item_id); | |
| 1076 current_map[item_id] = item; | |
| 1077 } | |
| 1078 DownloadIdSet new_set; // current_set - prev_set; | |
| 1079 std::set_difference(current_set.begin(), current_set.end(), | |
| 1080 prev_set.begin(), prev_set.end(), | |
| 1081 std::insert_iterator<DownloadIdSet>( | |
| 1082 new_set, new_set.begin())); | |
| 1083 | |
| 1084 // For each download that was not in the old set of downloads but is in the | |
| 1085 // new set of downloads, fire an onCreated event, register as an Observer of | |
| 1086 // the item, store a json representation of the item so that we can easily | |
| 1087 // find changes in that json representation, and make an OnChangedStat. | |
| 1088 for (DownloadIdSet::const_iterator iter = new_set.begin(); | |
| 1089 iter != new_set.end(); ++iter) { | |
| 1090 scoped_ptr<base::DictionaryValue> item( | |
| 1091 DownloadItemToJSON(current_map[*iter])); | |
| 1092 DispatchEvent(extension_event_names::kOnDownloadCreated, item->DeepCopy()); | |
| 1093 DCHECK(item_jsons_.find(*iter) == item_jsons_.end()); | |
| 1094 on_changed_stats_[*iter] = new OnChangedStat(); | |
| 1095 current_map[*iter]->AddObserver(this); | |
| 1096 item_jsons_[*iter] = item.release(); | |
| 1097 } | |
| 1098 downloads_.swap(current_map); | |
| 1099 | |
| 1100 // Dispatching onErased is handled in OnDownloadUpdated when an item | |
| 1101 // transitions to the REMOVING state. | |
| 1102 } | |
| 1103 | |
| 1104 void ExtensionDownloadsEventRouter::ManagerGoingDown( | |
| 1105 DownloadManager* manager) { | |
| 1106 manager_->RemoveObserver(this); | |
| 1107 manager_ = NULL; | |
| 1108 } | |
| 1109 | |
| 1110 void ExtensionDownloadsEventRouter::DispatchEvent( | |
| 1111 const char* event_name, base::Value* arg) { | |
| 1112 ListValue args; | |
| 1113 args.Append(arg); | |
| 1114 std::string json_args; | |
| 1115 base::JSONWriter::Write(&args, &json_args); | |
| 1116 profile_->GetExtensionEventRouter()->DispatchEventToRenderers( | |
| 1117 event_name, | |
| 1118 json_args, | |
| 1119 profile_, | |
| 1120 GURL()); | |
| 1121 } | |
| OLD | NEW |