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/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/json/json_writer.h"
17 #include "base/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/metrics/histogram.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string16.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/task/cancelable_task_tracker.h"
27 #include "base/values.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/download/download_danger_prompt.h"
31 #include "chrome/browser/download/download_file_icon_extractor.h"
32 #include "chrome/browser/download/download_prefs.h"
33 #include "chrome/browser/download/download_query.h"
34 #include "chrome/browser/download/download_service.h"
35 #include "chrome/browser/download/download_service_factory.h"
36 #include "chrome/browser/download/download_shelf.h"
37 #include "chrome/browser/download/download_stats.h"
38 #include "chrome/browser/download/drag_download_item.h"
39 #include "chrome/browser/extensions/extension_service.h"
40 #include "chrome/browser/extensions/extension_warning_service.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_process_host.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/render_widget_host_view.h"
61 #include "content/public/browser/resource_context.h"
62 #include "content/public/browser/resource_dispatcher_host.h"
63 #include "content/public/browser/web_contents.h"
64 #include "content/public/browser/web_contents_view.h"
65 #include "extensions/browser/event_router.h"
66 #include "extensions/browser/extension_function_dispatcher.h"
67 #include "extensions/browser/extension_registry.h"
68 #include "extensions/browser/extension_system.h"
69 #include "net/base/filename_util.h"
70 #include "net/base/load_flags.h"
71 #include "net/http/http_util.h"
72 #include "third_party/skia/include/core/SkBitmap.h"
73 #include "ui/base/webui/web_ui_util.h"
74 #include "ui/gfx/image/image_skia.h"
76 using content::BrowserContext
;
77 using content::BrowserThread
;
78 using content::DownloadItem
;
79 using content::DownloadManager
;
81 namespace download_extension_errors
{
83 const char kEmptyFile
[] = "Filename not yet determined";
84 const char kFileAlreadyDeleted
[] = "Download file already deleted";
85 const char kFileNotRemoved
[] = "Unable to remove file";
86 const char kIconNotFound
[] = "Icon not found";
87 const char kInvalidDangerType
[] = "Invalid danger type";
88 const char kInvalidFilename
[] = "Invalid filename";
89 const char kInvalidFilter
[] = "Invalid query filter";
90 const char kInvalidHeader
[] = "Invalid request header";
91 const char kInvalidId
[] = "Invalid downloadId";
92 const char kInvalidOrderBy
[] = "Invalid orderBy field";
93 const char kInvalidQueryLimit
[] = "Invalid query limit";
94 const char kInvalidState
[] = "Invalid state";
95 const char kInvalidURL
[] = "Invalid URL";
96 const char kInvisibleContext
[] = "Javascript execution context is not visible "
97 "(tab, window, popup bubble)";
98 const char kNotComplete
[] = "Download must be complete";
99 const char kNotDangerous
[] = "Download must be dangerous";
100 const char kNotInProgress
[] = "Download must be in progress";
101 const char kNotResumable
[] = "DownloadItem.canResume must be true";
102 const char kOpenPermission
[] = "The \"downloads.open\" permission is required";
103 const char kShelfDisabled
[] = "Another extension has disabled the shelf";
104 const char kShelfPermission
[] = "downloads.setShelfEnabled requires the "
105 "\"downloads.shelf\" permission";
106 const char kTooManyListeners
[] = "Each extension may have at most one "
107 "onDeterminingFilename listener between all of its renderer execution "
109 const char kUnexpectedDeterminer
[] = "Unexpected determineFilename call";
110 const char kUserGesture
[] = "User gesture required";
112 } // namespace download_extension_errors
114 namespace errors
= download_extension_errors
;
116 namespace extensions
{
120 namespace downloads
= extensions::api::downloads
;
122 // Default icon size for getFileIcon() in pixels.
123 const int kDefaultIconSize
= 32;
126 const char kByExtensionIdKey
[] = "byExtensionId";
127 const char kByExtensionNameKey
[] = "byExtensionName";
128 const char kBytesReceivedKey
[] = "bytesReceived";
129 const char kCanResumeKey
[] = "canResume";
130 const char kDangerAccepted
[] = "accepted";
131 const char kDangerContent
[] = "content";
132 const char kDangerFile
[] = "file";
133 const char kDangerHost
[] = "host";
134 const char kDangerKey
[] = "danger";
135 const char kDangerSafe
[] = "safe";
136 const char kDangerUncommon
[] = "uncommon";
137 const char kDangerUnwanted
[] = "unwanted";
138 const char kDangerUrl
[] = "url";
139 const char kEndTimeKey
[] = "endTime";
140 const char kEndedAfterKey
[] = "endedAfter";
141 const char kEndedBeforeKey
[] = "endedBefore";
142 const char kErrorKey
[] = "error";
143 const char kEstimatedEndTimeKey
[] = "estimatedEndTime";
144 const char kExistsKey
[] = "exists";
145 const char kFileSizeKey
[] = "fileSize";
146 const char kFilenameKey
[] = "filename";
147 const char kFilenameRegexKey
[] = "filenameRegex";
148 const char kIdKey
[] = "id";
149 const char kIncognitoKey
[] = "incognito";
150 const char kMimeKey
[] = "mime";
151 const char kPausedKey
[] = "paused";
152 const char kQueryKey
[] = "query";
153 const char kReferrerUrlKey
[] = "referrer";
154 const char kStartTimeKey
[] = "startTime";
155 const char kStartedAfterKey
[] = "startedAfter";
156 const char kStartedBeforeKey
[] = "startedBefore";
157 const char kStateComplete
[] = "complete";
158 const char kStateInProgress
[] = "in_progress";
159 const char kStateInterrupted
[] = "interrupted";
160 const char kStateKey
[] = "state";
161 const char kTotalBytesGreaterKey
[] = "totalBytesGreater";
162 const char kTotalBytesKey
[] = "totalBytes";
163 const char kTotalBytesLessKey
[] = "totalBytesLess";
164 const char kUrlKey
[] = "url";
165 const char kUrlRegexKey
[] = "urlRegex";
167 // Note: Any change to the danger type strings, should be accompanied by a
168 // corresponding change to downloads.json.
169 const char* kDangerStrings
[] = {
180 COMPILE_ASSERT(arraysize(kDangerStrings
) == content::DOWNLOAD_DANGER_TYPE_MAX
,
181 download_danger_type_enum_changed
);
183 // Note: Any change to the state strings, should be accompanied by a
184 // corresponding change to downloads.json.
185 const char* kStateStrings
[] = {
191 COMPILE_ASSERT(arraysize(kStateStrings
) == DownloadItem::MAX_DOWNLOAD_STATE
,
192 download_item_state_enum_changed
);
194 const char* DangerString(content::DownloadDangerType danger
) {
196 DCHECK(danger
< static_cast<content::DownloadDangerType
>(
197 arraysize(kDangerStrings
)));
198 if (danger
< 0 || danger
>= static_cast<content::DownloadDangerType
>(
199 arraysize(kDangerStrings
)))
201 return kDangerStrings
[danger
];
204 content::DownloadDangerType
DangerEnumFromString(const std::string
& danger
) {
205 for (size_t i
= 0; i
< arraysize(kDangerStrings
); ++i
) {
206 if (danger
== kDangerStrings
[i
])
207 return static_cast<content::DownloadDangerType
>(i
);
209 return content::DOWNLOAD_DANGER_TYPE_MAX
;
212 const char* StateString(DownloadItem::DownloadState state
) {
214 DCHECK(state
< static_cast<DownloadItem::DownloadState
>(
215 arraysize(kStateStrings
)));
216 if (state
< 0 || state
>= static_cast<DownloadItem::DownloadState
>(
217 arraysize(kStateStrings
)))
219 return kStateStrings
[state
];
222 DownloadItem::DownloadState
StateEnumFromString(const std::string
& state
) {
223 for (size_t i
= 0; i
< arraysize(kStateStrings
); ++i
) {
224 if ((kStateStrings
[i
] != NULL
) && (state
== kStateStrings
[i
]))
225 return static_cast<DownloadItem::DownloadState
>(i
);
227 return DownloadItem::MAX_DOWNLOAD_STATE
;
230 std::string
TimeToISO8601(const base::Time
& t
) {
231 base::Time::Exploded exploded
;
232 t
.UTCExplode(&exploded
);
233 return base::StringPrintf(
234 "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded
.year
, exploded
.month
,
235 exploded
.day_of_month
, exploded
.hour
, exploded
.minute
, exploded
.second
,
236 exploded
.millisecond
);
239 scoped_ptr
<base::DictionaryValue
> DownloadItemToJSON(
240 DownloadItem
* download_item
,
242 base::DictionaryValue
* json
= new base::DictionaryValue();
243 json
->SetBoolean(kExistsKey
, !download_item
->GetFileExternallyRemoved());
244 json
->SetInteger(kIdKey
, download_item
->GetId());
245 const GURL
& url
= download_item
->GetOriginalUrl();
246 json
->SetString(kUrlKey
, (url
.is_valid() ? url
.spec() : std::string()));
247 const GURL
& referrer
= download_item
->GetReferrerUrl();
248 json
->SetString(kReferrerUrlKey
, (referrer
.is_valid() ? referrer
.spec()
250 json
->SetString(kFilenameKey
,
251 download_item
->GetTargetFilePath().LossyDisplayName());
252 json
->SetString(kDangerKey
, DangerString(download_item
->GetDangerType()));
253 json
->SetString(kStateKey
, StateString(download_item
->GetState()));
254 json
->SetBoolean(kCanResumeKey
, download_item
->CanResume());
255 json
->SetBoolean(kPausedKey
, download_item
->IsPaused());
256 json
->SetString(kMimeKey
, download_item
->GetMimeType());
257 json
->SetString(kStartTimeKey
, TimeToISO8601(download_item
->GetStartTime()));
258 json
->SetDouble(kBytesReceivedKey
, download_item
->GetReceivedBytes());
259 json
->SetDouble(kTotalBytesKey
, download_item
->GetTotalBytes());
260 json
->SetBoolean(kIncognitoKey
, profile
->IsOffTheRecord());
261 if (download_item
->GetState() == DownloadItem::INTERRUPTED
) {
262 json
->SetString(kErrorKey
,
263 content::DownloadInterruptReasonToString(
264 download_item
->GetLastReason()));
265 } else if (download_item
->GetState() == DownloadItem::CANCELLED
) {
266 json
->SetString(kErrorKey
,
267 content::DownloadInterruptReasonToString(
268 content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
));
270 if (!download_item
->GetEndTime().is_null())
271 json
->SetString(kEndTimeKey
, TimeToISO8601(download_item
->GetEndTime()));
272 base::TimeDelta time_remaining
;
273 if (download_item
->TimeRemaining(&time_remaining
)) {
274 base::Time now
= base::Time::Now();
275 json
->SetString(kEstimatedEndTimeKey
, TimeToISO8601(now
+ time_remaining
));
277 DownloadedByExtension
* by_ext
= DownloadedByExtension::Get(download_item
);
279 json
->SetString(kByExtensionIdKey
, by_ext
->id());
280 json
->SetString(kByExtensionNameKey
, by_ext
->name());
281 // Lookup the extension's current name() in case the user changed their
282 // language. This won't work if the extension was uninstalled, so the name
283 // might be the wrong language.
284 bool include_disabled
= true;
285 const extensions::Extension
* extension
= extensions::ExtensionSystem::Get(
286 profile
)->extension_service()->GetExtensionById(
287 by_ext
->id(), include_disabled
);
289 json
->SetString(kByExtensionNameKey
, extension
->name());
291 // TODO(benjhayden): Implement fileSize.
292 json
->SetDouble(kFileSizeKey
, download_item
->GetTotalBytes());
293 return scoped_ptr
<base::DictionaryValue
>(json
);
296 class DownloadFileIconExtractorImpl
: public DownloadFileIconExtractor
{
298 DownloadFileIconExtractorImpl() {}
300 virtual ~DownloadFileIconExtractorImpl() {}
302 virtual bool ExtractIconURLForPath(const base::FilePath
& path
,
304 IconLoader::IconSize icon_size
,
305 IconURLCallback callback
) OVERRIDE
;
307 void OnIconLoadComplete(
308 float scale
, const IconURLCallback
& callback
, gfx::Image
* icon
);
310 base::CancelableTaskTracker cancelable_task_tracker_
;
313 bool DownloadFileIconExtractorImpl::ExtractIconURLForPath(
314 const base::FilePath
& path
,
316 IconLoader::IconSize icon_size
,
317 IconURLCallback callback
) {
318 IconManager
* im
= g_browser_process
->icon_manager();
319 // The contents of the file at |path| may have changed since a previous
320 // request, in which case the associated icon may also have changed.
321 // Therefore, always call LoadIcon instead of attempting a LookupIcon.
324 base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete
,
325 base::Unretained(this), scale
, callback
),
326 &cancelable_task_tracker_
);
330 void DownloadFileIconExtractorImpl::OnIconLoadComplete(
331 float scale
, const IconURLCallback
& callback
, gfx::Image
* icon
) {
332 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
333 callback
.Run(!icon
? std::string() : webui::GetBitmapDataUrl(
334 icon
->ToImageSkia()->GetRepresentation(scale
).sk_bitmap()));
337 IconLoader::IconSize
IconLoaderSizeFromPixelSize(int pixel_size
) {
338 switch (pixel_size
) {
339 case 16: return IconLoader::SMALL
;
340 case 32: return IconLoader::NORMAL
;
343 return IconLoader::NORMAL
;
347 typedef base::hash_map
<std::string
, DownloadQuery::FilterType
> FilterTypeMap
;
349 void InitFilterTypeMap(FilterTypeMap
& filter_types
) {
350 filter_types
[kBytesReceivedKey
] = DownloadQuery::FILTER_BYTES_RECEIVED
;
351 filter_types
[kExistsKey
] = DownloadQuery::FILTER_EXISTS
;
352 filter_types
[kFilenameKey
] = DownloadQuery::FILTER_FILENAME
;
353 filter_types
[kFilenameRegexKey
] = DownloadQuery::FILTER_FILENAME_REGEX
;
354 filter_types
[kMimeKey
] = DownloadQuery::FILTER_MIME
;
355 filter_types
[kPausedKey
] = DownloadQuery::FILTER_PAUSED
;
356 filter_types
[kQueryKey
] = DownloadQuery::FILTER_QUERY
;
357 filter_types
[kEndedAfterKey
] = DownloadQuery::FILTER_ENDED_AFTER
;
358 filter_types
[kEndedBeforeKey
] = DownloadQuery::FILTER_ENDED_BEFORE
;
359 filter_types
[kEndTimeKey
] = DownloadQuery::FILTER_END_TIME
;
360 filter_types
[kStartedAfterKey
] = DownloadQuery::FILTER_STARTED_AFTER
;
361 filter_types
[kStartedBeforeKey
] = DownloadQuery::FILTER_STARTED_BEFORE
;
362 filter_types
[kStartTimeKey
] = DownloadQuery::FILTER_START_TIME
;
363 filter_types
[kTotalBytesKey
] = DownloadQuery::FILTER_TOTAL_BYTES
;
364 filter_types
[kTotalBytesGreaterKey
] =
365 DownloadQuery::FILTER_TOTAL_BYTES_GREATER
;
366 filter_types
[kTotalBytesLessKey
] = DownloadQuery::FILTER_TOTAL_BYTES_LESS
;
367 filter_types
[kUrlKey
] = DownloadQuery::FILTER_URL
;
368 filter_types
[kUrlRegexKey
] = DownloadQuery::FILTER_URL_REGEX
;
371 typedef base::hash_map
<std::string
, DownloadQuery::SortType
> SortTypeMap
;
373 void InitSortTypeMap(SortTypeMap
& sorter_types
) {
374 sorter_types
[kBytesReceivedKey
] = DownloadQuery::SORT_BYTES_RECEIVED
;
375 sorter_types
[kDangerKey
] = DownloadQuery::SORT_DANGER
;
376 sorter_types
[kEndTimeKey
] = DownloadQuery::SORT_END_TIME
;
377 sorter_types
[kExistsKey
] = DownloadQuery::SORT_EXISTS
;
378 sorter_types
[kFilenameKey
] = DownloadQuery::SORT_FILENAME
;
379 sorter_types
[kMimeKey
] = DownloadQuery::SORT_MIME
;
380 sorter_types
[kPausedKey
] = DownloadQuery::SORT_PAUSED
;
381 sorter_types
[kStartTimeKey
] = DownloadQuery::SORT_START_TIME
;
382 sorter_types
[kStateKey
] = DownloadQuery::SORT_STATE
;
383 sorter_types
[kTotalBytesKey
] = DownloadQuery::SORT_TOTAL_BYTES
;
384 sorter_types
[kUrlKey
] = DownloadQuery::SORT_URL
;
387 bool IsNotTemporaryDownloadFilter(const DownloadItem
& download_item
) {
388 return !download_item
.IsTemporary();
391 // Set |manager| to the on-record DownloadManager, and |incognito_manager| to
392 // the off-record DownloadManager if one exists and is requested via
393 // |include_incognito|. This should work regardless of whether |profile| is
394 // original or incognito.
397 bool include_incognito
,
398 DownloadManager
** manager
,
399 DownloadManager
** incognito_manager
) {
400 *manager
= BrowserContext::GetDownloadManager(profile
->GetOriginalProfile());
401 if (profile
->HasOffTheRecordProfile() &&
402 (include_incognito
||
403 profile
->IsOffTheRecord())) {
404 *incognito_manager
= BrowserContext::GetDownloadManager(
405 profile
->GetOffTheRecordProfile());
407 *incognito_manager
= NULL
;
411 DownloadItem
* GetDownload(Profile
* profile
, bool include_incognito
, int id
) {
412 DownloadManager
* manager
= NULL
;
413 DownloadManager
* incognito_manager
= NULL
;
414 GetManagers(profile
, include_incognito
, &manager
, &incognito_manager
);
415 DownloadItem
* download_item
= manager
->GetDownload(id
);
416 if (!download_item
&& incognito_manager
)
417 download_item
= incognito_manager
->GetDownload(id
);
418 return download_item
;
421 enum DownloadsFunctionName
{
422 DOWNLOADS_FUNCTION_DOWNLOAD
= 0,
423 DOWNLOADS_FUNCTION_SEARCH
= 1,
424 DOWNLOADS_FUNCTION_PAUSE
= 2,
425 DOWNLOADS_FUNCTION_RESUME
= 3,
426 DOWNLOADS_FUNCTION_CANCEL
= 4,
427 DOWNLOADS_FUNCTION_ERASE
= 5,
429 DOWNLOADS_FUNCTION_ACCEPT_DANGER
= 7,
430 DOWNLOADS_FUNCTION_SHOW
= 8,
431 DOWNLOADS_FUNCTION_DRAG
= 9,
432 DOWNLOADS_FUNCTION_GET_FILE_ICON
= 10,
433 DOWNLOADS_FUNCTION_OPEN
= 11,
434 DOWNLOADS_FUNCTION_REMOVE_FILE
= 12,
435 DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER
= 13,
436 DOWNLOADS_FUNCTION_SET_SHELF_ENABLED
= 14,
437 DOWNLOADS_FUNCTION_DETERMINE_FILENAME
= 15,
438 // Insert new values here, not at the beginning.
439 DOWNLOADS_FUNCTION_LAST
442 void RecordApiFunctions(DownloadsFunctionName function
) {
443 UMA_HISTOGRAM_ENUMERATION("Download.ApiFunctions",
445 DOWNLOADS_FUNCTION_LAST
);
448 void CompileDownloadQueryOrderBy(
449 const std::vector
<std::string
>& order_by_strs
,
451 DownloadQuery
* query
) {
452 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
454 static base::LazyInstance
<SortTypeMap
> sorter_types
=
455 LAZY_INSTANCE_INITIALIZER
;
456 if (sorter_types
.Get().size() == 0)
457 InitSortTypeMap(sorter_types
.Get());
459 for (std::vector
<std::string
>::const_iterator iter
= order_by_strs
.begin();
460 iter
!= order_by_strs
.end(); ++iter
) {
461 std::string term_str
= *iter
;
462 if (term_str
.empty())
464 DownloadQuery::SortDirection direction
= DownloadQuery::ASCENDING
;
465 if (term_str
[0] == '-') {
466 direction
= DownloadQuery::DESCENDING
;
467 term_str
= term_str
.substr(1);
469 SortTypeMap::const_iterator sorter_type
=
470 sorter_types
.Get().find(term_str
);
471 if (sorter_type
== sorter_types
.Get().end()) {
472 *error
= errors::kInvalidOrderBy
;
475 query
->AddSorter(sorter_type
->second
, direction
);
479 void RunDownloadQuery(
480 const downloads::DownloadQuery
& query_in
,
481 DownloadManager
* manager
,
482 DownloadManager
* incognito_manager
,
484 DownloadQuery::DownloadVector
* results
) {
485 // TODO(benjhayden): Consider switching from LazyInstance to explicit string
487 static base::LazyInstance
<FilterTypeMap
> filter_types
=
488 LAZY_INSTANCE_INITIALIZER
;
489 if (filter_types
.Get().size() == 0)
490 InitFilterTypeMap(filter_types
.Get());
492 DownloadQuery query_out
;
495 if (query_in
.limit
.get()) {
496 if (*query_in
.limit
.get() < 0) {
497 *error
= errors::kInvalidQueryLimit
;
500 limit
= *query_in
.limit
.get();
503 query_out
.Limit(limit
);
506 std::string state_string
= downloads::ToString(query_in
.state
);
507 if (!state_string
.empty()) {
508 DownloadItem::DownloadState state
= StateEnumFromString(state_string
);
509 if (state
== DownloadItem::MAX_DOWNLOAD_STATE
) {
510 *error
= errors::kInvalidState
;
513 query_out
.AddFilter(state
);
515 std::string danger_string
=
516 downloads::ToString(query_in
.danger
);
517 if (!danger_string
.empty()) {
518 content::DownloadDangerType danger_type
= DangerEnumFromString(
520 if (danger_type
== content::DOWNLOAD_DANGER_TYPE_MAX
) {
521 *error
= errors::kInvalidDangerType
;
524 query_out
.AddFilter(danger_type
);
526 if (query_in
.order_by
.get()) {
527 CompileDownloadQueryOrderBy(*query_in
.order_by
.get(), error
, &query_out
);
532 scoped_ptr
<base::DictionaryValue
> query_in_value(query_in
.ToValue().Pass());
533 for (base::DictionaryValue::Iterator
query_json_field(*query_in_value
.get());
534 !query_json_field
.IsAtEnd(); query_json_field
.Advance()) {
535 FilterTypeMap::const_iterator filter_type
=
536 filter_types
.Get().find(query_json_field
.key());
537 if (filter_type
!= filter_types
.Get().end()) {
538 if (!query_out
.AddFilter(filter_type
->second
, query_json_field
.value())) {
539 *error
= errors::kInvalidFilter
;
545 DownloadQuery::DownloadVector all_items
;
546 if (query_in
.id
.get()) {
547 DownloadItem
* download_item
= manager
->GetDownload(*query_in
.id
.get());
548 if (!download_item
&& incognito_manager
)
549 download_item
= incognito_manager
->GetDownload(*query_in
.id
.get());
551 all_items
.push_back(download_item
);
553 manager
->GetAllDownloads(&all_items
);
554 if (incognito_manager
)
555 incognito_manager
->GetAllDownloads(&all_items
);
557 query_out
.AddFilter(base::Bind(&IsNotTemporaryDownloadFilter
));
558 query_out
.Search(all_items
.begin(), all_items
.end(), results
);
561 DownloadPathReservationTracker::FilenameConflictAction
ConvertConflictAction(
562 downloads::FilenameConflictAction action
) {
564 case downloads::FILENAME_CONFLICT_ACTION_NONE
:
565 case downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
:
566 return DownloadPathReservationTracker::UNIQUIFY
;
567 case downloads::FILENAME_CONFLICT_ACTION_OVERWRITE
:
568 return DownloadPathReservationTracker::OVERWRITE
;
569 case downloads::FILENAME_CONFLICT_ACTION_PROMPT
:
570 return DownloadPathReservationTracker::PROMPT
;
573 return DownloadPathReservationTracker::UNIQUIFY
;
576 class ExtensionDownloadsEventRouterData
: public base::SupportsUserData::Data
{
578 static ExtensionDownloadsEventRouterData
* Get(DownloadItem
* download_item
) {
579 base::SupportsUserData::Data
* data
= download_item
->GetUserData(kKey
);
580 return (data
== NULL
) ? NULL
:
581 static_cast<ExtensionDownloadsEventRouterData
*>(data
);
584 static void Remove(DownloadItem
* download_item
) {
585 download_item
->RemoveUserData(kKey
);
588 explicit ExtensionDownloadsEventRouterData(
589 DownloadItem
* download_item
,
590 scoped_ptr
<base::DictionaryValue
> json_item
)
593 json_(json_item
.Pass()),
594 creator_conflict_action_(
595 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
),
596 determined_conflict_action_(
597 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
) {
598 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
599 download_item
->SetUserData(kKey
, this);
602 virtual ~ExtensionDownloadsEventRouterData() {
604 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged",
605 (changed_fired_
* 100 / updated_
));
609 const base::DictionaryValue
& json() const { return *json_
.get(); }
610 void set_json(scoped_ptr
<base::DictionaryValue
> json_item
) {
611 json_
= json_item
.Pass();
614 void OnItemUpdated() { ++updated_
; }
615 void OnChangedFired() { ++changed_fired_
; }
617 static void SetDetermineFilenameTimeoutSecondsForTesting(int s
) {
618 determine_filename_timeout_s_
= s
;
621 void BeginFilenameDetermination(
622 const base::Closure
& no_change
,
623 const ExtensionDownloadsEventRouter::FilenameChangedCallback
& change
) {
624 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
625 ClearPendingDeterminers();
626 filename_no_change_
= no_change
;
627 filename_change_
= change
;
628 determined_filename_
= creator_suggested_filename_
;
629 determined_conflict_action_
= creator_conflict_action_
;
630 // determiner_.install_time should default to 0 so that creator suggestions
631 // should be lower priority than any actual onDeterminingFilename listeners.
633 // Ensure that the callback is called within a time limit.
634 weak_ptr_factory_
.reset(
635 new base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
>(this));
636 base::MessageLoopForUI::current()->PostDelayedTask(
638 base::Bind(&ExtensionDownloadsEventRouterData::DetermineFilenameTimeout
,
639 weak_ptr_factory_
->GetWeakPtr()),
640 base::TimeDelta::FromSeconds(determine_filename_timeout_s_
));
643 void DetermineFilenameTimeout() {
644 CallFilenameCallback();
647 void ClearPendingDeterminers() {
648 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
649 determined_filename_
.clear();
650 determined_conflict_action_
=
651 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
;
652 determiner_
= DeterminerInfo();
653 filename_no_change_
= base::Closure();
654 filename_change_
= ExtensionDownloadsEventRouter::FilenameChangedCallback();
655 weak_ptr_factory_
.reset();
656 determiners_
.clear();
659 void DeterminerRemoved(const std::string
& extension_id
) {
660 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
661 for (DeterminerInfoVector::iterator iter
= determiners_
.begin();
662 iter
!= determiners_
.end();) {
663 if (iter
->extension_id
== extension_id
) {
664 iter
= determiners_
.erase(iter
);
669 // If we just removed the last unreported determiner, then we need to call a
671 CheckAllDeterminersCalled();
674 void AddPendingDeterminer(const std::string
& extension_id
,
675 const base::Time
& installed
) {
676 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
677 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
678 if (determiners_
[index
].extension_id
== extension_id
) {
679 DCHECK(false) << extension_id
;
683 determiners_
.push_back(DeterminerInfo(extension_id
, installed
));
686 bool DeterminerAlreadyReported(const std::string
& extension_id
) {
687 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
688 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
689 if (determiners_
[index
].extension_id
== extension_id
) {
690 return determiners_
[index
].reported
;
696 void CreatorSuggestedFilename(
697 const base::FilePath
& filename
,
698 downloads::FilenameConflictAction conflict_action
) {
699 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
700 creator_suggested_filename_
= filename
;
701 creator_conflict_action_
= conflict_action
;
704 base::FilePath
creator_suggested_filename() const {
705 return creator_suggested_filename_
;
708 downloads::FilenameConflictAction
709 creator_conflict_action() const {
710 return creator_conflict_action_
;
713 void ResetCreatorSuggestion() {
714 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
715 creator_suggested_filename_
.clear();
716 creator_conflict_action_
=
717 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
;
720 // Returns false if this |extension_id| was not expected or if this
721 // |extension_id| has already reported. The caller is responsible for
722 // validating |filename|.
723 bool DeterminerCallback(
725 const std::string
& extension_id
,
726 const base::FilePath
& filename
,
727 downloads::FilenameConflictAction conflict_action
) {
728 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
729 bool found_info
= false;
730 for (size_t index
= 0; index
< determiners_
.size(); ++index
) {
731 if (determiners_
[index
].extension_id
== extension_id
) {
733 if (determiners_
[index
].reported
)
735 determiners_
[index
].reported
= true;
736 // Do not use filename if another determiner has already overridden the
737 // filename and they take precedence. Extensions that were installed
738 // later take precedence over previous extensions.
739 if (!filename
.empty() ||
740 (conflict_action
!= downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
741 extensions::ExtensionWarningSet warnings
;
742 std::string winner_extension_id
;
743 ExtensionDownloadsEventRouter::DetermineFilenameInternal(
746 determiners_
[index
].extension_id
,
747 determiners_
[index
].install_time
,
748 determiner_
.extension_id
,
749 determiner_
.install_time
,
750 &winner_extension_id
,
751 &determined_filename_
,
752 &determined_conflict_action_
,
754 if (!warnings
.empty())
755 extensions::ExtensionWarningService::NotifyWarningsOnUI(
757 if (winner_extension_id
== determiners_
[index
].extension_id
)
758 determiner_
= determiners_
[index
];
765 CheckAllDeterminersCalled();
770 static int determine_filename_timeout_s_
;
772 struct DeterminerInfo
{
774 DeterminerInfo(const std::string
& e_id
,
775 const base::Time
& installed
);
778 std::string extension_id
;
779 base::Time install_time
;
782 typedef std::vector
<DeterminerInfo
> DeterminerInfoVector
;
784 static const char kKey
[];
786 // This is safe to call even while not waiting for determiners to call back;
787 // in that case, the callbacks will be null so they won't be Run.
788 void CheckAllDeterminersCalled() {
789 for (DeterminerInfoVector::iterator iter
= determiners_
.begin();
790 iter
!= determiners_
.end(); ++iter
) {
794 CallFilenameCallback();
796 // Don't clear determiners_ immediately in case there's a second listener
797 // for one of the extensions, so that DetermineFilename can return
798 // kTooManyListeners. After a few seconds, DetermineFilename will return
799 // kUnexpectedDeterminer instead of kTooManyListeners so that determiners_
800 // doesn't keep hogging memory.
801 weak_ptr_factory_
.reset(
802 new base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
>(this));
803 base::MessageLoopForUI::current()->PostDelayedTask(
805 base::Bind(&ExtensionDownloadsEventRouterData::ClearPendingDeterminers
,
806 weak_ptr_factory_
->GetWeakPtr()),
807 base::TimeDelta::FromSeconds(15));
810 void CallFilenameCallback() {
811 if (determined_filename_
.empty() &&
812 (determined_conflict_action_
==
813 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
814 if (!filename_no_change_
.is_null())
815 filename_no_change_
.Run();
817 if (!filename_change_
.is_null()) {
818 filename_change_
.Run(determined_filename_
, ConvertConflictAction(
819 determined_conflict_action_
));
822 // Clear the callbacks immediately in case they aren't idempotent.
823 filename_no_change_
= base::Closure();
824 filename_change_
= ExtensionDownloadsEventRouter::FilenameChangedCallback();
830 scoped_ptr
<base::DictionaryValue
> json_
;
832 base::Closure filename_no_change_
;
833 ExtensionDownloadsEventRouter::FilenameChangedCallback filename_change_
;
835 DeterminerInfoVector determiners_
;
837 base::FilePath creator_suggested_filename_
;
838 downloads::FilenameConflictAction
839 creator_conflict_action_
;
840 base::FilePath determined_filename_
;
841 downloads::FilenameConflictAction
842 determined_conflict_action_
;
843 DeterminerInfo determiner_
;
845 scoped_ptr
<base::WeakPtrFactory
<ExtensionDownloadsEventRouterData
> >
848 DISALLOW_COPY_AND_ASSIGN(ExtensionDownloadsEventRouterData
);
851 int ExtensionDownloadsEventRouterData::determine_filename_timeout_s_
= 15;
853 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo(
854 const std::string
& e_id
,
855 const base::Time
& installed
)
856 : extension_id(e_id
),
857 install_time(installed
),
861 ExtensionDownloadsEventRouterData::DeterminerInfo::DeterminerInfo()
865 ExtensionDownloadsEventRouterData::DeterminerInfo::~DeterminerInfo() {}
867 const char ExtensionDownloadsEventRouterData::kKey
[] =
868 "DownloadItem ExtensionDownloadsEventRouterData";
870 class ManagerDestructionObserver
: public DownloadManager::Observer
{
872 static void CheckForHistoryFilesRemoval(DownloadManager
* manager
) {
875 if (!manager_file_existence_last_checked_
)
876 manager_file_existence_last_checked_
=
877 new std::map
<DownloadManager
*, ManagerDestructionObserver
*>();
878 if (!(*manager_file_existence_last_checked_
)[manager
])
879 (*manager_file_existence_last_checked_
)[manager
] =
880 new ManagerDestructionObserver(manager
);
881 (*manager_file_existence_last_checked_
)[manager
]->
882 CheckForHistoryFilesRemovalInternal();
886 static const int kFileExistenceRateLimitSeconds
= 10;
888 explicit ManagerDestructionObserver(DownloadManager
* manager
)
889 : manager_(manager
) {
890 manager_
->AddObserver(this);
893 virtual ~ManagerDestructionObserver() {
894 manager_
->RemoveObserver(this);
897 virtual void ManagerGoingDown(DownloadManager
* manager
) OVERRIDE
{
898 manager_file_existence_last_checked_
->erase(manager
);
899 if (manager_file_existence_last_checked_
->size() == 0) {
900 delete manager_file_existence_last_checked_
;
901 manager_file_existence_last_checked_
= NULL
;
905 void CheckForHistoryFilesRemovalInternal() {
906 base::Time
now(base::Time::Now());
907 int delta
= now
.ToTimeT() - last_checked_
.ToTimeT();
908 if (delta
> kFileExistenceRateLimitSeconds
) {
910 manager_
->CheckForHistoryFilesRemoval();
914 static std::map
<DownloadManager
*, ManagerDestructionObserver
*>*
915 manager_file_existence_last_checked_
;
917 DownloadManager
* manager_
;
918 base::Time last_checked_
;
920 DISALLOW_COPY_AND_ASSIGN(ManagerDestructionObserver
);
923 std::map
<DownloadManager
*, ManagerDestructionObserver
*>*
924 ManagerDestructionObserver::manager_file_existence_last_checked_
= NULL
;
926 void OnDeterminingFilenameWillDispatchCallback(
927 bool* any_determiners
,
928 ExtensionDownloadsEventRouterData
* data
,
929 content::BrowserContext
* context
,
930 const extensions::Extension
* extension
,
931 base::ListValue
* event_args
) {
932 *any_determiners
= true;
933 base::Time installed
=
934 extensions::ExtensionPrefs::Get(context
)->GetInstallTime(extension
->id());
935 data
->AddPendingDeterminer(extension
->id(), installed
);
938 bool Fault(bool error
,
939 const char* message_in
,
940 std::string
* message_out
) {
943 *message_out
= message_in
;
947 bool InvalidId(DownloadItem
* valid_item
, std::string
* message_out
) {
948 return Fault(!valid_item
, errors::kInvalidId
, message_out
);
951 bool IsDownloadDeltaField(const std::string
& field
) {
952 return ((field
== kUrlKey
) ||
953 (field
== kFilenameKey
) ||
954 (field
== kDangerKey
) ||
955 (field
== kMimeKey
) ||
956 (field
== kStartTimeKey
) ||
957 (field
== kEndTimeKey
) ||
958 (field
== kStateKey
) ||
959 (field
== kCanResumeKey
) ||
960 (field
== kPausedKey
) ||
961 (field
== kErrorKey
) ||
962 (field
== kTotalBytesKey
) ||
963 (field
== kFileSizeKey
) ||
964 (field
== kExistsKey
));
969 const char DownloadedByExtension::kKey
[] =
970 "DownloadItem DownloadedByExtension";
972 DownloadedByExtension
* DownloadedByExtension::Get(
973 content::DownloadItem
* item
) {
974 base::SupportsUserData::Data
* data
= item
->GetUserData(kKey
);
975 return (data
== NULL
) ? NULL
:
976 static_cast<DownloadedByExtension
*>(data
);
979 DownloadedByExtension::DownloadedByExtension(
980 content::DownloadItem
* item
,
981 const std::string
& id
,
982 const std::string
& name
)
985 item
->SetUserData(kKey
, this);
988 DownloadsDownloadFunction::DownloadsDownloadFunction() {}
990 DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
992 bool DownloadsDownloadFunction::RunImpl() {
993 scoped_ptr
<downloads::Download::Params
> params(
994 downloads::Download::Params::Create(*args_
));
995 EXTENSION_FUNCTION_VALIDATE(params
.get());
996 const downloads::DownloadOptions
& options
= params
->options
;
997 GURL
download_url(options
.url
);
998 if (Fault(!download_url
.is_valid(), errors::kInvalidURL
, &error_
))
1001 Profile
* current_profile
= GetProfile();
1002 if (include_incognito() && GetProfile()->HasOffTheRecordProfile())
1003 current_profile
= GetProfile()->GetOffTheRecordProfile();
1005 scoped_ptr
<content::DownloadUrlParameters
> download_params(
1006 new content::DownloadUrlParameters(
1008 render_view_host()->GetProcess()->GetID(),
1009 render_view_host()->GetRoutingID(),
1010 current_profile
->GetResourceContext()));
1012 base::FilePath creator_suggested_filename
;
1013 if (options
.filename
.get()) {
1015 // Can't get filename16 from options.ToValue() because that converts it from
1017 base::DictionaryValue
* options_value
= NULL
;
1018 EXTENSION_FUNCTION_VALIDATE(args_
->GetDictionary(0, &options_value
));
1019 base::string16 filename16
;
1020 EXTENSION_FUNCTION_VALIDATE(options_value
->GetString(
1021 kFilenameKey
, &filename16
));
1022 creator_suggested_filename
= base::FilePath(filename16
);
1023 #elif defined(OS_POSIX)
1024 creator_suggested_filename
= base::FilePath(*options
.filename
.get());
1026 if (!net::IsSafePortableRelativePath(creator_suggested_filename
)) {
1027 error_
= errors::kInvalidFilename
;
1032 if (options
.save_as
.get())
1033 download_params
->set_prompt(*options
.save_as
.get());
1035 if (options
.headers
.get()) {
1036 typedef downloads::HeaderNameValuePair HeaderNameValuePair
;
1037 for (std::vector
<linked_ptr
<HeaderNameValuePair
> >::const_iterator iter
=
1038 options
.headers
->begin();
1039 iter
!= options
.headers
->end();
1041 const HeaderNameValuePair
& name_value
= **iter
;
1042 if (!net::HttpUtil::IsSafeHeader(name_value
.name
)) {
1043 error_
= errors::kInvalidHeader
;
1046 download_params
->add_request_header(name_value
.name
, name_value
.value
);
1050 std::string method_string
=
1051 downloads::ToString(options
.method
);
1052 if (!method_string
.empty())
1053 download_params
->set_method(method_string
);
1054 if (options
.body
.get())
1055 download_params
->set_post_body(*options
.body
.get());
1056 download_params
->set_callback(base::Bind(
1057 &DownloadsDownloadFunction::OnStarted
, this,
1058 creator_suggested_filename
, options
.conflict_action
));
1059 // Prevent login prompts for 401/407 responses.
1060 download_params
->set_load_flags(net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
);
1062 DownloadManager
* manager
= BrowserContext::GetDownloadManager(
1064 manager
->DownloadUrl(download_params
.Pass());
1065 RecordDownloadSource(DOWNLOAD_INITIATED_BY_EXTENSION
);
1066 RecordApiFunctions(DOWNLOADS_FUNCTION_DOWNLOAD
);
1070 void DownloadsDownloadFunction::OnStarted(
1071 const base::FilePath
& creator_suggested_filename
,
1072 downloads::FilenameConflictAction creator_conflict_action
,
1074 content::DownloadInterruptReason interrupt_reason
) {
1075 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1076 VLOG(1) << __FUNCTION__
<< " " << item
<< " " << interrupt_reason
;
1078 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1079 SetResult(new base::FundamentalValue(static_cast<int>(item
->GetId())));
1080 if (!creator_suggested_filename
.empty() ||
1081 (creator_conflict_action
!=
1082 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
1083 ExtensionDownloadsEventRouterData
* data
=
1084 ExtensionDownloadsEventRouterData::Get(item
);
1086 data
= new ExtensionDownloadsEventRouterData(
1088 scoped_ptr
<base::DictionaryValue
>(new base::DictionaryValue()));
1090 data
->CreatorSuggestedFilename(
1091 creator_suggested_filename
, creator_conflict_action
);
1093 new DownloadedByExtension(
1094 item
, GetExtension()->id(), GetExtension()->name());
1095 item
->UpdateObservers();
1097 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
1098 error_
= content::DownloadInterruptReasonToString(interrupt_reason
);
1100 SendResponse(error_
.empty());
1103 DownloadsSearchFunction::DownloadsSearchFunction() {}
1105 DownloadsSearchFunction::~DownloadsSearchFunction() {}
1107 bool DownloadsSearchFunction::RunSync() {
1108 scoped_ptr
<downloads::Search::Params
> params(
1109 downloads::Search::Params::Create(*args_
));
1110 EXTENSION_FUNCTION_VALIDATE(params
.get());
1111 DownloadManager
* manager
= NULL
;
1112 DownloadManager
* incognito_manager
= NULL
;
1113 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1114 ManagerDestructionObserver::CheckForHistoryFilesRemoval(manager
);
1115 ManagerDestructionObserver::CheckForHistoryFilesRemoval(incognito_manager
);
1116 DownloadQuery::DownloadVector results
;
1117 RunDownloadQuery(params
->query
,
1122 if (!error_
.empty())
1125 base::ListValue
* json_results
= new base::ListValue();
1126 for (DownloadManager::DownloadVector::const_iterator it
= results
.begin();
1127 it
!= results
.end(); ++it
) {
1128 DownloadItem
* download_item
= *it
;
1129 uint32 download_id
= download_item
->GetId();
1130 bool off_record
= ((incognito_manager
!= NULL
) &&
1131 (incognito_manager
->GetDownload(download_id
) != NULL
));
1132 scoped_ptr
<base::DictionaryValue
> json_item(
1133 DownloadItemToJSON(*it
,
1134 off_record
? GetProfile()->GetOffTheRecordProfile()
1135 : GetProfile()->GetOriginalProfile()));
1136 json_results
->Append(json_item
.release());
1138 SetResult(json_results
);
1139 RecordApiFunctions(DOWNLOADS_FUNCTION_SEARCH
);
1143 DownloadsPauseFunction::DownloadsPauseFunction() {}
1145 DownloadsPauseFunction::~DownloadsPauseFunction() {}
1147 bool DownloadsPauseFunction::RunSync() {
1148 scoped_ptr
<downloads::Pause::Params
> params(
1149 downloads::Pause::Params::Create(*args_
));
1150 EXTENSION_FUNCTION_VALIDATE(params
.get());
1151 DownloadItem
* download_item
=
1152 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1153 if (InvalidId(download_item
, &error_
) ||
1154 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1155 errors::kNotInProgress
, &error_
))
1157 // If the item is already paused, this is a no-op and the operation will
1158 // silently succeed.
1159 download_item
->Pause();
1160 RecordApiFunctions(DOWNLOADS_FUNCTION_PAUSE
);
1164 DownloadsResumeFunction::DownloadsResumeFunction() {}
1166 DownloadsResumeFunction::~DownloadsResumeFunction() {}
1168 bool DownloadsResumeFunction::RunSync() {
1169 scoped_ptr
<downloads::Resume::Params
> params(
1170 downloads::Resume::Params::Create(*args_
));
1171 EXTENSION_FUNCTION_VALIDATE(params
.get());
1172 DownloadItem
* download_item
=
1173 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1174 if (InvalidId(download_item
, &error_
) ||
1175 Fault(download_item
->IsPaused() && !download_item
->CanResume(),
1176 errors::kNotResumable
, &error_
))
1178 // Note that if the item isn't paused, this will be a no-op, and the extension
1179 // call will seem successful.
1180 download_item
->Resume();
1181 RecordApiFunctions(DOWNLOADS_FUNCTION_RESUME
);
1185 DownloadsCancelFunction::DownloadsCancelFunction() {}
1187 DownloadsCancelFunction::~DownloadsCancelFunction() {}
1189 bool DownloadsCancelFunction::RunSync() {
1190 scoped_ptr
<downloads::Resume::Params
> params(
1191 downloads::Resume::Params::Create(*args_
));
1192 EXTENSION_FUNCTION_VALIDATE(params
.get());
1193 DownloadItem
* download_item
=
1194 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1195 if (download_item
&&
1196 (download_item
->GetState() == DownloadItem::IN_PROGRESS
))
1197 download_item
->Cancel(true);
1198 // |download_item| can be NULL if the download ID was invalid or if the
1199 // download is not currently active. Either way, it's not a failure.
1200 RecordApiFunctions(DOWNLOADS_FUNCTION_CANCEL
);
1204 DownloadsEraseFunction::DownloadsEraseFunction() {}
1206 DownloadsEraseFunction::~DownloadsEraseFunction() {}
1208 bool DownloadsEraseFunction::RunSync() {
1209 scoped_ptr
<downloads::Erase::Params
> params(
1210 downloads::Erase::Params::Create(*args_
));
1211 EXTENSION_FUNCTION_VALIDATE(params
.get());
1212 DownloadManager
* manager
= NULL
;
1213 DownloadManager
* incognito_manager
= NULL
;
1214 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1215 DownloadQuery::DownloadVector results
;
1216 RunDownloadQuery(params
->query
,
1221 if (!error_
.empty())
1223 base::ListValue
* json_results
= new base::ListValue();
1224 for (DownloadManager::DownloadVector::const_iterator it
= results
.begin();
1225 it
!= results
.end(); ++it
) {
1226 json_results
->Append(
1227 new base::FundamentalValue(static_cast<int>((*it
)->GetId())));
1230 SetResult(json_results
);
1231 RecordApiFunctions(DOWNLOADS_FUNCTION_ERASE
);
1235 DownloadsRemoveFileFunction::DownloadsRemoveFileFunction() {
1238 DownloadsRemoveFileFunction::~DownloadsRemoveFileFunction() {
1241 bool DownloadsRemoveFileFunction::RunImpl() {
1242 scoped_ptr
<downloads::RemoveFile::Params
> params(
1243 downloads::RemoveFile::Params::Create(*args_
));
1244 EXTENSION_FUNCTION_VALIDATE(params
.get());
1245 DownloadItem
* download_item
=
1246 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1247 if (InvalidId(download_item
, &error_
) ||
1248 Fault((download_item
->GetState() != DownloadItem::COMPLETE
),
1249 errors::kNotComplete
, &error_
) ||
1250 Fault(download_item
->GetFileExternallyRemoved(),
1251 errors::kFileAlreadyDeleted
, &error_
))
1253 RecordApiFunctions(DOWNLOADS_FUNCTION_REMOVE_FILE
);
1254 download_item
->DeleteFile(
1255 base::Bind(&DownloadsRemoveFileFunction::Done
, this));
1259 void DownloadsRemoveFileFunction::Done(bool success
) {
1260 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1262 error_
= errors::kFileNotRemoved
;
1264 SendResponse(error_
.empty());
1267 DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction() {}
1269 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
1271 DownloadsAcceptDangerFunction::OnPromptCreatedCallback
*
1272 DownloadsAcceptDangerFunction::on_prompt_created_
= NULL
;
1274 bool DownloadsAcceptDangerFunction::RunImpl() {
1275 scoped_ptr
<downloads::AcceptDanger::Params
> params(
1276 downloads::AcceptDanger::Params::Create(*args_
));
1277 EXTENSION_FUNCTION_VALIDATE(params
.get());
1278 PromptOrWait(params
->download_id
, 10);
1282 void DownloadsAcceptDangerFunction::PromptOrWait(int download_id
, int retries
) {
1283 DownloadItem
* download_item
=
1284 GetDownload(GetProfile(), include_incognito(), download_id
);
1285 content::WebContents
* web_contents
=
1286 dispatcher()->delegate()->GetVisibleWebContents();
1287 if (InvalidId(download_item
, &error_
) ||
1288 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1289 errors::kNotInProgress
, &error_
) ||
1290 Fault(!download_item
->IsDangerous(), errors::kNotDangerous
, &error_
) ||
1291 Fault(!web_contents
, errors::kInvisibleContext
, &error_
)) {
1292 SendResponse(error_
.empty());
1295 bool visible
= platform_util::IsVisible(
1296 web_contents
->GetView()->GetNativeView());
1299 base::MessageLoopForUI::current()->PostDelayedTask(
1301 base::Bind(&DownloadsAcceptDangerFunction::PromptOrWait
,
1302 this, download_id
, retries
- 1),
1303 base::TimeDelta::FromMilliseconds(100));
1306 error_
= errors::kInvisibleContext
;
1307 SendResponse(error_
.empty());
1310 RecordApiFunctions(DOWNLOADS_FUNCTION_ACCEPT_DANGER
);
1311 // DownloadDangerPrompt displays a modal dialog using native widgets that the
1312 // user must either accept or cancel. It cannot be scripted.
1313 DownloadDangerPrompt
* prompt
= DownloadDangerPrompt::Create(
1317 base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback
,
1318 this, download_id
));
1319 // DownloadDangerPrompt deletes itself
1320 if (on_prompt_created_
&& !on_prompt_created_
->is_null())
1321 on_prompt_created_
->Run(prompt
);
1322 SendResponse(error_
.empty());
1325 void DownloadsAcceptDangerFunction::DangerPromptCallback(
1326 int download_id
, DownloadDangerPrompt::Action action
) {
1327 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1328 DownloadItem
* download_item
=
1329 GetDownload(GetProfile(), include_incognito(), download_id
);
1330 if (InvalidId(download_item
, &error_
) ||
1331 Fault(download_item
->GetState() != DownloadItem::IN_PROGRESS
,
1332 errors::kNotInProgress
, &error_
))
1335 case DownloadDangerPrompt::ACCEPT
:
1336 download_item
->ValidateDangerousDownload();
1338 case DownloadDangerPrompt::CANCEL
:
1339 download_item
->Remove();
1341 case DownloadDangerPrompt::DISMISS
:
1344 SendResponse(error_
.empty());
1347 DownloadsShowFunction::DownloadsShowFunction() {}
1349 DownloadsShowFunction::~DownloadsShowFunction() {}
1351 bool DownloadsShowFunction::RunImpl() {
1352 scoped_ptr
<downloads::Show::Params
> params(
1353 downloads::Show::Params::Create(*args_
));
1354 EXTENSION_FUNCTION_VALIDATE(params
.get());
1355 DownloadItem
* download_item
=
1356 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1357 if (InvalidId(download_item
, &error_
))
1359 download_item
->ShowDownloadInShell();
1360 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW
);
1364 DownloadsShowDefaultFolderFunction::DownloadsShowDefaultFolderFunction() {}
1366 DownloadsShowDefaultFolderFunction::~DownloadsShowDefaultFolderFunction() {}
1368 bool DownloadsShowDefaultFolderFunction::RunImpl() {
1369 DownloadManager
* manager
= NULL
;
1370 DownloadManager
* incognito_manager
= NULL
;
1371 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1372 platform_util::OpenItem(
1374 DownloadPrefs::FromDownloadManager(manager
)->DownloadPath());
1375 RecordApiFunctions(DOWNLOADS_FUNCTION_SHOW_DEFAULT_FOLDER
);
1379 DownloadsOpenFunction::DownloadsOpenFunction() {}
1381 DownloadsOpenFunction::~DownloadsOpenFunction() {}
1383 bool DownloadsOpenFunction::RunSync() {
1384 scoped_ptr
<downloads::Open::Params
> params(
1385 downloads::Open::Params::Create(*args_
));
1386 EXTENSION_FUNCTION_VALIDATE(params
.get());
1387 DownloadItem
* download_item
=
1388 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1389 if (InvalidId(download_item
, &error_
) ||
1390 Fault(!user_gesture(), errors::kUserGesture
, &error_
) ||
1391 Fault(download_item
->GetState() != DownloadItem::COMPLETE
,
1392 errors::kNotComplete
, &error_
) ||
1393 Fault(!GetExtension()->HasAPIPermission(
1394 extensions::APIPermission::kDownloadsOpen
),
1395 errors::kOpenPermission
, &error_
))
1397 download_item
->OpenDownload();
1398 RecordApiFunctions(DOWNLOADS_FUNCTION_OPEN
);
1402 DownloadsDragFunction::DownloadsDragFunction() {}
1404 DownloadsDragFunction::~DownloadsDragFunction() {}
1406 bool DownloadsDragFunction::RunImpl() {
1407 scoped_ptr
<downloads::Drag::Params
> params(
1408 downloads::Drag::Params::Create(*args_
));
1409 EXTENSION_FUNCTION_VALIDATE(params
.get());
1410 DownloadItem
* download_item
=
1411 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1412 content::WebContents
* web_contents
=
1413 dispatcher()->delegate()->GetVisibleWebContents();
1414 if (InvalidId(download_item
, &error_
) ||
1415 Fault(!web_contents
, errors::kInvisibleContext
, &error_
))
1417 RecordApiFunctions(DOWNLOADS_FUNCTION_DRAG
);
1418 gfx::Image
* icon
= g_browser_process
->icon_manager()->LookupIconFromFilepath(
1419 download_item
->GetTargetFilePath(), IconLoader::NORMAL
);
1420 gfx::NativeView view
= web_contents
->GetView()->GetNativeView();
1422 // Enable nested tasks during DnD, while |DragDownload()| blocks.
1423 base::MessageLoop::ScopedNestableTaskAllower
allow(
1424 base::MessageLoop::current());
1425 DragDownloadItem(download_item
, icon
, view
);
1430 DownloadsSetShelfEnabledFunction::DownloadsSetShelfEnabledFunction() {}
1432 DownloadsSetShelfEnabledFunction::~DownloadsSetShelfEnabledFunction() {}
1434 bool DownloadsSetShelfEnabledFunction::RunSync() {
1435 scoped_ptr
<downloads::SetShelfEnabled::Params
> params(
1436 downloads::SetShelfEnabled::Params::Create(*args_
));
1437 EXTENSION_FUNCTION_VALIDATE(params
.get());
1438 if (!GetExtension()->HasAPIPermission(
1439 extensions::APIPermission::kDownloadsShelf
)) {
1440 error_
= download_extension_errors::kShelfPermission
;
1444 RecordApiFunctions(DOWNLOADS_FUNCTION_SET_SHELF_ENABLED
);
1445 DownloadManager
* manager
= NULL
;
1446 DownloadManager
* incognito_manager
= NULL
;
1447 GetManagers(GetProfile(), include_incognito(), &manager
, &incognito_manager
);
1448 DownloadService
* service
= NULL
;
1449 DownloadService
* incognito_service
= NULL
;
1451 service
= DownloadServiceFactory::GetForBrowserContext(
1452 manager
->GetBrowserContext());
1453 service
->GetExtensionEventRouter()->SetShelfEnabled(
1454 GetExtension(), params
->enabled
);
1456 if (incognito_manager
) {
1457 incognito_service
= DownloadServiceFactory::GetForBrowserContext(
1458 incognito_manager
->GetBrowserContext());
1459 incognito_service
->GetExtensionEventRouter()->SetShelfEnabled(
1460 GetExtension(), params
->enabled
);
1463 BrowserList
* browsers
= BrowserList::GetInstance(chrome::GetActiveDesktop());
1465 for (BrowserList::const_iterator iter
= browsers
->begin();
1466 iter
!= browsers
->end(); ++iter
) {
1467 const Browser
* browser
= *iter
;
1468 DownloadService
* current_service
=
1469 DownloadServiceFactory::GetForBrowserContext(browser
->profile());
1470 if (((current_service
== service
) ||
1471 (current_service
== incognito_service
)) &&
1472 browser
->window()->IsDownloadShelfVisible() &&
1473 !current_service
->IsShelfEnabled())
1474 browser
->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC
);
1478 if (params
->enabled
&&
1479 ((manager
&& !service
->IsShelfEnabled()) ||
1480 (incognito_manager
&& !incognito_service
->IsShelfEnabled()))) {
1481 error_
= download_extension_errors::kShelfDisabled
;
1488 DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
1489 : icon_extractor_(new DownloadFileIconExtractorImpl()) {
1492 DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
1494 void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
1495 DownloadFileIconExtractor
* extractor
) {
1497 icon_extractor_
.reset(extractor
);
1500 bool DownloadsGetFileIconFunction::RunImpl() {
1501 scoped_ptr
<downloads::GetFileIcon::Params
> params(
1502 downloads::GetFileIcon::Params::Create(*args_
));
1503 EXTENSION_FUNCTION_VALIDATE(params
.get());
1504 const downloads::GetFileIconOptions
* options
=
1505 params
->options
.get();
1506 int icon_size
= kDefaultIconSize
;
1507 if (options
&& options
->size
.get())
1508 icon_size
= *options
->size
.get();
1509 DownloadItem
* download_item
=
1510 GetDownload(GetProfile(), include_incognito(), params
->download_id
);
1511 if (InvalidId(download_item
, &error_
) ||
1512 Fault(download_item
->GetTargetFilePath().empty(),
1513 errors::kEmptyFile
, &error_
))
1515 // In-progress downloads return the intermediate filename for GetFullPath()
1516 // which doesn't have the final extension. Therefore a good file icon can't be
1517 // found, so use GetTargetFilePath() instead.
1518 DCHECK(icon_extractor_
.get());
1519 DCHECK(icon_size
== 16 || icon_size
== 32);
1521 content::WebContents
* web_contents
=
1522 dispatcher()->delegate()->GetVisibleWebContents();
1524 scale
= ui::GetImageScale(ui::GetScaleFactorForNativeView(
1525 web_contents
->GetRenderWidgetHostView()->GetNativeView()));
1527 EXTENSION_FUNCTION_VALIDATE(icon_extractor_
->ExtractIconURLForPath(
1528 download_item
->GetTargetFilePath(),
1530 IconLoaderSizeFromPixelSize(icon_size
),
1531 base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted
, this)));
1535 void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string
& url
) {
1536 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1537 if (Fault(url
.empty(), errors::kIconNotFound
, &error_
)) {
1538 SendResponse(false);
1541 RecordApiFunctions(DOWNLOADS_FUNCTION_GET_FILE_ICON
);
1542 SetResult(new base::StringValue(url
));
1546 ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(
1548 DownloadManager
* manager
)
1549 : profile_(profile
),
1550 notifier_(manager
, this),
1551 extension_registry_observer_(this) {
1552 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1554 extension_registry_observer_
.Add(
1555 extensions::ExtensionRegistry::Get(profile_
));
1556 extensions::EventRouter
* router
= extensions::EventRouter::Get(profile_
);
1558 router
->RegisterObserver(this,
1559 downloads::OnDeterminingFilename::kEventName
);
1562 ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
1563 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1564 extensions::EventRouter
* router
= extensions::EventRouter::Get(profile_
);
1566 router
->UnregisterObserver(this);
1569 void ExtensionDownloadsEventRouter::
1570 SetDetermineFilenameTimeoutSecondsForTesting(int s
) {
1571 ExtensionDownloadsEventRouterData::
1572 SetDetermineFilenameTimeoutSecondsForTesting(s
);
1575 void ExtensionDownloadsEventRouter::SetShelfEnabled(
1576 const extensions::Extension
* extension
, bool enabled
) {
1577 std::set
<const extensions::Extension
*>::iterator iter
=
1578 shelf_disabling_extensions_
.find(extension
);
1579 if (iter
== shelf_disabling_extensions_
.end()) {
1581 shelf_disabling_extensions_
.insert(extension
);
1582 } else if (enabled
) {
1583 shelf_disabling_extensions_
.erase(extension
);
1587 bool ExtensionDownloadsEventRouter::IsShelfEnabled() const {
1588 return shelf_disabling_extensions_
.empty();
1591 // The method by which extensions hook into the filename determination process
1592 // is based on the method by which the omnibox API allows extensions to hook
1593 // into the omnibox autocompletion process. Extensions that wish to play a part
1594 // in the filename determination process call
1595 // chrome.downloads.onDeterminingFilename.addListener, which adds an
1596 // EventListener object to ExtensionEventRouter::listeners().
1598 // When a download's filename is being determined, DownloadTargetDeterminer (via
1599 // ChromeDownloadManagerDelegate (CDMD) ::NotifyExtensions()) passes 2 callbacks
1600 // to ExtensionDownloadsEventRouter::OnDeterminingFilename (ODF), which stores
1601 // the callbacks in the item's ExtensionDownloadsEventRouterData (EDERD) along
1602 // with all of the extension IDs that are listening for onDeterminingFilename
1603 // events. ODF dispatches chrome.downloads.onDeterminingFilename.
1605 // When the extension's event handler calls |suggestCallback|,
1606 // downloads_custom_bindings.js calls
1607 // DownloadsInternalDetermineFilenameFunction::RunImpl, which calls
1608 // EDER::DetermineFilename, which notifies the item's EDERD.
1610 // When the last extension's event handler returns, EDERD calls one of the two
1611 // callbacks that CDMD passed to ODF, allowing DownloadTargetDeterminer to
1612 // continue the filename determination process. If multiple extensions wish to
1613 // override the filename, then the extension that was last installed wins.
1615 void ExtensionDownloadsEventRouter::OnDeterminingFilename(
1617 const base::FilePath
& suggested_path
,
1618 const base::Closure
& no_change
,
1619 const FilenameChangedCallback
& change
) {
1620 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1621 ExtensionDownloadsEventRouterData
* data
=
1622 ExtensionDownloadsEventRouterData::Get(item
);
1627 data
->BeginFilenameDetermination(no_change
, change
);
1628 bool any_determiners
= false;
1629 base::DictionaryValue
* json
= DownloadItemToJSON(
1630 item
, profile_
).release();
1631 json
->SetString(kFilenameKey
, suggested_path
.LossyDisplayName());
1632 DispatchEvent(downloads::OnDeterminingFilename::kEventName
,
1634 base::Bind(&OnDeterminingFilenameWillDispatchCallback
,
1638 if (!any_determiners
) {
1639 data
->ClearPendingDeterminers();
1640 if (!data
->creator_suggested_filename().empty() ||
1641 (data
->creator_conflict_action() !=
1642 downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
)) {
1643 change
.Run(data
->creator_suggested_filename(),
1644 ConvertConflictAction(data
->creator_conflict_action()));
1645 // If all listeners are removed, don't keep |data| around.
1646 data
->ResetCreatorSuggestion();
1653 void ExtensionDownloadsEventRouter::DetermineFilenameInternal(
1654 const base::FilePath
& filename
,
1655 downloads::FilenameConflictAction conflict_action
,
1656 const std::string
& suggesting_extension_id
,
1657 const base::Time
& suggesting_install_time
,
1658 const std::string
& incumbent_extension_id
,
1659 const base::Time
& incumbent_install_time
,
1660 std::string
* winner_extension_id
,
1661 base::FilePath
* determined_filename
,
1662 downloads::FilenameConflictAction
*
1663 determined_conflict_action
,
1664 extensions::ExtensionWarningSet
* warnings
) {
1665 DCHECK(!filename
.empty() ||
1666 (conflict_action
!= downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY
));
1667 DCHECK(!suggesting_extension_id
.empty());
1669 if (incumbent_extension_id
.empty()) {
1670 *winner_extension_id
= suggesting_extension_id
;
1671 *determined_filename
= filename
;
1672 *determined_conflict_action
= conflict_action
;
1676 if (suggesting_install_time
< incumbent_install_time
) {
1677 *winner_extension_id
= incumbent_extension_id
;
1679 extensions::ExtensionWarning::CreateDownloadFilenameConflictWarning(
1680 suggesting_extension_id
,
1681 incumbent_extension_id
,
1683 *determined_filename
));
1687 *winner_extension_id
= suggesting_extension_id
;
1689 extensions::ExtensionWarning::CreateDownloadFilenameConflictWarning(
1690 incumbent_extension_id
,
1691 suggesting_extension_id
,
1692 *determined_filename
,
1694 *determined_filename
= filename
;
1695 *determined_conflict_action
= conflict_action
;
1698 bool ExtensionDownloadsEventRouter::DetermineFilename(
1700 bool include_incognito
,
1701 const std::string
& ext_id
,
1703 const base::FilePath
& const_filename
,
1704 downloads::FilenameConflictAction conflict_action
,
1705 std::string
* error
) {
1706 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1707 RecordApiFunctions(DOWNLOADS_FUNCTION_DETERMINE_FILENAME
);
1708 DownloadItem
* item
= GetDownload(profile
, include_incognito
, download_id
);
1709 ExtensionDownloadsEventRouterData
* data
=
1710 item
? ExtensionDownloadsEventRouterData::Get(item
) : NULL
;
1711 // maxListeners=1 in downloads.idl and suggestCallback in
1712 // downloads_custom_bindings.js should prevent duplicate DeterminerCallback
1713 // calls from the same renderer, but an extension may have more than one
1714 // renderer, so don't DCHECK(!reported).
1715 if (InvalidId(item
, error
) ||
1716 Fault(item
->GetState() != DownloadItem::IN_PROGRESS
,
1717 errors::kNotInProgress
, error
) ||
1718 Fault(!data
, errors::kUnexpectedDeterminer
, error
) ||
1719 Fault(data
->DeterminerAlreadyReported(ext_id
),
1720 errors::kTooManyListeners
, error
))
1722 base::FilePath::StringType
filename_str(const_filename
.value());
1723 // Allow windows-style directory separators on all platforms.
1724 std::replace(filename_str
.begin(), filename_str
.end(),
1725 FILE_PATH_LITERAL('\\'), FILE_PATH_LITERAL('/'));
1726 base::FilePath
filename(filename_str
);
1727 bool valid_filename
= net::IsSafePortableRelativePath(filename
);
1728 filename
= (valid_filename
? filename
.NormalizePathSeparators() :
1730 // If the invalid filename check is moved to before DeterminerCallback(), then
1731 // it will block forever waiting for this ext_id to report.
1732 if (Fault(!data
->DeterminerCallback(
1733 profile
, ext_id
, filename
, conflict_action
),
1734 errors::kUnexpectedDeterminer
, error
) ||
1735 Fault((!const_filename
.empty() && !valid_filename
),
1736 errors::kInvalidFilename
, error
))
1741 void ExtensionDownloadsEventRouter::OnListenerRemoved(
1742 const extensions::EventListenerInfo
& details
) {
1743 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1744 DownloadManager
* manager
= notifier_
.GetManager();
1747 bool determiner_removed
= (
1748 details
.event_name
== downloads::OnDeterminingFilename::kEventName
);
1749 extensions::EventRouter
* router
= extensions::ExtensionSystem::Get(profile_
)->
1751 bool any_listeners
=
1752 router
->HasEventListener(downloads::OnChanged::kEventName
) ||
1753 router
->HasEventListener(downloads::OnDeterminingFilename::kEventName
);
1754 if (!determiner_removed
&& any_listeners
)
1756 DownloadManager::DownloadVector items
;
1757 manager
->GetAllDownloads(&items
);
1758 for (DownloadManager::DownloadVector::const_iterator iter
=
1760 iter
!= items
.end(); ++iter
) {
1761 ExtensionDownloadsEventRouterData
* data
=
1762 ExtensionDownloadsEventRouterData::Get(*iter
);
1765 if (determiner_removed
) {
1766 // Notify any items that may be waiting for callbacks from this
1767 // extension/determiner. This will almost always be a no-op, however, it
1768 // is possible for an extension renderer to be unloaded while a download
1769 // item is waiting for a determiner. In that case, the download item
1771 data
->DeterminerRemoved(details
.extension_id
);
1773 if (!any_listeners
&&
1774 data
->creator_suggested_filename().empty()) {
1775 ExtensionDownloadsEventRouterData::Remove(*iter
);
1780 // That's all the methods that have to do with filename determination. The rest
1781 // have to do with the other, less special events.
1783 void ExtensionDownloadsEventRouter::OnDownloadCreated(
1784 DownloadManager
* manager
, DownloadItem
* download_item
) {
1785 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1786 if (download_item
->IsTemporary())
1789 extensions::EventRouter
* router
= extensions::ExtensionSystem::Get(profile_
)->
1791 // Avoid allocating a bunch of memory in DownloadItemToJSON if it isn't going
1794 (!router
->HasEventListener(downloads::OnCreated::kEventName
) &&
1795 !router
->HasEventListener(downloads::OnChanged::kEventName
) &&
1796 !router
->HasEventListener(
1797 downloads::OnDeterminingFilename::kEventName
))) {
1800 scoped_ptr
<base::DictionaryValue
> json_item(
1801 DownloadItemToJSON(download_item
, profile_
));
1802 DispatchEvent(downloads::OnCreated::kEventName
,
1804 extensions::Event::WillDispatchCallback(),
1805 json_item
->DeepCopy());
1806 if (!ExtensionDownloadsEventRouterData::Get(download_item
) &&
1807 (router
->HasEventListener(downloads::OnChanged::kEventName
) ||
1808 router
->HasEventListener(
1809 downloads::OnDeterminingFilename::kEventName
))) {
1810 new ExtensionDownloadsEventRouterData(download_item
, json_item
.Pass());
1814 void ExtensionDownloadsEventRouter::OnDownloadUpdated(
1815 DownloadManager
* manager
, DownloadItem
* download_item
) {
1816 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1817 extensions::EventRouter
* router
= extensions::ExtensionSystem::Get(profile_
)->
1819 ExtensionDownloadsEventRouterData
* data
=
1820 ExtensionDownloadsEventRouterData::Get(download_item
);
1821 if (download_item
->IsTemporary() ||
1822 !router
->HasEventListener(downloads::OnChanged::kEventName
)) {
1826 // The download_item probably transitioned from temporary to not temporary,
1827 // or else an event listener was added.
1828 data
= new ExtensionDownloadsEventRouterData(
1830 scoped_ptr
<base::DictionaryValue
>(new base::DictionaryValue()));
1832 scoped_ptr
<base::DictionaryValue
> new_json(DownloadItemToJSON(
1833 download_item
, profile_
));
1834 scoped_ptr
<base::DictionaryValue
> delta(new base::DictionaryValue());
1835 delta
->SetInteger(kIdKey
, download_item
->GetId());
1836 std::set
<std::string
> new_fields
;
1837 bool changed
= false;
1839 // For each field in the new json representation of the download_item except
1840 // the bytesReceived field, if the field has changed from the previous old
1841 // json, set the differences in the |delta| object and remember that something
1842 // significant changed.
1843 for (base::DictionaryValue::Iterator
iter(*new_json
.get());
1844 !iter
.IsAtEnd(); iter
.Advance()) {
1845 new_fields
.insert(iter
.key());
1846 if (IsDownloadDeltaField(iter
.key())) {
1847 const base::Value
* old_value
= NULL
;
1848 if (!data
->json().HasKey(iter
.key()) ||
1849 (data
->json().Get(iter
.key(), &old_value
) &&
1850 !iter
.value().Equals(old_value
))) {
1851 delta
->Set(iter
.key() + ".current", iter
.value().DeepCopy());
1853 delta
->Set(iter
.key() + ".previous", old_value
->DeepCopy());
1859 // If a field was in the previous json but is not in the new json, set the
1860 // difference in |delta|.
1861 for (base::DictionaryValue::Iterator
iter(data
->json());
1862 !iter
.IsAtEnd(); iter
.Advance()) {
1863 if ((new_fields
.find(iter
.key()) == new_fields
.end()) &&
1864 IsDownloadDeltaField(iter
.key())) {
1865 // estimatedEndTime disappears after completion, but bytesReceived stays.
1866 delta
->Set(iter
.key() + ".previous", iter
.value().DeepCopy());
1871 // Update the OnChangedStat and dispatch the event if something significant
1872 // changed. Replace the stored json with the new json.
1873 data
->OnItemUpdated();
1875 DispatchEvent(downloads::OnChanged::kEventName
,
1877 extensions::Event::WillDispatchCallback(),
1879 data
->OnChangedFired();
1881 data
->set_json(new_json
.Pass());
1884 void ExtensionDownloadsEventRouter::OnDownloadRemoved(
1885 DownloadManager
* manager
, DownloadItem
* download_item
) {
1886 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1887 if (download_item
->IsTemporary())
1889 DispatchEvent(downloads::OnErased::kEventName
,
1891 extensions::Event::WillDispatchCallback(),
1892 new base::FundamentalValue(
1893 static_cast<int>(download_item
->GetId())));
1896 void ExtensionDownloadsEventRouter::DispatchEvent(
1897 const std::string
& event_name
,
1898 bool include_incognito
,
1899 const extensions::Event::WillDispatchCallback
& will_dispatch_callback
,
1901 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1902 if (!extensions::EventRouter::Get(profile_
))
1904 scoped_ptr
<base::ListValue
> args(new base::ListValue());
1906 std::string json_args
;
1907 base::JSONWriter::Write(args
.get(), &json_args
);
1908 scoped_ptr
<extensions::Event
> event(new extensions::Event(
1909 event_name
, args
.Pass()));
1910 // The downloads system wants to share on-record events with off-record
1911 // extension renderers even in incognito_split_mode because that's how
1912 // chrome://downloads works. The "restrict_to_profile" mechanism does not
1913 // anticipate this, so it does not automatically prevent sharing off-record
1914 // events with on-record extension renderers.
1915 event
->restrict_to_browser_context
=
1916 (include_incognito
&& !profile_
->IsOffTheRecord()) ? NULL
: profile_
;
1917 event
->will_dispatch_callback
= will_dispatch_callback
;
1918 extensions::EventRouter::Get(profile_
)->BroadcastEvent(event
.Pass());
1919 DownloadsNotificationSource notification_source
;
1920 notification_source
.event_name
= event_name
;
1921 notification_source
.profile
= profile_
;
1922 content::Source
<DownloadsNotificationSource
> content_source(
1923 ¬ification_source
);
1924 content::NotificationService::current()->Notify(
1925 chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT
,
1927 content::Details
<std::string
>(&json_args
));
1930 void ExtensionDownloadsEventRouter::OnExtensionUnloaded(
1931 content::BrowserContext
* browser_context
,
1932 const extensions::Extension
* extension
,
1933 extensions::UnloadedExtensionInfo::Reason reason
) {
1934 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1935 std::set
<const extensions::Extension
*>::iterator iter
=
1936 shelf_disabling_extensions_
.find(extension
);
1937 if (iter
!= shelf_disabling_extensions_
.end())
1938 shelf_disabling_extensions_
.erase(iter
);
1941 } // namespace extensions