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