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.
5 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/callback.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/json/json_writer.h"
17 #include "base/lazy_instance.h"
18 #include "base/location.h"
19 #include "base/logging.h"
20 #include "base/memory/weak_ptr.h"
21 #include "base/metrics/histogram.h"
22 #include "base/single_thread_task_runner.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_split.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/task/cancelable_task_tracker.h"
29 #include "base/thread_task_runner_handle.h"
30 #include "base/values.h"
31 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/download/download_danger_prompt.h"
33 #include "chrome/browser/download/download_file_icon_extractor.h"
34 #include "chrome/browser/download/download_prefs.h"
35 #include "chrome/browser/download/download_query.h"
36 #include "chrome/browser/download/download_service.h"
37 #include "chrome/browser/download/download_service_factory.h"
38 #include "chrome/browser/download/download_shelf.h"
39 #include "chrome/browser/download/download_stats.h"
40 #include "chrome/browser/download/drag_download_item.h"
41 #include "chrome/browser/icon_loader.h"
42 #include "chrome/browser/icon_manager.h"
43 #include "chrome/browser/platform_util.h"
44 #include "chrome/browser/profiles/profile.h"
45 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
46 #include "chrome/browser/ui/browser.h"
47 #include "chrome/browser/ui/browser_list.h"
48 #include "chrome/browser/ui/browser_window.h"
49 #include "chrome/common/extensions/api/downloads.h"
50 #include "components/web_modal/web_contents_modal_dialog_manager.h"
51 #include "content/public/browser/download_interrupt_reasons.h"
52 #include "content/public/browser/download_item.h"
53 #include "content/public/browser/download_save_info.h"
54 #include "content/public/browser/download_url_parameters.h"
55 #include "content/public/browser/notification_details.h"
56 #include "content/public/browser/notification_service.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/render_frame_host.h"
59 #include "content/public/browser/render_process_host.h"
60 #include "content/public/browser/render_view_host.h"
61 #include "content/public/browser/render_widget_host_view.h"
62 #include "content/public/browser/resource_context.h"
63 #include "content/public/browser/resource_dispatcher_host.h"
64 #include "content/public/browser/web_contents.h"
65 #include "extensions/browser/event_router.h"
66 #include "extensions/browser/extension_function_dispatcher.h"
67 #include "extensions/browser/extension_prefs.h"
68 #include "extensions/browser/extension_registry.h"
69 #include "extensions/browser/notification_types.h"
70 #include "extensions/browser/warning_service.h"
71 #include "extensions/common/permissions/permissions_data.h"
72 #include "net/base/filename_util.h"
73 #include "net/base/load_flags.h"
74 #include "net/http/http_util.h"
75 #include "third_party/skia/include/core/SkBitmap.h"
76 #include "ui/base/webui/web_ui_util.h"
77 #include "ui/gfx/image/image_skia.h"
79 using content::BrowserContext
;
80 using content::BrowserThread
;
81 using content::DownloadItem
;
82 using content::DownloadManager
;
84 namespace download_extension_errors
{
86 const char kEmptyFile
[] = "Filename not yet determined";
87 const char kFileAlreadyDeleted
[] = "Download file already deleted";
88 const char kFileNotRemoved
[] = "Unable to remove file";
89 const char kIconNotFound
[] = "Icon not found";
90 const char kInvalidDangerType
[] = "Invalid danger type";
91 const char kInvalidFilename
[] = "Invalid filename";
92 const char kInvalidFilter
[] = "Invalid query filter";
93 const char kInvalidHeaderName
[] = "Invalid request header name";
94 const char kInvalidHeaderUnsafe
[] = "Unsafe request header name";
95 const char kInvalidHeaderValue
[] = "Invalid request header value";
96 const char kInvalidId
[] = "Invalid downloadId";
97 const char kInvalidOrderBy
[] = "Invalid orderBy field";
98 const char kInvalidQueryLimit
[] = "Invalid query limit";
99 const char kInvalidState
[] = "Invalid state";
100 const char kInvalidURL
[] = "Invalid URL";
101 const char kInvisibleContext
[] = "Javascript execution context is not visible "
102 "(tab, window, popup bubble)";
103 const char kNotComplete
[] = "Download must be complete";
104 const char kNotDangerous
[] = "Download must be dangerous";
105 const char kNotInProgress
[] = "Download must be in progress";
106 const char kNotResumable
[] = "DownloadItem.canResume must be true";
107 const char kOpenPermission
[] = "The \"downloads.open\" permission is required";
108 const char kShelfDisabled
[] = "Another extension has disabled the shelf";
109 const char kShelfPermission
[] = "downloads.setShelfEnabled requires the "
110 "\"downloads.shelf\" permission";
111 const char kTooManyListeners
[] = "Each extension may have at most one "
112 "onDeterminingFilename listener between all of its renderer execution "
114 const char kUnexpectedDeterminer
[] = "Unexpected determineFilename call";
115 const char kUserGesture
[] = "User gesture required";
117 } // namespace download_extension_errors
119 namespace errors
= download_extension_errors
;
121 namespace extensions
{
125 namespace downloads
= api::downloads
;
127 // Default icon size for getFileIcon() in pixels.
128 const int kDefaultIconSize
= 32;
131 const char kByExtensionIdKey
[] = "byExtensionId";
132 const char kByExtensionNameKey
[] = "byExtensionName";
133 const char kBytesReceivedKey
[] = "bytesReceived";
134 const char kCanResumeKey
[] = "canResume";
135 const char kDangerAccepted
[] = "accepted";
136 const char kDangerContent
[] = "content";
137 const char kDangerFile
[] = "file";
138 const char kDangerHost
[] = "host";
139 const char kDangerKey
[] = "danger";
140 const char kDangerSafe
[] = "safe";
141 const char kDangerUncommon
[] = "uncommon";
142 const char kDangerUnwanted
[] = "unwanted";
143 const char kDangerUrl
[] = "url";
144 const char kEndTimeKey
[] = "endTime";
145 const char kEndedAfterKey
[] = "endedAfter";
146 const char kEndedBeforeKey
[] = "endedBefore";
147 const char kErrorKey
[] = "error";
148 const char kEstimatedEndTimeKey
[] = "estimatedEndTime";
149 const char kExistsKey
[] = "exists";
150 const char kFileSizeKey
[] = "fileSize";
151 const char kFilenameKey
[] = "filename";
152 const char kFilenameRegexKey
[] = "filenameRegex";
153 const char kIdKey
[] = "id";
154 const char kIncognitoKey
[] = "incognito";
155 const char kMimeKey
[] = "mime";
156 const char kPausedKey
[] = "paused";
157 const char kQueryKey
[] = "query";
158 const char kReferrerUrlKey
[] = "referrer";
159 const char kStartTimeKey
[] = "startTime";
160 const char kStartedAfterKey
[] = "startedAfter";
161 const char kStartedBeforeKey
[] = "startedBefore";
162 const char kStateComplete
[] = "complete";
163 const char kStateInProgress
[] = "in_progress";
164 const char kStateInterrupted
[] = "interrupted";
165 const char kStateKey
[] = "state";
166 const char kTotalBytesGreaterKey
[] = "totalBytesGreater";
167 const char kTotalBytesKey
[] = "totalBytes";
168 const char kTotalBytesLessKey
[] = "totalBytesLess";
169 const char kUrlKey
[] = "url";
170 const char kUrlRegexKey
[] = "urlRegex";
172 // Note: Any change to the danger type strings, should be accompanied by a
173 // corresponding change to downloads.json.
174 const char* const kDangerStrings
[] = {
185 static_assert(arraysize(kDangerStrings
) == content::DOWNLOAD_DANGER_TYPE_MAX
,
186 "kDangerStrings should have DOWNLOAD_DANGER_TYPE_MAX elements");
188 // Note: Any change to the state strings, should be accompanied by a
189 // corresponding change to downloads.json.
190 const char* const kStateStrings
[] = {
196 static_assert(arraysize(kStateStrings
) == DownloadItem::MAX_DOWNLOAD_STATE
,
197 "kStateStrings should have MAX_DOWNLOAD_STATE elements");
199 const char* DangerString(content::DownloadDangerType danger
) {
201 DCHECK(danger
< static_cast<content::DownloadDangerType
>(
202 arraysize(kDangerStrings
)));
203 if (danger
< 0 || danger
>= static_cast<content::DownloadDangerType
>(
204 arraysize(kDangerStrings
)))
206 return kDangerStrings
[danger
];
209 content::DownloadDangerType
DangerEnumFromString(const std::string
& danger
) {
210 for (size_t i
= 0; i
< arraysize(kDangerStrings
); ++i
) {
211 if (danger
== kDangerStrings
[i
])
212 return static_cast<content::DownloadDangerType
>(i
);
214 return content::DOWNLOAD_DANGER_TYPE_MAX
;
217 const char* StateString(DownloadItem::DownloadState state
) {
219 DCHECK(state
< static_cast<DownloadItem::DownloadState
>(
220 arraysize(kStateStrings
)));
221 if (state
< 0 || state
>= static_cast<DownloadItem::DownloadState
>(
222 arraysize(kStateStrings
)))
224 return kStateStrings
[state
];
227 DownloadItem::DownloadState
StateEnumFromString(const std::string
& state
) {
228 for (size_t i
= 0; i
< arraysize(kStateStrings
); ++i
) {
229 if ((kStateStrings
[i
] != NULL
) && (state
== kStateStrings
[i
]))
230 return static_cast<DownloadItem::DownloadState
>(i
);
232 return DownloadItem::MAX_DOWNLOAD_STATE
;
235 std::string
TimeToISO8601(const base::Time
& t
) {
236 base::Time::Exploded exploded
;
237 t
.UTCExplode(&exploded
);
238 return base::StringPrintf(
239 "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded
.year
, exploded
.month
,
240 exploded
.day_of_month
, exploded
.hour
, exploded
.minute
, exploded
.second
,
241 exploded
.millisecond
);
244 scoped_ptr
<base::DictionaryValue
> DownloadItemToJSON(
245 DownloadItem
* download_item
,
247 base::DictionaryValue
* json
= new base::DictionaryValue();
248 json
->SetBoolean(kExistsKey
, !download_item
->GetFileExternallyRemoved());
249 json
->SetInteger(kIdKey
, download_item
->GetId());
250 const GURL
& url
= download_item
->GetOriginalUrl();
251 json
->SetString(kUrlKey
, (url
.is_valid() ? url
.spec() : std::string()));
252 const GURL
& referrer
= download_item
->GetReferrerUrl();
253 json
->SetString(kReferrerUrlKey
, (referrer
.is_valid() ? referrer
.spec()
255 json
->SetString(kFilenameKey
,
256 download_item
->GetTargetFilePath().LossyDisplayName());
257 json
->SetString(kDangerKey
, DangerString(download_item
->GetDangerType()));
258 json
->SetString(kStateKey
, StateString(download_item
->GetState()));
259 json
->SetBoolean(kCanResumeKey
, download_item
->CanResume());
260 json
->SetBoolean(kPausedKey
, download_item
->IsPaused());
261 json
->SetString(kMimeKey
, download_item
->GetMimeType());
262 json
->SetString(kStartTimeKey
, TimeToISO8601(download_item
->GetStartTime()));
263 json
->SetDouble(kBytesReceivedKey
, download_item
->GetReceivedBytes());
264 json
->SetDouble(kTotalBytesKey
, download_item
->GetTotalBytes());
265 json
->SetBoolean(kIncognitoKey
, profile
->IsOffTheRecord());
266 if (download_item
->GetState() == DownloadItem::INTERRUPTED
) {
267 json
->SetString(kErrorKey
,
268 content::DownloadInterruptReasonToString(
269 download_item
->GetLastReason()));
270 } else if (download_item
->GetState() == DownloadItem::CANCELLED
) {
271 json
->SetString(kErrorKey
,
272 content::DownloadInterruptReasonToString(
273 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
));
275 if (!download_item
->GetEndTime().is_null())
276 json
->SetString(kEndTimeKey
, TimeToISO8601(download_item
->GetEndTime()));
277 base::TimeDelta time_remaining
;
278 if (download_item
->TimeRemaining(&time_remaining
)) {
279 base::Time now
= base::Time::Now();
280 json
->SetString(kEstimatedEndTimeKey
, TimeToISO8601(now
+ time_remaining
));
282 DownloadedByExtension
* by_ext
= DownloadedByExtension::Get(download_item
);
284 json
->SetString(kByExtensionIdKey
, by_ext
->id());
285 json
->SetString(kByExtensionNameKey
, by_ext
->name());
286 // Lookup the extension's current name() in case the user changed their
287 // language. This won't work if the extension was uninstalled, so the name
288 // might be the wrong language.
289 const Extension
* extension
=
290 ExtensionRegistry::Get(profile
)
291 ->GetExtensionById(by_ext
->id(), ExtensionRegistry::EVERYTHING
);
293 json
->SetString(kByExtensionNameKey
, extension
->name());
295 // TODO(benjhayden): Implement fileSize.
296 json
->SetDouble(kFileSizeKey
, download_item
->GetTotalBytes());
297 return scoped_ptr
<base::DictionaryValue
>(json
);
300 class DownloadFileIconExtractorImpl
: public DownloadFileIconExtractor
{
302 DownloadFileIconExtractorImpl() {}
304 ~DownloadFileIconExtractorImpl() override
{}
306 bool ExtractIconURLForPath(const base::FilePath
& path
,
308 IconLoader::IconSize icon_size
,
309 IconURLCallback callback
) override
;
312 void OnIconLoadComplete(
313 float scale
, const IconURLCallback
& callback
, gfx::Image
* icon
);
315 base::CancelableTaskTracker cancelable_task_tracker_
;
318 bool DownloadFileIconExtractorImpl::ExtractIconURLForPath(
319 const base::FilePath
& path
,
321 IconLoader::IconSize icon_size
,
322 IconURLCallback callback
) {
323 IconManager
* im
= g_browser_process
->icon_manager();
324 // The contents of the file at |path| may have changed since a previous
325 // request, in which case the associated icon may also have changed.
326 // Therefore, always call LoadIcon instead of attempting a LookupIcon.
329 base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete
,
330 base::Unretained(this), scale
, callback
),
331 &cancelable_task_tracker_
);
335 void DownloadFileIconExtractorImpl::OnIconLoadComplete(
336 float scale
, const IconURLCallback
& callback
, gfx::Image
* icon
) {
337 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
338 callback
.Run(!icon
? std::string() : webui::GetBitmapDataUrl(
339 icon
->ToImageSkia()->GetRepresentation(scale
).sk_bitmap()));
342 IconLoader::IconSize
IconLoaderSizeFromPixelSize(int pixel_size
) {
343 switch (pixel_size
) {
344 case 16: return IconLoader::SMALL
;
345 case 32: return IconLoader::NORMAL
;
348 return IconLoader::NORMAL
;
352 typedef base::hash_map
<std::string
, DownloadQuery::FilterType
> FilterTypeMap
;
354 void InitFilterTypeMap(FilterTypeMap
* filter_types_ptr
) {
355 FilterTypeMap
& filter_types
= *filter_types_ptr
;
356 filter_types
[kBytesReceivedKey
] = DownloadQuery::FILTER_BYTES_RECEIVED
;
357 filter_types
[kExistsKey
] = DownloadQuery::FILTER_EXISTS
;
358 filter_types
[kFilenameKey
] = DownloadQuery::FILTER_FILENAME
;
359 filter_types
[kFilenameRegexKey
] = DownloadQuery::FILTER_FILENAME_REGEX
;
360 filter_types
[kMimeKey
] = DownloadQuery::FILTER_MIME
;
361 filter_types
[kPausedKey
] = DownloadQuery::FILTER_PAUSED
;
362 filter_types
[kQueryKey
] = DownloadQuery::FILTER_QUERY
;
363 filter_types
[kEndedAfterKey
] = DownloadQuery::FILTER_ENDED_AFTER
;
364 filter_types
[kEndedBeforeKey
] = DownloadQuery::FILTER_ENDED_BEFORE
;
365 filter_types
[kEndTimeKey
] = DownloadQuery::FILTER_END_TIME
;
366 filter_types
[kStartedAfterKey
] = DownloadQuery::FILTER_STARTED_AFTER
;
367 filter_types
[kStartedBeforeKey
] = DownloadQuery::FILTER_STARTED_BEFORE
;
368 filter_types
[kStartTimeKey
] = DownloadQuery::FILTER_START_TIME
;
369 filter_types
[kTotalBytesKey
] = DownloadQuery::FILTER_TOTAL_BYTES
;
370 filter_types
[kTotalBytesGreaterKey
] =
371 DownloadQuery::FILTER_TOTAL_BYTES_GREATER
;
372 filter_types
[kTotalBytesLessKey
] = DownloadQuery::FILTER_TOTAL_BYTES_LESS
;
373 filter_types
[kUrlKey
] = DownloadQuery::FILTER_URL
;
374 filter_types
[kUrlRegexKey
] = DownloadQuery::FILTER_URL_REGEX
;
377 typedef base::hash_map
<std::string
, DownloadQuery::SortType
> SortTypeMap
;
379 void InitSortTypeMap(SortTypeMap
* sorter_types_ptr
) {
380 SortTypeMap
& sorter_types
= *sorter_types_ptr
;
381 sorter_types
[kBytesReceivedKey
] = DownloadQuery::SORT_BYTES_RECEIVED
;
382 sorter_types
[kDangerKey
] = DownloadQuery::SORT_DANGER
;
383 sorter_types
[kEndTimeKey
] = DownloadQuery::SORT_END_TIME
;
384 sorter_types
[kExistsKey
] = DownloadQuery::SORT_EXISTS
;
385 sorter_types
[kFilenameKey
] = DownloadQuery::SORT_FILENAME
;
386 sorter_types
[kMimeKey
] = DownloadQuery::SORT_MIME
;
387 sorter_types
[kPausedKey
] = DownloadQuery::SORT_PAUSED
;
388 sorter_types
[kStartTimeKey
] = DownloadQuery::SORT_START_TIME
;
389 sorter_types
[kStateKey
] = DownloadQuery::SORT_STATE
;
390 sorter_types
[kTotalBytesKey
] = DownloadQuery::SORT_TOTAL_BYTES
;
391 sorter_types
[kUrlKey
] = DownloadQuery::SORT_URL
;
394 bool IsNotTemporaryDownloadFilter(const DownloadItem
& download_item
) {
395 return !download_item
.IsTemporary();
398 // Set |manager| to the on-record DownloadManager, and |incognito_manager| to
399 // the off-record DownloadManager if one exists and is requested via
400 // |include_incognito|. This should work regardless of whether |profile| is
401 // original or incognito.
404 bool include_incognito
,
405 DownloadManager
** manager
,
406 DownloadManager
** incognito_manager
) {
407 *manager
= BrowserContext::GetDownloadManager(profile
->GetOriginalProfile());
408 if (profile
->HasOffTheRecordProfile() &&
409 (include_incognito
||
410 profile
->IsOffTheRecord())) {
411 *incognito_manager
= BrowserContext::GetDownloadManager(
412 profile
->GetOffTheRecordProfile());
414 *incognito_manager
= NULL
;
418 DownloadItem
* GetDownload(Profile
* profile
, bool include_incognito
, int id
) {
419 DownloadManager
* manager
= NULL
;
420 DownloadManager
* incognito_manager
= NULL
;
421 GetManagers(profile
, include_incognito
, &manager
, &incognito_manager
);
422 DownloadItem
* download_item
= manager
->GetDownload(id
);
423 if (!download_item
&& incognito_manager
)
424 download_item
= incognito_manager
->GetDownload(id
);
425 return download_item
;
428 enum DownloadsFunctionName
{
429 DOWNLOADS_FUNCTION_DOWNLOAD
= 0,
430 DOWNLOADS_FUNCTION_SEARCH
= 1,
431 DOWNLOADS_FUNCTION_PAUSE
= 2,
432 DOWNLOADS_FUNCTION_RESUME
= 3,
433 DOWNLOADS_FUNCTION_CANCEL
= 4,
434 DOWNLOADS_FUNCTION_ERASE
= 5,
436 DOWNLOADS_FUNCTION_ACCEPT_DANGER
= 7,
437 DOWNLOADS_FUNCTION_SHOW
= 8,
438 DOWNLOADS_FUNCTION_DRAG
= 9,
439 DOWNLOADS_FUNCTION_GET_FILE_ICON
= 10,
440 DOWNLOADS_FUNCTION_OPEN
= 11,
441 DOWNLOADS_FUNCTION_REMOVE_FILE
= 12,
442 DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER
= 13,
443 DOWNLOADS_FUNCTION_SET_SHELF_ENABLED
= 14,
444 DOWNLOADS_FUNCTION_DETERMINE_FILENAME
= 15,
445 // Insert new values here, not at the beginning.
446 DOWNLOADS_FUNCTION_LAST
449 void RecordApiFunctions(DownloadsFunctionName function
) {
450 UMA_HISTOGRAM_ENUMERATION("Download.ApiFunctions",
452 DOWNLOADS_FUNCTION_LAST
);
455 void CompileDownloadQueryOrderBy(
456 const std::vector
<std::string
>& order_by_strs
,
458 DownloadQuery
* query
) {
459 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
461 static base::LazyInstance
<SortTypeMap
> sorter_types
=
462 LAZY_INSTANCE_INITIALIZER
;
463 if (sorter_types
.Get().empty())
464 InitSortTypeMap(sorter_types
.Pointer());
466 for (std::vector
<std::string
>::const_iterator iter
= order_by_strs
.begin();
467 iter
!= order_by_strs
.end(); ++iter
) {
468 std::string term_str
= *iter
;
469 if (term_str
.empty())
471 DownloadQuery::SortDirection direction
= DownloadQuery::ASCENDING
;
472 if (term_str
[0] == '-') {
473 direction
= DownloadQuery::DESCENDING
;
474 term_str
= term_str
.substr(1);
476 SortTypeMap::const_iterator sorter_type
=
477 sorter_types
.Get().find(term_str
);
478 if (sorter_type
== sorter_types
.Get().end()) {
479 *error
= errors::kInvalidOrderBy
;
482 query
->AddSorter(sorter_type
->second
, direction
);
486 void RunDownloadQuery(
487 const downloads::DownloadQuery
& query_in
,
488 DownloadManager
* manager
,
489 DownloadManager
* incognito_manager
,
491 DownloadQuery::DownloadVector
* results
) {
492 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
494 static base::LazyInstance
<FilterTypeMap
> filter_types
=
495 LAZY_INSTANCE_INITIALIZER
;
496 if (filter_types
.Get().empty())
497 InitFilterTypeMap(filter_types
.Pointer());
499 DownloadQuery query_out
;
502 if (query_in
.limit
.get()) {
503 if (*query_in
.limit
.get() < 0) {
504 *error
= errors::kInvalidQueryLimit
;
507 limit
= *query_in
.limit
.get();
510 query_out
.Limit(limit
);
513 std::string state_string
= downloads::ToString(query_in
.state
);
514 if (!state_string
.empty()) {
515 DownloadItem::DownloadState state
= StateEnumFromString(state_string
);
516 if (state
== DownloadItem::MAX_DOWNLOAD_STATE
) {
517 *error
= errors::kInvalidState
;
520 query_out
.AddFilter(state
);
522 std::string danger_string
=
523 downloads::ToString(query_in
.danger
);
524 if (!danger_string
.empty()) {
525 content::DownloadDangerType danger_type
= DangerEnumFromString(
527 if (danger_type
== content::DOWNLOAD_DANGER_TYPE_MAX
) {
528 *error
= errors::kInvalidDangerType
;
531 query_out
.AddFilter(danger_type
);
533 if (query_in
.order_by
.get()) {
534 CompileDownloadQueryOrderBy(*query_in
.order_by
.get(), error
, &query_out
);
539 scoped_ptr
<base::DictionaryValue
> query_in_value(query_in
.ToValue().Pass());
540 for (base::DictionaryValue::Iterator
query_json_field(*query_in_value
.get());
541 !query_json_field
.IsAtEnd(); query_json_field
.Advance()) {
542 FilterTypeMap::const_iterator filter_type
=
543 filter_types
.Get().find(query_json_field
.key());
544 if (filter_type
!= filter_types
.Get().end()) {
545 if (!query_out
.AddFilter(filter_type
->second
, query_json_field
.value())) {
546 *error
= errors::kInvalidFilter
;
552 DownloadQuery::DownloadVector all_items
;
553 if (query_in
.id
.get()) {
554 DownloadItem
* download_item
= manager
->GetDownload(*query_in
.id
.get());
555 if (!download_item
&& incognito_manager
)
556 download_item
= incognito_manager
->GetDownload(*query_in
.id
.get());
558 all_items
.push_back(download_item
);
560 manager
->GetAllDownloads(&all_items
);
561 if (incognito_manager
)
562 incognito_manager
->GetAllDownloads(&all_items
);
564 query_out
.AddFilter(base::Bind(&IsNotTemporaryDownloadFilter
));
565 query_out
.Search(all_items
.begin(), all_items
.end(), results
);
568 DownloadPathReservationTracker::FilenameConflictAction
ConvertConflictAction(
569 downloads::FilenameConflictAction action
) {
571 case downloads::FILENAME_CONFLICT_ACTION_NONE
:
572 case downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
:
573 return DownloadPathReservationTracker::UNIQUIFY
;
574 case downloads::FILENAME_CONFLICT_ACTION_OVERWRITE
:
575 return DownloadPathReservationTracker::OVERWRITE
;
576 case downloads::FILENAME_CONFLICT_ACTION_PROMPT
:
577 return DownloadPathReservationTracker::PROMPT
;
580 return DownloadPathReservationTracker::UNIQUIFY
;
583 class ExtensionDownloadsEventRouterData
: public base::SupportsUserData::Data
{
585 static ExtensionDownloadsEventRouterData
* Get(DownloadItem
* download_item
) {
586 base::SupportsUserData::Data
* data
= download_item
->GetUserData(kKey
);
587 return (data
== NULL
) ? NULL
:
588 static_cast<ExtensionDownloadsEventRouterData
*>(data
);
591 static void Remove(DownloadItem
* download_item
) {
592 download_item
->RemoveUserData(kKey
);
595 explicit ExtensionDownloadsEventRouterData(
596 DownloadItem
* download_item
,
597 scoped_ptr
<base::DictionaryValue
> json_item
)
600 json_(json_item
.Pass()),
601 creator_conflict_action_(
602 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
),
603 determined_conflict_action_(
604 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
) {
605 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
606 download_item
->SetUserData(kKey
, this);
609 ~ExtensionDownloadsEventRouterData() override
{
611 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged",
612 (changed_fired_
* 100 / updated_
));
616 const base::DictionaryValue
& json() const { return *json_
.get(); }
617 void set_json(scoped_ptr
<base::DictionaryValue
> json_item
) {
618 json_
= json_item
.Pass();
621 void OnItemUpdated() { ++updated_
; }
622 void OnChangedFired() { ++changed_fired_
; }
624 static void SetDetermineFilenameTimeoutSecondsForTesting(int s
) {
625 determine_filename_timeout_s_
= s
;
628 void BeginFilenameDetermination(
629 const base::Closure
& no_change
,
630 const ExtensionDownloadsEventRouter::FilenameChangedCallback
& change
) {
631 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
632 ClearPendingDeterminers();
633 filename_no_change_
= no_change
;
634 filename_change_
= change
;
635 determined_filename_
= creator_suggested_filename_
;
636 determined_conflict_action_
= creator_conflict_action_
;
637 // determiner_.install_time should default to 0 so that creator suggestions
638 // should be lower priority than any actual onDeterminingFilename listeners.
640 // Ensure that the callback is called within a time limit.
641 weak_ptr_factory_
.reset(
642 new base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
>(this));
643 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
645 base::Bind(&ExtensionDownloadsEventRouterData::DetermineFilenameTimeout
,
646 weak_ptr_factory_
->GetWeakPtr()),
647 base::TimeDelta::FromSeconds(determine_filename_timeout_s_
));
650 void DetermineFilenameTimeout() {
651 CallFilenameCallback();
654 void ClearPendingDeterminers() {
655 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
656 determined_filename_
.clear();
657 determined_conflict_action_
=
658 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
;
659 determiner_
= DeterminerInfo();
660 filename_no_change_
= base::Closure();
661 filename_change_
= ExtensionDownloadsEventRouter::FilenameChangedCallback();
662 weak_ptr_factory_
.reset();
663 determiners_
.clear();
666 void DeterminerRemoved(const std::string
& extension_id
) {
667 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
668 for (DeterminerInfoVector::iterator iter
= determiners_
.begin();
669 iter
!= determiners_
.end();) {
670 if (iter
->extension_id
== extension_id
) {
671 iter
= determiners_
.erase(iter
);
676 // If we just removed the last unreported determiner, then we need to call a
678 CheckAllDeterminersCalled();
681 void AddPendingDeterminer(const std::string
& extension_id
,
682 const base::Time
& installed
) {
683 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
684 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
685 if (determiners_
[index
].extension_id
== extension_id
) {
686 DCHECK(false) << extension_id
;
690 determiners_
.push_back(DeterminerInfo(extension_id
, installed
));
693 bool DeterminerAlreadyReported(const std::string
& extension_id
) {
694 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
695 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
696 if (determiners_
[index
].extension_id
== extension_id
) {
697 return determiners_
[index
].reported
;
703 void CreatorSuggestedFilename(
704 const base::FilePath
& filename
,
705 downloads::FilenameConflictAction conflict_action
) {
706 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
707 creator_suggested_filename_
= filename
;
708 creator_conflict_action_
= conflict_action
;
711 base::FilePath
creator_suggested_filename() const {
712 return creator_suggested_filename_
;
715 downloads::FilenameConflictAction
716 creator_conflict_action() const {
717 return creator_conflict_action_
;
720 void ResetCreatorSuggestion() {
721 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
722 creator_suggested_filename_
.clear();
723 creator_conflict_action_
=
724 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
;
727 // Returns false if this |extension_id| was not expected or if this
728 // |extension_id| has already reported. The caller is responsible for
729 // validating |filename|.
730 bool DeterminerCallback(
732 const std::string
& extension_id
,
733 const base::FilePath
& filename
,
734 downloads::FilenameConflictAction conflict_action
) {
735 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
736 bool found_info
= false;
737 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
738 if (determiners_
[index
].extension_id
== extension_id
) {
740 if (determiners_
[index
].reported
)
742 determiners_
[index
].reported
= true;
743 // Do not use filename if another determiner has already overridden the
744 // filename and they take precedence. Extensions that were installed
745 // later take precedence over previous extensions.
746 if (!filename
.empty() ||
747 (conflict_action
!= downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
749 std::string winner_extension_id
;
750 ExtensionDownloadsEventRouter::DetermineFilenameInternal(
753 determiners_
[index
].extension_id
,
754 determiners_
[index
].install_time
,
755 determiner_
.extension_id
,
756 determiner_
.install_time
,
757 &winner_extension_id
,
758 &determined_filename_
,
759 &determined_conflict_action_
,
761 if (!warnings
.empty())
762 WarningService::NotifyWarningsOnUI(profile
, warnings
);
763 if (winner_extension_id
== determiners_
[index
].extension_id
)
764 determiner_
= determiners_
[index
];
771 CheckAllDeterminersCalled();
776 static int determine_filename_timeout_s_
;
778 struct DeterminerInfo
{
780 DeterminerInfo(const std::string
& e_id
,
781 const base::Time
& installed
);
784 std::string extension_id
;
785 base::Time install_time
;
788 typedef std::vector
<DeterminerInfo
> DeterminerInfoVector
;
790 static const char kKey
[];
792 // This is safe to call even while not waiting for determiners to call back;
793 // in that case, the callbacks will be null so they won't be Run.
794 void CheckAllDeterminersCalled() {
795 for (DeterminerInfoVector::iterator iter
= determiners_
.begin();
796 iter
!= determiners_
.end(); ++iter
) {
800 CallFilenameCallback();
802 // Don't clear determiners_ immediately in case there's a second listener
803 // for one of the extensions, so that DetermineFilename can return
804 // kTooManyListeners. After a few seconds, DetermineFilename will return
805 // kUnexpectedDeterminer instead of kTooManyListeners so that determiners_
806 // doesn't keep hogging memory.
807 weak_ptr_factory_
.reset(
808 new base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
>(this));
809 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
811 base::Bind(&ExtensionDownloadsEventRouterData::ClearPendingDeterminers
,
812 weak_ptr_factory_
->GetWeakPtr()),
813 base::TimeDelta::FromSeconds(15));
816 void CallFilenameCallback() {
817 if (determined_filename_
.empty() &&
818 (determined_conflict_action_
==
819 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
820 if (!filename_no_change_
.is_null())
821 filename_no_change_
.Run();
823 if (!filename_change_
.is_null()) {
824 filename_change_
.Run(determined_filename_
, ConvertConflictAction(
825 determined_conflict_action_
));
828 // Clear the callbacks immediately in case they aren't idempotent.
829 filename_no_change_
= base::Closure();
830 filename_change_
= ExtensionDownloadsEventRouter::FilenameChangedCallback();
836 scoped_ptr
<base::DictionaryValue
> json_
;
838 base::Closure filename_no_change_
;
839 ExtensionDownloadsEventRouter::FilenameChangedCallback filename_change_
;
841 DeterminerInfoVector determiners_
;
843 base::FilePath creator_suggested_filename_
;
844 downloads::FilenameConflictAction
845 creator_conflict_action_
;
846 base::FilePath determined_filename_
;
847 downloads::FilenameConflictAction
848 determined_conflict_action_
;
849 DeterminerInfo determiner_
;
851 scoped_ptr
<base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
> >
854 DISALLOW_COPY_AND_ASSIGN(ExtensionDownloadsEventRouterData
);
857 int ExtensionDownloadsEventRouterData::determine_filename_timeout_s_
= 15;
859 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo(
860 const std::string
& e_id
,
861 const base::Time
& installed
)
862 : extension_id(e_id
),
863 install_time(installed
),
867 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo()
871 ExtensionDownloadsEventRouterData::DeterminerInfo::~DeterminerInfo() {}
873 const char ExtensionDownloadsEventRouterData::kKey
[] =
874 "DownloadItem ExtensionDownloadsEventRouterData";
876 bool OnDeterminingFilenameWillDispatchCallback(
877 bool* any_determiners
,
878 ExtensionDownloadsEventRouterData
* data
,
879 content::BrowserContext
* context
,
880 const Extension
* extension
,
881 base::ListValue
* event_args
,
882 const base::DictionaryValue
* listener_filter
) {
883 *any_determiners
= true;
884 base::Time installed
=
885 ExtensionPrefs::Get(context
)->GetInstallTime(extension
->id());
886 data
->AddPendingDeterminer(extension
->id(), installed
);
890 bool Fault(bool error
,
891 const char* message_in
,
892 std::string
* message_out
) {
895 *message_out
= message_in
;
899 bool InvalidId(DownloadItem
* valid_item
, std::string
* message_out
) {
900 return Fault(!valid_item
, errors::kInvalidId
, message_out
);
903 bool IsDownloadDeltaField(const std::string
& field
) {
904 return ((field
== kUrlKey
) ||
905 (field
== kFilenameKey
) ||
906 (field
== kDangerKey
) ||
907 (field
== kMimeKey
) ||
908 (field
== kStartTimeKey
) ||
909 (field
== kEndTimeKey
) ||
910 (field
== kStateKey
) ||
911 (field
== kCanResumeKey
) ||
912 (field
== kPausedKey
) ||
913 (field
== kErrorKey
) ||
914 (field
== kTotalBytesKey
) ||
915 (field
== kFileSizeKey
) ||
916 (field
== kExistsKey
));
921 const char DownloadedByExtension::kKey
[] =
922 "DownloadItem DownloadedByExtension";
924 DownloadedByExtension
* DownloadedByExtension::Get(
925 content::DownloadItem
* item
) {
926 base::SupportsUserData::Data
* data
= item
->GetUserData(kKey
);
927 return (data
== NULL
) ? NULL
:
928 static_cast<DownloadedByExtension
*>(data
);
931 DownloadedByExtension::DownloadedByExtension(
932 content::DownloadItem
* item
,
933 const std::string
& id
,
934 const std::string
& name
)
937 item
->SetUserData(kKey
, this);
940 DownloadsDownloadFunction::DownloadsDownloadFunction() {}
942 DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
944 bool DownloadsDownloadFunction::RunAsync() {
945 scoped_ptr
<downloads::Download::Params
> params(
946 downloads::Download::Params::Create(*args_
));
947 EXTENSION_FUNCTION_VALIDATE(params
.get());
948 const downloads::DownloadOptions
& options
= params
->options
;
949 GURL
download_url(options
.url
);
950 if (Fault(!download_url
.is_valid(), errors::kInvalidURL
, &error_
))
953 Profile
* current_profile
= GetProfile();
954 if (include_incognito() && GetProfile()->HasOffTheRecordProfile())
955 current_profile
= GetProfile()->GetOffTheRecordProfile();
957 scoped_ptr
<content::DownloadUrlParameters
> download_params(
958 new content::DownloadUrlParameters(
959 download_url
, render_frame_host()->GetProcess()->GetID(),
960 render_view_host_do_not_use()->GetRoutingID(),
961 current_profile
->GetResourceContext()));
963 base::FilePath creator_suggested_filename
;
964 if (options
.filename
.get()) {
966 // Can't get filename16 from options.ToValue() because that converts it from
968 base::DictionaryValue
* options_value
= NULL
;
969 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(0, &options_value
));
970 base::string16 filename16
;
971 EXTENSION_FUNCTION_VALIDATE(options_value
->GetString(
972 kFilenameKey
, &filename16
));
973 creator_suggested_filename
= base::FilePath(filename16
);
974 #elif defined(OS_POSIX)
975 creator_suggested_filename
= base::FilePath(*options
.filename
.get());
977 if (!net::IsSafePortableRelativePath(creator_suggested_filename
)) {
978 error_
= errors::kInvalidFilename
;
983 if (options
.save_as
.get())
984 download_params
->set_prompt(*options
.save_as
.get());
986 if (options
.headers
.get()) {
987 typedef downloads::HeaderNameValuePair HeaderNameValuePair
;
988 for (std::vector
<linked_ptr
<HeaderNameValuePair
> >::const_iterator iter
=
989 options
.headers
->begin();
990 iter
!= options
.headers
->end();
992 const HeaderNameValuePair
& name_value
= **iter
;
993 if (!net::HttpUtil::IsValidHeaderName(name_value
.name
)) {
994 error_
= errors::kInvalidHeaderName
;
997 if (!net::HttpUtil::IsSafeHeader(name_value
.name
)) {
998 error_
= errors::kInvalidHeaderUnsafe
;
1001 if (!net::HttpUtil::IsValidHeaderValue(name_value
.value
)) {
1002 error_
= errors::kInvalidHeaderValue
;
1005 download_params
->add_request_header(name_value
.name
, name_value
.value
);
1009 std::string method_string
=
1010 downloads::ToString(options
.method
);
1011 if (!method_string
.empty())
1012 download_params
->set_method(method_string
);
1013 if (options
.body
.get())
1014 download_params
->set_post_body(*options
.body
.get());
1015 download_params
->set_callback(base::Bind(
1016 &DownloadsDownloadFunction::OnStarted
, this,
1017 creator_suggested_filename
, options
.conflict_action
));
1018 // Prevent login prompts for 401/407 responses.
1019 download_params
->set_do_not_prompt_for_login(true);
1021 DownloadManager
* manager
= BrowserContext::GetDownloadManager(
1023 manager
->DownloadUrl(download_params
.Pass());
1024 RecordDownloadSource(DOWNLOAD_INITIATED_BY_EXTENSION
);
1025 RecordApiFunctions(DOWNLOADS_FUNCTION_DOWNLOAD
);
1029 void DownloadsDownloadFunction::OnStarted(
1030 const base::FilePath
& creator_suggested_filename
,
1031 downloads::FilenameConflictAction creator_conflict_action
,
1033 content::DownloadInterruptReason interrupt_reason
) {
1034 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1035 VLOG(1) << __FUNCTION__
<< " " << item
<< " " << interrupt_reason
;
1037 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1038 SetResult(new base::FundamentalValue(static_cast<int>(item
->GetId())));
1039 if (!creator_suggested_filename
.empty() ||
1040 (creator_conflict_action
!=
1041 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
1042 ExtensionDownloadsEventRouterData
* data
=
1043 ExtensionDownloadsEventRouterData::Get(item
);
1045 data
= new ExtensionDownloadsEventRouterData(
1047 scoped_ptr
<base::DictionaryValue
>(new base::DictionaryValue()));
1049 data
->CreatorSuggestedFilename(
1050 creator_suggested_filename
, creator_conflict_action
);
1052 new DownloadedByExtension(item
, extension()->id(), extension()->name());
1053 item
->UpdateObservers();
1055 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1056 error_
= content::DownloadInterruptReasonToString(interrupt_reason
);
1058 SendResponse(error_
.empty());
1061 DownloadsSearchFunction::DownloadsSearchFunction() {}
1063 DownloadsSearchFunction::~DownloadsSearchFunction() {}
1065 bool DownloadsSearchFunction::RunSync() {
1066 scoped_ptr
<downloads::Search::Params
> params(
1067 downloads::Search::Params::Create(*args_
));
1068 EXTENSION_FUNCTION_VALIDATE(params
.get());
1069 DownloadManager
* manager
= NULL
;
1070 DownloadManager
* incognito_manager
= NULL
;
1071 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1072 ExtensionDownloadsEventRouter
* router
=
1073 DownloadServiceFactory::GetForBrowserContext(
1074 manager
->GetBrowserContext())->GetExtensionEventRouter();
1075 router
->CheckForHistoryFilesRemoval();
1076 if (incognito_manager
) {
1077 ExtensionDownloadsEventRouter
* incognito_router
=
1078 DownloadServiceFactory::GetForBrowserContext(
1079 incognito_manager
->GetBrowserContext())->GetExtensionEventRouter();
1080 incognito_router
->CheckForHistoryFilesRemoval();
1082 DownloadQuery::DownloadVector results
;
1083 RunDownloadQuery(params
->query
,
1088 if (!error_
.empty())
1091 base::ListValue
* json_results
= new base::ListValue();
1092 for (DownloadManager::DownloadVector::const_iterator it
= results
.begin();
1093 it
!= results
.end(); ++it
) {
1094 DownloadItem
* download_item
= *it
;
1095 uint32 download_id
= download_item
->GetId();
1096 bool off_record
= ((incognito_manager
!= NULL
) &&
1097 (incognito_manager
->GetDownload(download_id
) != NULL
));
1098 scoped_ptr
<base::DictionaryValue
> json_item(
1099 DownloadItemToJSON(*it
,
1100 off_record
? GetProfile()->GetOffTheRecordProfile()
1101 : GetProfile()->GetOriginalProfile()));
1102 json_results
->Append(json_item
.release());
1104 SetResult(json_results
);
1105 RecordApiFunctions(DOWNLOADS_FUNCTION_SEARCH
);
1109 DownloadsPauseFunction::DownloadsPauseFunction() {}
1111 DownloadsPauseFunction::~DownloadsPauseFunction() {}
1113 bool DownloadsPauseFunction::RunSync() {
1114 scoped_ptr
<downloads::Pause::Params
> params(
1115 downloads::Pause::Params::Create(*args_
));
1116 EXTENSION_FUNCTION_VALIDATE(params
.get());
1117 DownloadItem
* download_item
=
1118 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1119 if (InvalidId(download_item
, &error_
) ||
1120 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1121 errors::kNotInProgress
, &error_
))
1123 // If the item is already paused, this is a no-op and the operation will
1124 // silently succeed.
1125 download_item
->Pause();
1126 RecordApiFunctions(DOWNLOADS_FUNCTION_PAUSE
);
1130 DownloadsResumeFunction::DownloadsResumeFunction() {}
1132 DownloadsResumeFunction::~DownloadsResumeFunction() {}
1134 bool DownloadsResumeFunction::RunSync() {
1135 scoped_ptr
<downloads::Resume::Params
> params(
1136 downloads::Resume::Params::Create(*args_
));
1137 EXTENSION_FUNCTION_VALIDATE(params
.get());
1138 DownloadItem
* download_item
=
1139 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1140 if (InvalidId(download_item
, &error_
) ||
1141 Fault(download_item
->IsPaused() && !download_item
->CanResume(),
1142 errors::kNotResumable
, &error_
))
1144 // Note that if the item isn't paused, this will be a no-op, and the extension
1145 // call will seem successful.
1146 download_item
->Resume();
1147 RecordApiFunctions(DOWNLOADS_FUNCTION_RESUME
);
1151 DownloadsCancelFunction::DownloadsCancelFunction() {}
1153 DownloadsCancelFunction::~DownloadsCancelFunction() {}
1155 bool DownloadsCancelFunction::RunSync() {
1156 scoped_ptr
<downloads::Resume::Params
> params(
1157 downloads::Resume::Params::Create(*args_
));
1158 EXTENSION_FUNCTION_VALIDATE(params
.get());
1159 DownloadItem
* download_item
=
1160 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1161 if (download_item
&&
1162 (download_item
->GetState() == DownloadItem::IN_PROGRESS
))
1163 download_item
->Cancel(true);
1164 // |download_item| can be NULL if the download ID was invalid or if the
1165 // download is not currently active. Either way, it's not a failure.
1166 RecordApiFunctions(DOWNLOADS_FUNCTION_CANCEL
);
1170 DownloadsEraseFunction::DownloadsEraseFunction() {}
1172 DownloadsEraseFunction::~DownloadsEraseFunction() {}
1174 bool DownloadsEraseFunction::RunSync() {
1175 scoped_ptr
<downloads::Erase::Params
> params(
1176 downloads::Erase::Params::Create(*args_
));
1177 EXTENSION_FUNCTION_VALIDATE(params
.get());
1178 DownloadManager
* manager
= NULL
;
1179 DownloadManager
* incognito_manager
= NULL
;
1180 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1181 DownloadQuery::DownloadVector results
;
1182 RunDownloadQuery(params
->query
,
1187 if (!error_
.empty())
1189 base::ListValue
* json_results
= new base::ListValue();
1190 for (DownloadManager::DownloadVector::const_iterator it
= results
.begin();
1191 it
!= results
.end(); ++it
) {
1192 json_results
->Append(
1193 new base::FundamentalValue(static_cast<int>((*it
)->GetId())));
1196 SetResult(json_results
);
1197 RecordApiFunctions(DOWNLOADS_FUNCTION_ERASE
);
1201 DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {
1204 DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() {
1207 bool DownloadsRemoveFileFunction::RunAsync() {
1208 scoped_ptr
<downloads::RemoveFile::Params
> params(
1209 downloads::RemoveFile::Params::Create(*args_
));
1210 EXTENSION_FUNCTION_VALIDATE(params
.get());
1211 DownloadItem
* download_item
=
1212 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1213 if (InvalidId(download_item
, &error_
) ||
1214 Fault((download_item
->GetState() != DownloadItem::COMPLETE
),
1215 errors::kNotComplete
, &error_
) ||
1216 Fault(download_item
->GetFileExternallyRemoved(),
1217 errors::kFileAlreadyDeleted
, &error_
))
1219 RecordApiFunctions(DOWNLOADS_FUNCTION_REMOVE_FILE
);
1220 download_item
->DeleteFile(
1221 base::Bind(&DownloadsRemoveFileFunction::Done
, this));
1225 void DownloadsRemoveFileFunction::Done(bool success
) {
1226 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1228 error_
= errors::kFileNotRemoved
;
1230 SendResponse(error_
.empty());
1233 DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {}
1235 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
1237 DownloadsAcceptDangerFunction::OnPromptCreatedCallback
*
1238 DownloadsAcceptDangerFunction::on_prompt_created_
= NULL
;
1240 bool DownloadsAcceptDangerFunction::RunAsync() {
1241 scoped_ptr
<downloads::AcceptDanger::Params
> params(
1242 downloads::AcceptDanger::Params::Create(*args_
));
1243 EXTENSION_FUNCTION_VALIDATE(params
.get());
1244 PromptOrWait(params
->download_id
, 10);
1248 void DownloadsAcceptDangerFunction::PromptOrWait(int download_id
, int retries
) {
1249 DownloadItem
* download_item
=
1250 GetDownload(GetProfile(), include_incognito(), download_id
);
1251 content::WebContents
* web_contents
= dispatcher()->GetVisibleWebContents();
1252 if (InvalidId(download_item
, &error_
) ||
1253 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1254 errors::kNotInProgress
, &error_
) ||
1255 Fault(!download_item
->IsDangerous(), errors::kNotDangerous
, &error_
) ||
1256 Fault(!web_contents
, errors::kInvisibleContext
, &error_
)) {
1257 SendResponse(error_
.empty());
1260 bool visible
= platform_util::IsVisible(web_contents
->GetNativeView());
1263 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1264 FROM_HERE
, base::Bind(&DownloadsAcceptDangerFunction::PromptOrWait
,
1265 this, download_id
, retries
- 1),
1266 base::TimeDelta::FromMilliseconds(100));
1269 error_
= errors::kInvisibleContext
;
1270 SendResponse(error_
.empty());
1273 RecordApiFunctions(DOWNLOADS_FUNCTION_ACCEPT_DANGER
);
1274 // DownloadDangerPrompt displays a modal dialog using native widgets that the
1275 // user must either accept or cancel. It cannot be scripted.
1276 DownloadDangerPrompt
* prompt
= DownloadDangerPrompt::Create(
1280 base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback
,
1281 this, download_id
));
1282 // DownloadDangerPrompt deletes itself
1283 if (on_prompt_created_
&& !on_prompt_created_
->is_null())
1284 on_prompt_created_
->Run(prompt
);
1285 SendResponse(error_
.empty());
1288 void DownloadsAcceptDangerFunction::DangerPromptCallback(
1289 int download_id
, DownloadDangerPrompt::Action action
) {
1290 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1291 DownloadItem
* download_item
=
1292 GetDownload(GetProfile(), include_incognito(), download_id
);
1293 if (InvalidId(download_item
, &error_
) ||
1294 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1295 errors::kNotInProgress
, &error_
))
1298 case DownloadDangerPrompt::ACCEPT
:
1299 download_item
->ValidateDangerousDownload();
1301 case DownloadDangerPrompt::CANCEL
:
1302 download_item
->Remove();
1304 case DownloadDangerPrompt::DISMISS
:
1307 SendResponse(error_
.empty());
1310 DownloadsShowFunction::DownloadsShowFunction() {}
1312 DownloadsShowFunction::~DownloadsShowFunction() {}
1314 bool DownloadsShowFunction::RunAsync() {
1315 scoped_ptr
<downloads::Show::Params
> params(
1316 downloads::Show::Params::Create(*args_
));
1317 EXTENSION_FUNCTION_VALIDATE(params
.get());
1318 DownloadItem
* download_item
=
1319 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1320 if (InvalidId(download_item
, &error_
))
1322 download_item
->ShowDownloadInShell();
1323 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW
);
1327 DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {}
1329 DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {}
1331 bool DownloadsShowDefaultFolderFunction::RunAsync() {
1332 DownloadManager
* manager
= NULL
;
1333 DownloadManager
* incognito_manager
= NULL
;
1334 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1335 platform_util::OpenItem(
1336 GetProfile(), DownloadPrefs::FromDownloadManager(manager
)->DownloadPath(),
1337 platform_util::OPEN_FOLDER
, platform_util::OpenOperationCallback());
1338 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER
);
1342 DownloadsOpenFunction::DownloadsOpenFunction() {}
1344 DownloadsOpenFunction::~DownloadsOpenFunction() {}
1346 bool DownloadsOpenFunction::RunSync() {
1347 scoped_ptr
<downloads::Open::Params
> params(
1348 downloads::Open::Params::Create(*args_
));
1349 EXTENSION_FUNCTION_VALIDATE(params
.get());
1350 DownloadItem
* download_item
=
1351 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1352 if (InvalidId(download_item
, &error_
) ||
1353 Fault(!user_gesture(), errors::kUserGesture
, &error_
) ||
1354 Fault(download_item
->GetState() != DownloadItem::COMPLETE
,
1355 errors::kNotComplete
,
1357 Fault(!extension()->permissions_data()->HasAPIPermission(
1358 APIPermission::kDownloadsOpen
),
1359 errors::kOpenPermission
,
1362 download_item
->OpenDownload();
1363 RecordApiFunctions(DOWNLOADS_FUNCTION_OPEN
);
1367 DownloadsDragFunction::DownloadsDragFunction() {}
1369 DownloadsDragFunction::~DownloadsDragFunction() {}
1371 bool DownloadsDragFunction::RunAsync() {
1372 scoped_ptr
<downloads::Drag::Params
> params(
1373 downloads::Drag::Params::Create(*args_
));
1374 EXTENSION_FUNCTION_VALIDATE(params
.get());
1375 DownloadItem
* download_item
=
1376 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1377 content::WebContents
* web_contents
=
1378 dispatcher()->GetVisibleWebContents();
1379 if (InvalidId(download_item
, &error_
) ||
1380 Fault(!web_contents
, errors::kInvisibleContext
, &error_
))
1382 RecordApiFunctions(DOWNLOADS_FUNCTION_DRAG
);
1383 gfx::Image
* icon
= g_browser_process
->icon_manager()->LookupIconFromFilepath(
1384 download_item
->GetTargetFilePath(), IconLoader::NORMAL
);
1385 gfx::NativeView view
= web_contents
->GetNativeView();
1387 // Enable nested tasks during DnD, while |DragDownload()| blocks.
1388 base::MessageLoop::ScopedNestableTaskAllower
allow(
1389 base::MessageLoop::current());
1390 DragDownloadItem(download_item
, icon
, view
);
1395 DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
1397 DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
1399 bool DownloadsSetShelfEnabledFunction::RunSync() {
1400 scoped_ptr
<downloads::SetShelfEnabled::Params
> params(
1401 downloads::SetShelfEnabled::Params::Create(*args_
));
1402 EXTENSION_FUNCTION_VALIDATE(params
.get());
1403 if (!extension()->permissions_data()->HasAPIPermission(
1404 APIPermission::kDownloadsShelf
)) {
1405 error_
= download_extension_errors::kShelfPermission
;
1409 RecordApiFunctions(DOWNLOADS_FUNCTION_SET_SHELF_ENABLED
);
1410 DownloadManager
* manager
= NULL
;
1411 DownloadManager
* incognito_manager
= NULL
;
1412 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1413 DownloadService
* service
= NULL
;
1414 DownloadService
* incognito_service
= NULL
;
1416 service
= DownloadServiceFactory::GetForBrowserContext(
1417 manager
->GetBrowserContext());
1418 service
->GetExtensionEventRouter()->SetShelfEnabled(extension(),
1421 if (incognito_manager
) {
1422 incognito_service
= DownloadServiceFactory::GetForBrowserContext(
1423 incognito_manager
->GetBrowserContext());
1424 incognito_service
->GetExtensionEventRouter()->SetShelfEnabled(
1425 extension(), params
->enabled
);
1428 BrowserList
* browsers
= BrowserList::GetInstance(chrome::GetActiveDesktop());
1430 for (BrowserList::const_iterator iter
= browsers
->begin();
1431 iter
!= browsers
->end(); ++iter
) {
1432 const Browser
* browser
= *iter
;
1433 DownloadService
* current_service
=
1434 DownloadServiceFactory::GetForBrowserContext(browser
->profile());
1435 if (((current_service
== service
) ||
1436 (current_service
== incognito_service
)) &&
1437 browser
->window()->IsDownloadShelfVisible() &&
1438 !current_service
->IsShelfEnabled())
1439 browser
->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC
);
1443 if (params
->enabled
&&
1444 ((manager
&& !service
->IsShelfEnabled()) ||
1445 (incognito_manager
&& !incognito_service
->IsShelfEnabled()))) {
1446 error_
= download_extension_errors::kShelfDisabled
;
1453 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
1454 : icon_extractor_(new DownloadFileIconExtractorImpl()) {
1457 DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
1459 void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
1460 DownloadFileIconExtractor
* extractor
) {
1462 icon_extractor_
.reset(extractor
);
1465 bool DownloadsGetFileIconFunction::RunAsync() {
1466 scoped_ptr
<downloads::GetFileIcon::Params
> params(
1467 downloads::GetFileIcon::Params::Create(*args_
));
1468 EXTENSION_FUNCTION_VALIDATE(params
.get());
1469 const downloads::GetFileIconOptions
* options
=
1470 params
->options
.get();
1471 int icon_size
= kDefaultIconSize
;
1472 if (options
&& options
->size
.get())
1473 icon_size
= *options
->size
.get();
1474 DownloadItem
* download_item
=
1475 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1476 if (InvalidId(download_item
, &error_
) ||
1477 Fault(download_item
->GetTargetFilePath().empty(),
1478 errors::kEmptyFile
, &error_
))
1480 // In-progress downloads return the intermediate filename for GetFullPath()
1481 // which doesn't have the final extension. Therefore a good file icon can't be
1482 // found, so use GetTargetFilePath() instead.
1483 DCHECK(icon_extractor_
.get());
1484 DCHECK(icon_size
== 16 || icon_size
== 32);
1486 content::WebContents
* web_contents
=
1487 dispatcher()->GetVisibleWebContents();
1489 scale
= ui::GetScaleFactorForNativeView(
1490 web_contents
->GetRenderWidgetHostView()->GetNativeView());
1492 EXTENSION_FUNCTION_VALIDATE(icon_extractor_
->ExtractIconURLForPath(
1493 download_item
->GetTargetFilePath(),
1495 IconLoaderSizeFromPixelSize(icon_size
),
1496 base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted
, this)));
1500 void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string
& url
) {
1501 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1502 if (Fault(url
.empty(), errors::kIconNotFound
, &error_
)) {
1503 SendResponse(false);
1506 RecordApiFunctions(DOWNLOADS_FUNCTION_GET_FILE_ICON
);
1507 SetResult(new base::StringValue(url
));
1511 ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(
1513 DownloadManager
* manager
)
1514 : profile_(profile
),
1515 notifier_(manager
, this),
1516 extension_registry_observer_(this) {
1517 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1519 extension_registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
1520 EventRouter
* router
= EventRouter::Get(profile_
);
1522 router
->RegisterObserver(this,
1523 downloads::OnDeterminingFilename::kEventName
);
1526 ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
1527 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1528 EventRouter
* router
= EventRouter::Get(profile_
);
1530 router
->UnregisterObserver(this);
1533 void ExtensionDownloadsEventRouter::
1534 SetDetermineFilenameTimeoutSecondsForTesting(int s
) {
1535 ExtensionDownloadsEventRouterData::
1536 SetDetermineFilenameTimeoutSecondsForTesting(s
);
1539 void ExtensionDownloadsEventRouter::SetShelfEnabled(const Extension
* extension
,
1541 std::set
<const Extension
*>::iterator iter
=
1542 shelf_disabling_extensions_
.find(extension
);
1543 if (iter
== shelf_disabling_extensions_
.end()) {
1545 shelf_disabling_extensions_
.insert(extension
);
1546 } else if (enabled
) {
1547 shelf_disabling_extensions_
.erase(extension
);
1551 bool ExtensionDownloadsEventRouter::IsShelfEnabled() const {
1552 return shelf_disabling_extensions_
.empty();
1555 // The method by which extensions hook into the filename determination process
1556 // is based on the method by which the omnibox API allows extensions to hook
1557 // into the omnibox autocompletion process. Extensions that wish to play a part
1558 // in the filename determination process call
1559 // chrome.downloads.onDeterminingFilename.addListener, which adds an
1560 // EventListener object to ExtensionEventRouter::listeners().
1562 // When a download's filename is being determined, DownloadTargetDeterminer (via
1563 // ChromeDownloadManagerDelegate (CDMD) ::NotifyExtensions()) passes 2 callbacks
1564 // to ExtensionDownloadsEventRouter::OnDeterminingFilename (ODF), which stores
1565 // the callbacks in the item's ExtensionDownloadsEventRouterData (EDERD) along
1566 // with all of the extension IDs that are listening for onDeterminingFilename
1567 // events. ODF dispatches chrome.downloads.onDeterminingFilename.
1569 // When the extension's event handler calls |suggestCallback|,
1570 // downloads_custom_bindings.js calls
1571 // DownloadsInternalDetermineFilenameFunction::RunAsync, which calls
1572 // EDER::DetermineFilename, which notifies the item's EDERD.
1574 // When the last extension's event handler returns, EDERD calls one of the two
1575 // callbacks that CDMD passed to ODF, allowing DownloadTargetDeterminer to
1576 // continue the filename determination process. If multiple extensions wish to
1577 // override the filename, then the extension that was last installed wins.
1579 void ExtensionDownloadsEventRouter::OnDeterminingFilename(
1581 const base::FilePath
& suggested_path
,
1582 const base::Closure
& no_change
,
1583 const FilenameChangedCallback
& change
) {
1584 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1585 ExtensionDownloadsEventRouterData
* data
=
1586 ExtensionDownloadsEventRouterData::Get(item
);
1591 data
->BeginFilenameDetermination(no_change
, change
);
1592 bool any_determiners
= false;
1593 base::DictionaryValue
* json
= DownloadItemToJSON(
1594 item
, profile_
).release();
1595 json
->SetString(kFilenameKey
, suggested_path
.LossyDisplayName());
1596 DispatchEvent(events::DOWNLOADS_ON_DETERMINING_FILENAME
,
1597 downloads::OnDeterminingFilename::kEventName
, false,
1598 base::Bind(&OnDeterminingFilenameWillDispatchCallback
,
1599 &any_determiners
, data
),
1601 if (!any_determiners
) {
1602 data
->ClearPendingDeterminers();
1603 if (!data
->creator_suggested_filename().empty() ||
1604 (data
->creator_conflict_action() !=
1605 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
1606 change
.Run(data
->creator_suggested_filename(),
1607 ConvertConflictAction(data
->creator_conflict_action()));
1608 // If all listeners are removed, don't keep |data| around.
1609 data
->ResetCreatorSuggestion();
1616 void ExtensionDownloadsEventRouter::DetermineFilenameInternal(
1617 const base::FilePath
& filename
,
1618 downloads::FilenameConflictAction conflict_action
,
1619 const std::string
& suggesting_extension_id
,
1620 const base::Time
& suggesting_install_time
,
1621 const std::string
& incumbent_extension_id
,
1622 const base::Time
& incumbent_install_time
,
1623 std::string
* winner_extension_id
,
1624 base::FilePath
* determined_filename
,
1625 downloads::FilenameConflictAction
* determined_conflict_action
,
1626 WarningSet
* warnings
) {
1627 DCHECK(!filename
.empty() ||
1628 (conflict_action
!= downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
));
1629 DCHECK(!suggesting_extension_id
.empty());
1631 if (incumbent_extension_id
.empty()) {
1632 *winner_extension_id
= suggesting_extension_id
;
1633 *determined_filename
= filename
;
1634 *determined_conflict_action
= conflict_action
;
1638 if (suggesting_install_time
< incumbent_install_time
) {
1639 *winner_extension_id
= incumbent_extension_id
;
1640 warnings
->insert(Warning::CreateDownloadFilenameConflictWarning(
1641 suggesting_extension_id
,
1642 incumbent_extension_id
,
1644 *determined_filename
));
1648 *winner_extension_id
= suggesting_extension_id
;
1649 warnings
->insert(Warning::CreateDownloadFilenameConflictWarning(
1650 incumbent_extension_id
,
1651 suggesting_extension_id
,
1652 *determined_filename
,
1654 *determined_filename
= filename
;
1655 *determined_conflict_action
= conflict_action
;
1658 bool ExtensionDownloadsEventRouter::DetermineFilename(
1660 bool include_incognito
,
1661 const std::string
& ext_id
,
1663 const base::FilePath
& const_filename
,
1664 downloads::FilenameConflictAction conflict_action
,
1665 std::string
* error
) {
1666 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1667 RecordApiFunctions(DOWNLOADS_FUNCTION_DETERMINE_FILENAME
);
1668 DownloadItem
* item
= GetDownload(profile
, include_incognito
, download_id
);
1669 ExtensionDownloadsEventRouterData
* data
=
1670 item
? ExtensionDownloadsEventRouterData::Get(item
) : NULL
;
1671 // maxListeners=1 in downloads.idl and suggestCallback in
1672 // downloads_custom_bindings.js should prevent duplicate DeterminerCallback
1673 // calls from the same renderer, but an extension may have more than one
1674 // renderer, so don't DCHECK(!reported).
1675 if (InvalidId(item
, error
) ||
1676 Fault(item
->GetState() != DownloadItem::IN_PROGRESS
,
1677 errors::kNotInProgress
, error
) ||
1678 Fault(!data
, errors::kUnexpectedDeterminer
, error
) ||
1679 Fault(data
->DeterminerAlreadyReported(ext_id
),
1680 errors::kTooManyListeners
, error
))
1682 base::FilePath::StringType
filename_str(const_filename
.value());
1683 // Allow windows-style directory separators on all platforms.
1684 std::replace(filename_str
.begin(), filename_str
.end(),
1685 FILE_PATH_LITERAL('\\'), FILE_PATH_LITERAL('/'));
1686 base::FilePath
filename(filename_str
);
1687 bool valid_filename
= net::IsSafePortableRelativePath(filename
);
1688 filename
= (valid_filename
? filename
.NormalizePathSeparators() :
1690 // If the invalid filename check is moved to before DeterminerCallback(), then
1691 // it will block forever waiting for this ext_id to report.
1692 if (Fault(!data
->DeterminerCallback(
1693 profile
, ext_id
, filename
, conflict_action
),
1694 errors::kUnexpectedDeterminer
, error
) ||
1695 Fault((!const_filename
.empty() && !valid_filename
),
1696 errors::kInvalidFilename
, error
))
1701 void ExtensionDownloadsEventRouter::OnListenerRemoved(
1702 const EventListenerInfo
& details
) {
1703 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1704 DownloadManager
* manager
= notifier_
.GetManager();
1707 bool determiner_removed
= (
1708 details
.event_name
== downloads::OnDeterminingFilename::kEventName
);
1709 EventRouter
* router
= EventRouter::Get(profile_
);
1710 bool any_listeners
=
1711 router
->HasEventListener(downloads::OnChanged::kEventName
) ||
1712 router
->HasEventListener(downloads::OnDeterminingFilename::kEventName
);
1713 if (!determiner_removed
&& any_listeners
)
1715 DownloadManager::DownloadVector items
;
1716 manager
->GetAllDownloads(&items
);
1717 for (DownloadManager::DownloadVector::const_iterator iter
=
1719 iter
!= items
.end(); ++iter
) {
1720 ExtensionDownloadsEventRouterData
* data
=
1721 ExtensionDownloadsEventRouterData::Get(*iter
);
1724 if (determiner_removed
) {
1725 // Notify any items that may be waiting for callbacks from this
1726 // extension/determiner. This will almost always be a no-op, however, it
1727 // is possible for an extension renderer to be unloaded while a download
1728 // item is waiting for a determiner. In that case, the download item
1730 data
->DeterminerRemoved(details
.extension_id
);
1732 if (!any_listeners
&&
1733 data
->creator_suggested_filename().empty()) {
1734 ExtensionDownloadsEventRouterData::Remove(*iter
);
1739 // That's all the methods that have to do with filename determination. The rest
1740 // have to do with the other, less special events.
1742 void ExtensionDownloadsEventRouter::OnDownloadCreated(
1743 DownloadManager
* manager
, DownloadItem
* download_item
) {
1744 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1745 if (download_item
->IsTemporary())
1748 EventRouter
* router
= EventRouter::Get(profile_
);
1749 // Avoid allocating a bunch of memory in DownloadItemToJSON if it isn't going
1752 (!router
->HasEventListener(downloads::OnCreated::kEventName
) &&
1753 !router
->HasEventListener(downloads::OnChanged::kEventName
) &&
1754 !router
->HasEventListener(
1755 downloads::OnDeterminingFilename::kEventName
))) {
1758 scoped_ptr
<base::DictionaryValue
> json_item(
1759 DownloadItemToJSON(download_item
, profile_
));
1760 DispatchEvent(events::DOWNLOADS_ON_CREATED
, downloads::OnCreated::kEventName
,
1761 true, Event::WillDispatchCallback(), json_item
->DeepCopy());
1762 if (!ExtensionDownloadsEventRouterData::Get(download_item
) &&
1763 (router
->HasEventListener(downloads::OnChanged::kEventName
) ||
1764 router
->HasEventListener(
1765 downloads::OnDeterminingFilename::kEventName
))) {
1766 new ExtensionDownloadsEventRouterData(download_item
, json_item
.Pass());
1770 void ExtensionDownloadsEventRouter::OnDownloadUpdated(
1771 DownloadManager
* manager
, DownloadItem
* download_item
) {
1772 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1773 EventRouter
* router
= EventRouter::Get(profile_
);
1774 ExtensionDownloadsEventRouterData
* data
=
1775 ExtensionDownloadsEventRouterData::Get(download_item
);
1776 if (download_item
->IsTemporary() ||
1777 !router
->HasEventListener(downloads::OnChanged::kEventName
)) {
1781 // The download_item probably transitioned from temporary to not temporary,
1782 // or else an event listener was added.
1783 data
= new ExtensionDownloadsEventRouterData(
1785 scoped_ptr
<base::DictionaryValue
>(new base::DictionaryValue()));
1787 scoped_ptr
<base::DictionaryValue
> new_json(DownloadItemToJSON(
1788 download_item
, profile_
));
1789 scoped_ptr
<base::DictionaryValue
> delta(new base::DictionaryValue());
1790 delta
->SetInteger(kIdKey
, download_item
->GetId());
1791 std::set
<std::string
> new_fields
;
1792 bool changed
= false;
1794 // For each field in the new json representation of the download_item except
1795 // the bytesReceived field, if the field has changed from the previous old
1796 // json, set the differences in the |delta| object and remember that something
1797 // significant changed.
1798 for (base::DictionaryValue::Iterator
iter(*new_json
.get());
1799 !iter
.IsAtEnd(); iter
.Advance()) {
1800 new_fields
.insert(iter
.key());
1801 if (IsDownloadDeltaField(iter
.key())) {
1802 const base::Value
* old_value
= NULL
;
1803 if (!data
->json().HasKey(iter
.key()) ||
1804 (data
->json().Get(iter
.key(), &old_value
) &&
1805 !iter
.value().Equals(old_value
))) {
1806 delta
->Set(iter
.key() + ".current", iter
.value().DeepCopy());
1808 delta
->Set(iter
.key() + ".previous", old_value
->DeepCopy());
1814 // If a field was in the previous json but is not in the new json, set the
1815 // difference in |delta|.
1816 for (base::DictionaryValue::Iterator
iter(data
->json());
1817 !iter
.IsAtEnd(); iter
.Advance()) {
1818 if ((new_fields
.find(iter
.key()) == new_fields
.end()) &&
1819 IsDownloadDeltaField(iter
.key())) {
1820 // estimatedEndTime disappears after completion, but bytesReceived stays.
1821 delta
->Set(iter
.key() + ".previous", iter
.value().DeepCopy());
1826 // Update the OnChangedStat and dispatch the event if something significant
1827 // changed. Replace the stored json with the new json.
1828 data
->OnItemUpdated();
1830 DispatchEvent(events::DOWNLOADS_ON_CHANGED
,
1831 downloads::OnChanged::kEventName
, true,
1832 Event::WillDispatchCallback(), delta
.release());
1833 data
->OnChangedFired();
1835 data
->set_json(new_json
.Pass());
1838 void ExtensionDownloadsEventRouter::OnDownloadRemoved(
1839 DownloadManager
* manager
, DownloadItem
* download_item
) {
1840 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1841 if (download_item
->IsTemporary())
1844 events::DOWNLOADS_ON_ERASED
, downloads::OnErased::kEventName
, true,
1845 Event::WillDispatchCallback(),
1846 new base::FundamentalValue(static_cast<int>(download_item
->GetId())));
1849 void ExtensionDownloadsEventRouter::DispatchEvent(
1850 events::HistogramValue histogram_value
,
1851 const std::string
& event_name
,
1852 bool include_incognito
,
1853 const Event::WillDispatchCallback
& will_dispatch_callback
,
1855 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1856 if (!EventRouter::Get(profile_
))
1858 scoped_ptr
<base::ListValue
> args(new base::ListValue());
1860 std::string json_args
;
1861 base::JSONWriter::Write(*args
, &json_args
);
1862 scoped_ptr
<Event
> event(new Event(histogram_value
, event_name
, args
.Pass()));
1863 // The downloads system wants to share on-record events with off-record
1864 // extension renderers even in incognito_split_mode because that's how
1865 // chrome://downloads works. The "restrict_to_profile" mechanism does not
1866 // anticipate this, so it does not automatically prevent sharing off-record
1867 // events with on-record extension renderers.
1868 event
->restrict_to_browser_context
=
1869 (include_incognito
&& !profile_
->IsOffTheRecord()) ? NULL
: profile_
;
1870 event
->will_dispatch_callback
= will_dispatch_callback
;
1871 EventRouter::Get(profile_
)->BroadcastEvent(event
.Pass());
1872 DownloadsNotificationSource notification_source
;
1873 notification_source
.event_name
= event_name
;
1874 notification_source
.profile
= profile_
;
1875 content::Source
<DownloadsNotificationSource
> content_source(
1876 ¬ification_source
);
1877 content::NotificationService::current()->Notify(
1878 extensions::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT
,
1880 content::Details
<std::string
>(&json_args
));
1883 void ExtensionDownloadsEventRouter::OnExtensionUnloaded(
1884 content::BrowserContext
* browser_context
,
1885 const Extension
* extension
,
1886 UnloadedExtensionInfo::Reason reason
) {
1887 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1888 std::set
<const Extension
*>::iterator iter
=
1889 shelf_disabling_extensions_
.find(extension
);
1890 if (iter
!= shelf_disabling_extensions_
.end())
1891 shelf_disabling_extensions_
.erase(iter
);
1894 void ExtensionDownloadsEventRouter::CheckForHistoryFilesRemoval() {
1895 static const int kFileExistenceRateLimitSeconds
= 10;
1896 DownloadManager
* manager
= notifier_
.GetManager();
1899 base::Time
now(base::Time::Now());
1900 int delta
= now
.ToTimeT() - last_checked_removal_
.ToTimeT();
1901 if (delta
<= kFileExistenceRateLimitSeconds
)
1903 last_checked_removal_
= now
;
1904 manager
->CheckForHistoryFilesRemoval();
1907 } // namespace extensions