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