• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       &notification_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