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