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