[M120 Migration]Fix for crash during chrome exit
[platform/framework/web/chromium-efl.git] / chrome / browser / file_select_helper.cc
1 // Copyright 2012 The Chromium Authors
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/file_select_helper.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <string>
11 #include <utility>
12
13 #include "base/files/file_util.h"
14 #include "base/functional/bind.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/task/thread_pool.h"
20 #include "base/threading/hang_watcher.h"
21 #include "build/build_config.h"
22 #include "build/chromeos_buildflags.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/enterprise/connectors/common.h"
25 #include "chrome/browser/platform_util.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/browser/ui/browser_dialogs.h"
29 #include "chrome/browser/ui/chrome_select_file_policy.h"
30 #include "chrome/grit/generated_resources.h"
31 #include "components/enterprise/buildflags/buildflags.h"
32 #include "components/enterprise/common/proto/connectors.pb.h"
33 #include "components/safe_browsing/buildflags.h"
34 #include "content/public/browser/browser_task_traits.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/file_select_listener.h"
37 #include "content/public/browser/global_routing_id.h"
38 #include "content/public/browser/render_frame_host.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/storage_partition.h"
41 #include "content/public/browser/web_contents.h"
42 #include "net/base/filename_util.h"
43 #include "net/base/mime_util.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/shell_dialogs/selected_file_info.h"
46
47 #if BUILDFLAG(IS_ANDROID)
48 #include "chrome/browser/file_select_helper_contacts_android.h"
49 #endif
50
51 #if BUILDFLAG(IS_CHROMEOS_ASH)
52 #include "chrome/browser/ash/file_manager/fileapi_util.h"
53 #include "content/public/browser/site_instance.h"
54 #endif
55
56 #if BUILDFLAG(FULL_SAFE_BROWSING)
57 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
58 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
59 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
60 #endif
61
62 using blink::mojom::FileChooserFileInfo;
63 using blink::mojom::FileChooserFileInfoPtr;
64 using blink::mojom::FileChooserParams;
65 using blink::mojom::FileChooserParamsPtr;
66 using content::BrowserThread;
67 using content::WebContents;
68
69 namespace {
70
71 #if BUILDFLAG(IS_ANDROID)
72 // The MIME type for selecting contacts.
73 constexpr char16_t kContactsMimeType[] = u"text/json+contacts";
74 #endif
75
76 void DeleteFiles(std::vector<base::FilePath> paths) {
77   for (auto& file_path : paths)
78     base::DeleteFile(file_path);
79 }
80
81 bool IsValidProfile(Profile* profile) {
82   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
83   if (!profile) {
84     return false;
85   }
86   // No profile manager in unit tests.
87   if (!g_browser_process->profile_manager())
88     return true;
89   return g_browser_process->profile_manager()->IsValidProfile(profile);
90 }
91
92 #if BUILDFLAG(FULL_SAFE_BROWSING)
93
94 bool IsDownloadAllowedBySafeBrowsing(
95     safe_browsing::DownloadCheckResult result) {
96   using Result = safe_browsing::DownloadCheckResult;
97   switch (result) {
98     // Only allow downloads that are marked as SAFE or UNKNOWN by SafeBrowsing.
99     // All other types are going to be blocked. UNKNOWN could be the result of a
100     // failed safe browsing ping.
101     case Result::UNKNOWN:
102     case Result::SAFE:
103     case Result::ALLOWLISTED_BY_POLICY:
104       return true;
105
106     case Result::DANGEROUS:
107     case Result::UNCOMMON:
108     case Result::DANGEROUS_HOST:
109     case Result::POTENTIALLY_UNWANTED:
110     case Result::DANGEROUS_ACCOUNT_COMPROMISE:
111       return false;
112
113     // Safe Browsing should only return these results for client downloads, not
114     // for PPAPI downloads.
115     case Result::ASYNC_SCANNING:
116     case Result::BLOCKED_PASSWORD_PROTECTED:
117     case Result::BLOCKED_TOO_LARGE:
118     case Result::SENSITIVE_CONTENT_BLOCK:
119     case Result::SENSITIVE_CONTENT_WARNING:
120     case Result::DEEP_SCANNED_SAFE:
121     case Result::PROMPT_FOR_SCANNING:
122     case Result::PROMPT_FOR_LOCAL_PASSWORD_SCANNING:
123     case Result::BLOCKED_UNSUPPORTED_FILE_TYPE:
124     case Result::DEEP_SCANNED_FAILED:
125       NOTREACHED();
126       return true;
127   }
128   NOTREACHED();
129   return false;
130 }
131
132 void InterpretSafeBrowsingVerdict(base::OnceCallback<void(bool)> recipient,
133                                   safe_browsing::DownloadCheckResult result) {
134   std::move(recipient).Run(IsDownloadAllowedBySafeBrowsing(result));
135 }
136
137 #endif
138
139 }  // namespace
140
141 struct FileSelectHelper::ActiveDirectoryEnumeration {
142   explicit ActiveDirectoryEnumeration(const base::FilePath& path)
143       : path_(path) {}
144
145   std::unique_ptr<net::DirectoryLister> lister_;
146   const base::FilePath path_;
147   std::vector<base::FilePath> results_;
148 };
149
150 FileSelectHelper::FileSelectHelper(Profile* profile)
151     : profile_(profile),
152       render_frame_host_(nullptr),
153       web_contents_(nullptr),
154       select_file_dialog_(),
155       select_file_types_(),
156       dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
157       dialog_mode_(FileChooserParams::Mode::kOpen) {}
158
159 FileSelectHelper::~FileSelectHelper() {
160   // There may be pending file dialogs, we need to tell them that we've gone
161   // away so they don't try and call back to us.
162   if (select_file_dialog_)
163     select_file_dialog_->ListenerDestroyed();
164 }
165
166 void FileSelectHelper::FileSelected(const base::FilePath& path,
167                                     int index,
168                                     void* params) {
169   FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
170 }
171
172 void FileSelectHelper::FileSelectedWithExtraInfo(
173     const ui::SelectedFileInfo& file,
174     int index,
175     void* params) {
176   if (IsValidProfile(profile_)) {
177     base::FilePath path = file.file_path;
178     if (dialog_mode_ != FileChooserParams::Mode::kUploadFolder)
179       path = path.DirName();
180     profile_->set_last_selected_directory(path);
181   }
182
183   if (!render_frame_host_) {
184     RunFileChooserEnd();
185     return;
186   }
187
188   const base::FilePath& path = file.local_path;
189   if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
190     StartNewEnumeration(path);
191     return;
192   }
193
194   std::vector<ui::SelectedFileInfo> files;
195   files.push_back(file);
196
197 #if BUILDFLAG(IS_MAC)
198   base::ThreadPool::PostTask(
199       FROM_HERE,
200       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
201       base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
202 #else
203   ConvertToFileChooserFileInfoList(files);
204 #endif  // BUILDFLAG(IS_MAC)
205 }
206
207 void FileSelectHelper::MultiFilesSelected(
208     const std::vector<base::FilePath>& files,
209     void* params) {
210   std::vector<ui::SelectedFileInfo> selected_files =
211       ui::FilePathListToSelectedFileInfoList(files);
212
213   MultiFilesSelectedWithExtraInfo(selected_files, params);
214 }
215
216 void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
217     const std::vector<ui::SelectedFileInfo>& files,
218     void* params) {
219   if (!files.empty() && IsValidProfile(profile_)) {
220     base::FilePath path = files[0].file_path;
221     if (dialog_mode_ != FileChooserParams::Mode::kUploadFolder)
222       path = path.DirName();
223     profile_->set_last_selected_directory(path);
224   }
225 #if BUILDFLAG(IS_MAC)
226   base::ThreadPool::PostTask(
227       FROM_HERE,
228       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
229       base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
230 #else
231   ConvertToFileChooserFileInfoList(files);
232 #endif  // BUILDFLAG(IS_MAC)
233 }
234
235 void FileSelectHelper::FileSelectionCanceled(void* params) {
236   RunFileChooserEnd();
237 }
238
239 void FileSelectHelper::StartNewEnumeration(const base::FilePath& path) {
240   base_dir_ = path;
241   auto entry = std::make_unique<ActiveDirectoryEnumeration>(path);
242   entry->lister_ = base::WrapUnique(new net::DirectoryLister(
243       path, net::DirectoryLister::NO_SORT_RECURSIVE, this));
244   entry->lister_->Start();
245   directory_enumeration_ = std::move(entry);
246 }
247
248 void FileSelectHelper::OnListFile(
249     const net::DirectoryLister::DirectoryListerData& data) {
250   // Directory upload only cares about files.
251   if (data.info.IsDirectory())
252     return;
253
254   directory_enumeration_->results_.push_back(data.path);
255 }
256
257 void FileSelectHelper::LaunchConfirmationDialog(
258     const base::FilePath& path,
259     std::vector<ui::SelectedFileInfo> selected_files) {
260   ShowFolderUploadConfirmationDialog(
261       path,
262       base::BindOnce(&FileSelectHelper::ConvertToFileChooserFileInfoList, this),
263       std::move(selected_files), web_contents_);
264 }
265
266 void FileSelectHelper::OnListDone(int error) {
267   if (!web_contents_) {
268     // Web contents was destroyed under us (probably by closing the tab). We
269     // must notify |listener_| and release our reference to
270     // ourself. RunFileChooserEnd() performs this.
271     RunFileChooserEnd();
272     return;
273   }
274
275   // This entry needs to be cleaned up when this function is done.
276   std::unique_ptr<ActiveDirectoryEnumeration> entry =
277       std::move(directory_enumeration_);
278   if (error) {
279     FileSelectionCanceled(nullptr);
280     return;
281   }
282
283   std::vector<ui::SelectedFileInfo> selected_files =
284       ui::FilePathListToSelectedFileInfoList(entry->results_);
285
286   if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
287     LaunchConfirmationDialog(entry->path_, std::move(selected_files));
288   } else {
289     std::vector<FileChooserFileInfoPtr> chooser_files;
290     for (const auto& file_path : entry->results_) {
291       chooser_files.push_back(FileChooserFileInfo::NewNativeFile(
292           blink::mojom::NativeFileInfo::New(file_path, std::u16string())));
293     }
294
295     listener_->FileSelected(std::move(chooser_files), base_dir_,
296                             FileChooserParams::Mode::kUploadFolder);
297     listener_.reset();
298     EnumerateDirectoryEnd();
299   }
300 }
301
302 void FileSelectHelper::ConvertToFileChooserFileInfoList(
303     const std::vector<ui::SelectedFileInfo>& files) {
304   if (AbortIfWebContentsDestroyed())
305     return;
306
307 #if BUILDFLAG(IS_CHROMEOS_ASH)
308   if (!files.empty()) {
309     if (!IsValidProfile(profile_)) {
310       RunFileChooserEnd();
311       return;
312     }
313     // Converts |files| into FileChooserFileInfo with handling of non-native
314     // files.
315     content::SiteInstance* site_instance =
316         render_frame_host_->GetSiteInstance();
317     storage::FileSystemContext* file_system_context =
318         profile_->GetStoragePartition(site_instance)->GetFileSystemContext();
319     file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
320         file_system_context, render_frame_host_->GetLastCommittedOrigin(),
321         files,
322         base::BindOnce(&FileSelectHelper::PerformContentAnalysisIfNeeded,
323                        this));
324     return;
325   }
326 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
327
328   std::vector<FileChooserFileInfoPtr> chooser_files;
329   for (const auto& file : files) {
330     chooser_files.push_back(
331         FileChooserFileInfo::NewNativeFile(blink::mojom::NativeFileInfo::New(
332             file.local_path,
333             base::FilePath(file.display_name).AsUTF16Unsafe())));
334   }
335
336   PerformContentAnalysisIfNeeded(std::move(chooser_files));
337 }
338
339 void FileSelectHelper::PerformContentAnalysisIfNeeded(
340     std::vector<FileChooserFileInfoPtr> list) {
341   if (AbortIfWebContentsDestroyed())
342     return;
343
344 #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
345   enterprise_connectors::ContentAnalysisDelegate::Data data;
346   if (enterprise_connectors::ContentAnalysisDelegate::IsEnabled(
347           profile_, web_contents_->GetLastCommittedURL(), &data,
348           enterprise_connectors::AnalysisConnector::FILE_ATTACHED)) {
349     data.reason =
350         enterprise_connectors::ContentAnalysisRequest::FILE_PICKER_DIALOG;
351     data.paths.reserve(list.size());
352     for (const auto& file : list) {
353       if (file && file->is_native_file())
354         data.paths.push_back(file->get_native_file()->file_path);
355     }
356
357     if (data.paths.empty()) {
358       NotifyListenerAndEnd(std::move(list));
359     } else {
360       enterprise_connectors::ContentAnalysisDelegate::CreateForWebContents(
361           web_contents_, std::move(data),
362           base::BindOnce(&FileSelectHelper::ContentAnalysisCompletionCallback,
363                          this, std::move(list)),
364           safe_browsing::DeepScanAccessPoint::UPLOAD);
365     }
366   } else {
367     NotifyListenerAndEnd(std::move(list));
368   }
369 #else
370   NotifyListenerAndEnd(std::move(list));
371 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
372 }
373
374 #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
375 void FileSelectHelper::ContentAnalysisCompletionCallback(
376     std::vector<blink::mojom::FileChooserFileInfoPtr> list,
377     const enterprise_connectors::ContentAnalysisDelegate::Data& data,
378     enterprise_connectors::ContentAnalysisDelegate::Result& result) {
379   if (AbortIfWebContentsDestroyed())
380     return;
381
382   DCHECK_EQ(data.text.size(), 0u);
383   DCHECK_EQ(result.text_results.size(), 0u);
384   DCHECK_EQ(data.paths.size(), result.paths_results.size());
385   DCHECK_GE(list.size(), result.paths_results.size());
386
387   // If the user chooses to upload a folder and the folder contains sensitive
388   // files, block the entire folder and update `result` to reflect the block
389   // verdict for all files scanned.
390   if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
391     if (base::Contains(result.paths_results, false)) {
392       list.clear();
393       for (size_t index = 0; index < data.paths.size(); ++index) {
394         result.paths_results[index] = false;
395       }
396     }
397     // Early return for folder upload, regardless of list being empty or not.
398     NotifyListenerAndEnd(std::move(list));
399     return;
400   }
401
402   // For single or multiple file uploads, remove any files that did not pass the
403   // deep scan. Non-native files are skipped.
404   size_t i = 0;
405   for (auto it = list.begin(); it != list.end();) {
406     if ((*it)->is_native_file()) {
407       if (!result.paths_results[i]) {
408         it = list.erase(it);
409       } else {
410         ++it;
411       }
412       ++i;
413     } else {
414       // Skip non-native files by incrementing the iterator without changing `i`
415       // so that no result is skipped.
416       ++it;
417     }
418   }
419
420   NotifyListenerAndEnd(std::move(list));
421 }
422 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
423
424 void FileSelectHelper::NotifyListenerAndEnd(
425     std::vector<blink::mojom::FileChooserFileInfoPtr> list) {
426   listener_->FileSelected(std::move(list), base_dir_, dialog_mode_);
427   listener_.reset();
428
429   // No members should be accessed from here on.
430   RunFileChooserEnd();
431 }
432
433 void FileSelectHelper::DeleteTemporaryFiles() {
434   base::ThreadPool::PostTask(
435       FROM_HERE,
436       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
437        base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
438       base::BindOnce(&DeleteFiles, std::move(temporary_files_)));
439 }
440
441 void FileSelectHelper::CleanUp() {
442   if (!temporary_files_.empty()) {
443     DeleteTemporaryFiles();
444
445     // Now that the temporary files have been scheduled for deletion, there
446     // is no longer any reason to keep this instance around.
447     Release();
448   }
449 }
450
451 bool FileSelectHelper::AbortIfWebContentsDestroyed() {
452   if (abort_on_missing_web_contents_in_tests_ &&
453       (render_frame_host_ == nullptr || web_contents_ == nullptr)) {
454     RunFileChooserEnd();
455     return true;
456   }
457
458   return false;
459 }
460
461 void FileSelectHelper::SetFileSelectListenerForTesting(
462     scoped_refptr<content::FileSelectListener> listener) {
463   DCHECK(listener);
464   DCHECK(!listener_);
465   listener_ = std::move(listener);
466 }
467
468 void FileSelectHelper::DontAbortOnMissingWebContentsForTesting() {
469   abort_on_missing_web_contents_in_tests_ = false;
470 }
471
472 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
473 FileSelectHelper::GetFileTypesFromAcceptType(
474     const std::vector<std::u16string>& accept_types) {
475   auto base_file_type = std::make_unique<ui::SelectFileDialog::FileTypeInfo>();
476   if (accept_types.empty())
477     return base_file_type;
478
479   // Create FileTypeInfo and pre-allocate for the first extension list.
480   auto file_type =
481       std::make_unique<ui::SelectFileDialog::FileTypeInfo>(*base_file_type);
482   file_type->include_all_files = true;
483   file_type->extensions.resize(1);
484   std::vector<base::FilePath::StringType>* extensions =
485       &file_type->extensions.back();
486
487   // Find the corresponding extensions.
488   int valid_type_count = 0;
489   int description_id = 0;
490   for (const auto& accept_type : accept_types) {
491     size_t old_extension_size = extensions->size();
492     if (accept_type[0] == '.') {
493       // If the type starts with a period it is assumed to be a file extension
494       // so we just have to add it to the list.
495       base::FilePath::StringType ext =
496           base::FilePath::FromUTF16Unsafe(accept_type).value();
497       extensions->push_back(ext.substr(1));
498     } else {
499       if (!base::IsStringASCII(accept_type))
500         continue;
501       std::string ascii_type = base::UTF16ToASCII(accept_type);
502       if (ascii_type == "image/*")
503         description_id = IDS_IMAGE_FILES;
504       else if (ascii_type == "audio/*")
505         description_id = IDS_AUDIO_FILES;
506       else if (ascii_type == "video/*")
507         description_id = IDS_VIDEO_FILES;
508
509       net::GetExtensionsForMimeType(ascii_type, extensions);
510     }
511
512     if (extensions->size() > old_extension_size)
513       valid_type_count++;
514   }
515
516   // If no valid extension is added, bail out.
517   if (valid_type_count == 0)
518     return base_file_type;
519
520   // Use a generic description "Custom Files" if either of the following is
521   // true:
522   // 1) There're multiple types specified, like "audio/*,video/*"
523   // 2) There're multiple extensions for a MIME type without parameter, like
524   //    "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
525   //    dialog uses the first extension in the list to form the description,
526   //    like "EHTML Files". This is not what we want.
527   if (valid_type_count > 1 ||
528       (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
529     description_id = IDS_CUSTOM_FILES;
530
531   if (description_id) {
532     file_type->extension_description_overrides.push_back(
533         l10n_util::GetStringUTF16(description_id));
534   }
535
536   return file_type;
537 }
538
539 // static
540 void FileSelectHelper::RunFileChooser(
541     content::RenderFrameHost* render_frame_host,
542     scoped_refptr<content::FileSelectListener> listener,
543     const FileChooserParams& params) {
544   Profile* profile = Profile::FromBrowserContext(
545       render_frame_host->GetProcess()->GetBrowserContext());
546
547 #if BUILDFLAG(IS_ANDROID)
548   if (params.accept_types.size() == 1 &&
549       params.accept_types[0] == kContactsMimeType) {
550     scoped_refptr<FileSelectHelperContactsAndroid> file_select_helper_android(
551         new FileSelectHelperContactsAndroid(profile));
552     file_select_helper_android->RunFileChooser(
553         render_frame_host, std::move(listener), params.Clone());
554     return;
555   }
556 #endif
557
558   // FileSelectHelper will keep itself alive until it sends the result
559   // message.
560   scoped_refptr<FileSelectHelper> file_select_helper(
561       new FileSelectHelper(profile));
562   file_select_helper->RunFileChooser(render_frame_host, std::move(listener),
563                                      params.Clone());
564 }
565
566 // static
567 void FileSelectHelper::EnumerateDirectory(
568     content::WebContents* tab,
569     scoped_refptr<content::FileSelectListener> listener,
570     const base::FilePath& path) {
571   Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
572   // FileSelectHelper will keep itself alive until it sends the result
573   // message.
574   scoped_refptr<FileSelectHelper> file_select_helper(
575       new FileSelectHelper(profile));
576   file_select_helper->EnumerateDirectoryImpl(tab, std::move(listener), path);
577 }
578
579 void FileSelectHelper::RunFileChooser(
580     content::RenderFrameHost* render_frame_host,
581     scoped_refptr<content::FileSelectListener> listener,
582     FileChooserParamsPtr params) {
583   DCHECK(!render_frame_host_);
584   DCHECK(!web_contents_);
585   DCHECK(listener);
586   DCHECK(!listener_);
587   DCHECK(params->default_file_name.empty() ||
588          params->mode == FileChooserParams::Mode::kSave)
589       << "The default_file_name parameter should only be specified for Save "
590          "file choosers";
591   DCHECK(params->default_file_name == params->default_file_name.BaseName())
592       << "The default_file_name parameter should not contain path separators";
593
594   render_frame_host_ = render_frame_host;
595   web_contents_ = WebContents::FromRenderFrameHost(render_frame_host);
596   listener_ = std::move(listener);
597   content::WebContentsObserver::Observe(web_contents_);
598
599   base::ThreadPool::PostTask(
600       FROM_HERE, {base::MayBlock()},
601       base::BindOnce(&FileSelectHelper::GetFileTypesInThreadPool, this,
602                      std::move(params)));
603
604   // Because this class returns notifications to the RenderViewHost, it is
605   // difficult for callers to know how long to keep a reference to this
606   // instance. We AddRef() here to keep the instance alive after we return
607   // to the caller, until the last callback is received from the file dialog.
608   // At that point, we must call RunFileChooserEnd().
609   AddRef();
610 }
611
612 void FileSelectHelper::GetFileTypesInThreadPool(FileChooserParamsPtr params) {
613   select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
614   select_file_types_->allowed_paths =
615       params->need_local_path ? ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH
616                               : ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
617
618   content::GetUIThreadTaskRunner({})->PostTask(
619       FROM_HERE,
620       base::BindOnce(&FileSelectHelper::GetSanitizedFilenameOnUIThread, this,
621                      std::move(params)));
622 }
623
624 void FileSelectHelper::GetSanitizedFilenameOnUIThread(
625     FileChooserParamsPtr params) {
626   if (AbortIfWebContentsDestroyed())
627     return;
628
629   base::FilePath default_file_path = profile_->last_selected_directory().Append(
630       GetSanitizedFileName(params->default_file_name));
631 #if BUILDFLAG(FULL_SAFE_BROWSING)
632   if (params->mode == FileChooserParams::Mode::kSave) {
633     CheckDownloadRequestWithSafeBrowsing(default_file_path, std::move(params));
634     return;
635   }
636 #endif
637   RunFileChooserOnUIThread(default_file_path, std::move(params));
638 }
639
640 #if BUILDFLAG(FULL_SAFE_BROWSING)
641 void FileSelectHelper::CheckDownloadRequestWithSafeBrowsing(
642     const base::FilePath& default_file_path,
643     FileChooserParamsPtr params) {
644   // Download Protection is not supported on Android.
645   safe_browsing::SafeBrowsingService* sb_service =
646       g_browser_process->safe_browsing_service();
647   bool real_time_download_protection_request_allowed =
648       safe_browsing::IsRealTimeDownloadProtectionRequestAllowed(
649           *profile_->GetPrefs());
650
651   if (!sb_service || !sb_service->download_protection_service() ||
652       !sb_service->download_protection_service()->enabled() ||
653       !real_time_download_protection_request_allowed) {
654     RunFileChooserOnUIThread(default_file_path, std::move(params));
655     return;
656   }
657
658   std::vector<base::FilePath::StringType> alternate_extensions;
659   if (select_file_types_) {
660     for (const auto& extensions_list : select_file_types_->extensions) {
661       for (const auto& extension_in_list : extensions_list) {
662         base::FilePath::StringType extension =
663             default_file_path.ReplaceExtension(extension_in_list)
664                 .FinalExtension();
665         alternate_extensions.push_back(extension);
666       }
667     }
668   }
669
670   GURL requestor_url = params->requestor;
671   sb_service->download_protection_service()->CheckPPAPIDownloadRequest(
672       requestor_url, render_frame_host_, default_file_path,
673       alternate_extensions, profile_,
674       base::BindOnce(
675           &InterpretSafeBrowsingVerdict,
676           base::BindOnce(&FileSelectHelper::ProceedWithSafeBrowsingVerdict,
677                          this, default_file_path, std::move(params))));
678 }
679
680 void FileSelectHelper::ProceedWithSafeBrowsingVerdict(
681     const base::FilePath& default_file_path,
682     FileChooserParamsPtr params,
683     bool allowed_by_safe_browsing) {
684   if (!allowed_by_safe_browsing) {
685     RunFileChooserEnd();
686     return;
687   }
688   RunFileChooserOnUIThread(default_file_path, std::move(params));
689 }
690 #endif
691
692 void FileSelectHelper::RunFileChooserOnUIThread(
693     const base::FilePath& default_file_path,
694     FileChooserParamsPtr params) {
695   DCHECK(params);
696   DCHECK(!select_file_dialog_);
697   if (AbortIfWebContentsDestroyed())
698     return;
699
700   select_file_dialog_ = ui::SelectFileDialog::Create(
701       this, std::make_unique<ChromeSelectFilePolicy>(web_contents_));
702   if (!select_file_dialog_)
703     return;
704
705   dialog_mode_ = params->mode;
706   switch (params->mode) {
707     case FileChooserParams::Mode::kOpen:
708       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
709       break;
710     case FileChooserParams::Mode::kOpenMultiple:
711       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
712       break;
713     case FileChooserParams::Mode::kUploadFolder:
714       dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
715       break;
716     case FileChooserParams::Mode::kSave:
717       dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
718       break;
719     default:
720       // Prevent warning.
721       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
722       NOTREACHED();
723   }
724
725   gfx::NativeWindow owning_window =
726       platform_util::GetTopLevel(web_contents_->GetNativeView());
727
728 #if BUILDFLAG(IS_ANDROID)
729   // Android needs the original MIME types and an additional capture value.
730   std::pair<std::vector<std::u16string>, bool> accept_types =
731       std::make_pair(params->accept_types, params->use_media_capture);
732   void* accept_types_ptr = &accept_types;
733 #else
734   void* accept_types_ptr = nullptr;
735 #endif
736
737   // Never consider the current scope as hung. The hang watching deadline (if
738   // any) is not valid since the user can take unbounded time to choose the
739   // file.
740   base::HangWatcher::InvalidateActiveExpectations();
741
742   // 1-based index of default extension to show.
743   int file_type_index =
744       select_file_types_ && !select_file_types_->extensions.empty() ? 1 : 0;
745
746   const GURL* caller =
747       &render_frame_host_->GetMainFrame()->GetLastCommittedURL();
748
749   select_file_dialog_->SelectFile(dialog_type_, params->title,
750                                   default_file_path, select_file_types_.get(),
751                                   file_type_index, base::FilePath::StringType(),
752                                   owning_window, accept_types_ptr, caller);
753
754   select_file_types_.reset();
755 }
756
757 // This method is called when we receive the last callback from the file chooser
758 // dialog or if the renderer was destroyed. Perform any cleanup and release the
759 // reference we added in RunFileChooser().
760 void FileSelectHelper::RunFileChooserEnd() {
761   // If there are temporary files, then this instance needs to stick around
762   // until web_contents_ is destroyed, so that this instance can delete the
763   // temporary files.
764   if (!temporary_files_.empty())
765     return;
766
767   if (listener_)
768     listener_->FileSelectionCanceled();
769   render_frame_host_ = nullptr;
770   web_contents_ = nullptr;
771   // If the dialog was actually opened, dispose of our reference.
772   if (select_file_dialog_) {
773     select_file_dialog_->ListenerDestroyed();
774     select_file_dialog_.reset();
775   }
776   Release();
777 }
778
779 void FileSelectHelper::EnumerateDirectoryImpl(
780     content::WebContents* tab,
781     scoped_refptr<content::FileSelectListener> listener,
782     const base::FilePath& path) {
783   DCHECK(listener);
784   DCHECK(!listener_);
785   dialog_type_ = ui::SelectFileDialog::SELECT_NONE;
786   web_contents_ = tab;
787   listener_ = std::move(listener);
788   // Because this class returns notifications to the RenderViewHost, it is
789   // difficult for callers to know how long to keep a reference to this
790   // instance. We AddRef() here to keep the instance alive after we return
791   // to the caller, until the last callback is received from the enumeration
792   // code. At that point, we must call EnumerateDirectoryEnd().
793   AddRef();
794   StartNewEnumeration(path);
795 }
796
797 // This method is called when we receive the last callback from the enumeration
798 // code. Perform any cleanup and release the reference we added in
799 // EnumerateDirectoryImpl().
800 void FileSelectHelper::EnumerateDirectoryEnd() {
801   Release();
802 }
803
804 void FileSelectHelper::RenderFrameHostChanged(
805     content::RenderFrameHost* old_host,
806     content::RenderFrameHost* new_host) {
807   // The |old_host| and its children are now pending deletion. Do not give them
808   // file access past this point.
809   for (content::RenderFrameHost* host = render_frame_host_; host;
810        host = host->GetParentOrOuterDocument()) {
811     if (host == old_host) {
812       render_frame_host_ = nullptr;
813       return;
814     }
815   }
816 }
817
818 void FileSelectHelper::RenderFrameDeleted(
819     content::RenderFrameHost* render_frame_host) {
820   if (render_frame_host == render_frame_host_)
821     render_frame_host_ = nullptr;
822 }
823
824 void FileSelectHelper::WebContentsDestroyed() {
825   render_frame_host_ = nullptr;
826   web_contents_ = nullptr;
827   profile_ = nullptr;
828   CleanUp();
829 }
830
831 // static
832 bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
833   // TODO(raymes): This only does some basic checks, extend to test more cases.
834   // A 1 character accept type will always be invalid (either a "." in the case
835   // of an extension or a "/" in the case of a MIME type).
836   std::string unused;
837   if (accept_type.length() <= 1 ||
838       base::ToLowerASCII(accept_type) != accept_type ||
839       base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
840           base::TRIM_NONE) {
841     return false;
842   }
843   return true;
844 }
845
846 // static
847 base::FilePath FileSelectHelper::GetSanitizedFileName(
848     const base::FilePath& suggested_filename) {
849   if (suggested_filename.empty())
850     return base::FilePath();
851   return net::GenerateFileName(
852       GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
853       std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
854 }