// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "weblayer/browser/file_select_helper.h" #include #include "build/build_config.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/selected_file_info.h" #if defined(OS_ANDROID) #include "ui/android/view_android.h" #else #include "ui/aura/window.h" #endif namespace weblayer { using blink::mojom::FileChooserFileInfo; using blink::mojom::FileChooserFileInfoPtr; using blink::mojom::FileChooserParams; using blink::mojom::FileChooserParamsPtr; // static void FileSelectHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, std::unique_ptr listener, const FileChooserParams& params) { // TODO: Should we handle text/json+contacts accept type? // FileSelectHelper will keep itself alive until it sends the result // message. scoped_refptr file_select_helper(new FileSelectHelper()); file_select_helper->RunFileChooser(render_frame_host, std::move(listener), params.Clone()); } FileSelectHelper::FileSelectHelper() = default; FileSelectHelper::~FileSelectHelper() { // There may be pending file dialogs, we need to tell them that we've gone // away so they don't try and call back to us. if (select_file_dialog_) select_file_dialog_->ListenerDestroyed(); } void FileSelectHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, std::unique_ptr listener, FileChooserParamsPtr params) { DCHECK(!web_contents()); DCHECK(listener); DCHECK(!listener_); listener_ = std::move(listener); Observe(content::WebContents::FromRenderFrameHost(render_frame_host)); select_file_dialog_ = ui::SelectFileDialog::Create(this, nullptr); dialog_mode_ = params->mode; switch (params->mode) { case FileChooserParams::Mode::kOpen: dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE; break; case FileChooserParams::Mode::kOpenMultiple: dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE; break; case FileChooserParams::Mode::kUploadFolder: // For now we don't support inputs with webkitdirectory in weblayer. dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE; break; case FileChooserParams::Mode::kSave: dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE; break; default: // Prevent warning. dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE; NOTREACHED(); } gfx::NativeWindow owning_window; #if defined(OS_ANDROID) owning_window = web_contents()->GetNativeView()->GetWindowAndroid(); #else owning_window = web_contents()->GetNativeView()->GetToplevelWindow(); #endif #if defined(OS_ANDROID) // Android needs the original MIME types and an additional capture value. std::pair, bool> accept_types = std::make_pair(params->accept_types, params->use_media_capture); #endif // Many of these params are not used in the Android SelectFileDialog // implementation, so we can safely pass empty values. select_file_dialog_->SelectFile(dialog_type_, base::string16(), base::FilePath(), nullptr, 0, base::FilePath::StringType(), owning_window, #if defined(OS_ANDROID) &accept_types); #else nullptr); #endif // Because this class returns notifications to the RenderViewHost, it is // difficult for callers to know how long to keep a reference to this // instance. We AddRef() here to keep the instance alive after we return // to the caller, until the last callback is received from the file dialog. // At that point, we must call RunFileChooserEnd(). AddRef(); } void FileSelectHelper::RunFileChooserEnd() { if (listener_) listener_->FileSelectionCanceled(); Release(); } void FileSelectHelper::FileSelected(const base::FilePath& path, int index, void* params) { FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params); } void FileSelectHelper::FileSelectedWithExtraInfo( const ui::SelectedFileInfo& file, int index, void* params) { ConvertToFileChooserFileInfoList({file}); } void FileSelectHelper::MultiFilesSelected( const std::vector& files, void* params) { std::vector selected_files = ui::FilePathListToSelectedFileInfoList(files); MultiFilesSelectedWithExtraInfo(selected_files, params); } void FileSelectHelper::MultiFilesSelectedWithExtraInfo( const std::vector& files, void* params) { ConvertToFileChooserFileInfoList(files); } void FileSelectHelper::FileSelectionCanceled(void* params) { RunFileChooserEnd(); } void FileSelectHelper::ConvertToFileChooserFileInfoList( const std::vector& files) { if (AbortIfWebContentsDestroyed()) return; std::vector chooser_files; for (const auto& file : files) { chooser_files.push_back( FileChooserFileInfo::NewNativeFile(blink::mojom::NativeFileInfo::New( file.local_path, base::FilePath(file.display_name).AsUTF16Unsafe()))); } listener_->FileSelected(std::move(chooser_files), base::FilePath(), dialog_mode_); listener_ = nullptr; // No members should be accessed from here on. RunFileChooserEnd(); } bool FileSelectHelper::AbortIfWebContentsDestroyed() { if (web_contents() == nullptr) { RunFileChooserEnd(); return true; } return false; } } // namespace weblayer