mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	- The |category| value for all TRACE calls from CEF client applications is now "cef.client" due to https://crrev.com/331266377d. - The |with_menu_marker| parameter to CreateMenuButton has been removed due to https://crrev.com/7f7e382118.
		
			
				
	
	
		
			389 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			389 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
 | 
						|
// Portions copyright (c) 2012 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 "libcef/browser/file_dialog_manager.h"
 | 
						|
 | 
						|
#include <utility>
 | 
						|
 | 
						|
#include "include/cef_dialog_handler.h"
 | 
						|
#include "libcef/browser/browser_host_impl.h"
 | 
						|
#include "libcef/browser/thread_util.h"
 | 
						|
 | 
						|
#include "content/public/browser/file_select_listener.h"
 | 
						|
#include "content/public/browser/render_frame_host.h"
 | 
						|
#include "content/public/common/file_chooser_file_info.h"
 | 
						|
#include "net/base/directory_lister.h"
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class CefFileDialogCallbackImpl : public CefFileDialogCallback {
 | 
						|
 public:
 | 
						|
  explicit CefFileDialogCallbackImpl(
 | 
						|
      const CefFileDialogRunner::RunFileChooserCallback& callback)
 | 
						|
      : callback_(callback) {}
 | 
						|
  ~CefFileDialogCallbackImpl() override {
 | 
						|
    if (!callback_.is_null()) {
 | 
						|
      // The callback is still pending. Cancel it now.
 | 
						|
      if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
        CancelNow(callback_);
 | 
						|
      } else {
 | 
						|
        CEF_POST_TASK(CEF_UIT, base::Bind(&CefFileDialogCallbackImpl::CancelNow,
 | 
						|
                                          callback_));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void Continue(int selected_accept_filter,
 | 
						|
                const std::vector<CefString>& file_paths) override {
 | 
						|
    if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
      if (!callback_.is_null()) {
 | 
						|
        std::vector<base::FilePath> vec;
 | 
						|
        if (!file_paths.empty()) {
 | 
						|
          std::vector<CefString>::const_iterator it = file_paths.begin();
 | 
						|
          for (; it != file_paths.end(); ++it)
 | 
						|
            vec.push_back(base::FilePath(*it));
 | 
						|
        }
 | 
						|
        callback_.Run(selected_accept_filter, vec);
 | 
						|
        callback_.Reset();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      CEF_POST_TASK(CEF_UIT,
 | 
						|
                    base::Bind(&CefFileDialogCallbackImpl::Continue, this,
 | 
						|
                               selected_accept_filter, file_paths));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void Cancel() override {
 | 
						|
    if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
      if (!callback_.is_null()) {
 | 
						|
        CancelNow(callback_);
 | 
						|
        callback_.Reset();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      CEF_POST_TASK(CEF_UIT,
 | 
						|
                    base::Bind(&CefFileDialogCallbackImpl::Cancel, this));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsConnected() { return !callback_.is_null(); }
 | 
						|
 | 
						|
  void Disconnect() { callback_.Reset(); }
 | 
						|
 | 
						|
 private:
 | 
						|
  static void CancelNow(
 | 
						|
      const CefFileDialogRunner::RunFileChooserCallback& callback) {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    std::vector<base::FilePath> file_paths;
 | 
						|
    callback.Run(0, file_paths);
 | 
						|
  }
 | 
						|
 | 
						|
  CefFileDialogRunner::RunFileChooserCallback callback_;
 | 
						|
 | 
						|
  IMPLEMENT_REFCOUNTING(CefFileDialogCallbackImpl);
 | 
						|
};
 | 
						|
 | 
						|
void RunFileDialogDismissed(CefRefPtr<CefRunFileDialogCallback> callback,
 | 
						|
                            int selected_accept_filter,
 | 
						|
                            const std::vector<base::FilePath>& file_paths) {
 | 
						|
  std::vector<CefString> paths;
 | 
						|
  if (file_paths.size() > 0) {
 | 
						|
    for (size_t i = 0; i < file_paths.size(); ++i)
 | 
						|
      paths.push_back(file_paths[i].value());
 | 
						|
  }
 | 
						|
  callback->OnFileDialogDismissed(selected_accept_filter, paths);
 | 
						|
}
 | 
						|
 | 
						|
class UploadFolderHelper
 | 
						|
    : public net::DirectoryLister::DirectoryListerDelegate {
 | 
						|
 public:
 | 
						|
  explicit UploadFolderHelper(
 | 
						|
      const CefFileDialogRunner::RunFileChooserCallback& callback)
 | 
						|
      : callback_(callback) {}
 | 
						|
 | 
						|
  ~UploadFolderHelper() override {
 | 
						|
    if (!callback_.is_null()) {
 | 
						|
      if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
        CancelNow(callback_);
 | 
						|
      } else {
 | 
						|
        CEF_POST_TASK(CEF_UIT,
 | 
						|
                      base::Bind(&UploadFolderHelper::CancelNow, callback_));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void OnListFile(
 | 
						|
      const net::DirectoryLister::DirectoryListerData& data) override {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    if (!data.info.IsDirectory())
 | 
						|
      select_files_.push_back(data.path);
 | 
						|
  }
 | 
						|
 | 
						|
  void OnListDone(int error) override {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    if (!callback_.is_null()) {
 | 
						|
      callback_.Run(0, select_files_);
 | 
						|
      callback_.Reset();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  static void CancelNow(
 | 
						|
      const CefFileDialogRunner::RunFileChooserCallback& callback) {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    std::vector<base::FilePath> file_paths;
 | 
						|
    callback.Run(0, file_paths);
 | 
						|
  }
 | 
						|
 | 
						|
  CefFileDialogRunner::RunFileChooserCallback callback_;
 | 
						|
  std::vector<base::FilePath> select_files_;
 | 
						|
 | 
						|
  DISALLOW_COPY_AND_ASSIGN(UploadFolderHelper);
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
CefFileDialogManager::CefFileDialogManager(
 | 
						|
    CefBrowserHostImpl* browser,
 | 
						|
    std::unique_ptr<CefFileDialogRunner> runner)
 | 
						|
    : browser_(browser),
 | 
						|
      runner_(std::move(runner)),
 | 
						|
      file_chooser_pending_(false),
 | 
						|
      weak_ptr_factory_(this) {}
 | 
						|
 | 
						|
CefFileDialogManager::~CefFileDialogManager() {}
 | 
						|
 | 
						|
void CefFileDialogManager::Destroy() {
 | 
						|
  DCHECK(!file_chooser_pending_);
 | 
						|
  runner_.reset(NULL);
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::RunFileDialog(
 | 
						|
    cef_file_dialog_mode_t mode,
 | 
						|
    const CefString& title,
 | 
						|
    const CefString& default_file_path,
 | 
						|
    const std::vector<CefString>& accept_filters,
 | 
						|
    int selected_accept_filter,
 | 
						|
    CefRefPtr<CefRunFileDialogCallback> callback) {
 | 
						|
  DCHECK(callback.get());
 | 
						|
  if (!callback.get())
 | 
						|
    return;
 | 
						|
 | 
						|
  CefFileDialogRunner::FileChooserParams params;
 | 
						|
  switch (mode & FILE_DIALOG_TYPE_MASK) {
 | 
						|
    case FILE_DIALOG_OPEN:
 | 
						|
      params.mode = blink::mojom::FileChooserParams::Mode::kOpen;
 | 
						|
      break;
 | 
						|
    case FILE_DIALOG_OPEN_MULTIPLE:
 | 
						|
      params.mode = blink::mojom::FileChooserParams::Mode::kOpenMultiple;
 | 
						|
      break;
 | 
						|
    case FILE_DIALOG_OPEN_FOLDER:
 | 
						|
      params.mode = blink::mojom::FileChooserParams::Mode::kUploadFolder;
 | 
						|
      break;
 | 
						|
    case FILE_DIALOG_SAVE:
 | 
						|
      params.mode = blink::mojom::FileChooserParams::Mode::kSave;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  DCHECK_GE(selected_accept_filter, 0);
 | 
						|
  params.selected_accept_filter = selected_accept_filter;
 | 
						|
 | 
						|
  params.overwriteprompt = !!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG);
 | 
						|
  params.hidereadonly = !!(mode & FILE_DIALOG_HIDEREADONLY_FLAG);
 | 
						|
 | 
						|
  params.title = title;
 | 
						|
  if (!default_file_path.empty())
 | 
						|
    params.default_file_name = base::FilePath(default_file_path);
 | 
						|
 | 
						|
  if (!accept_filters.empty()) {
 | 
						|
    std::vector<CefString>::const_iterator it = accept_filters.begin();
 | 
						|
    for (; it != accept_filters.end(); ++it)
 | 
						|
      params.accept_types.push_back(*it);
 | 
						|
  }
 | 
						|
 | 
						|
  RunFileChooser(params, base::Bind(RunFileDialogDismissed, callback));
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::RunFileChooser(
 | 
						|
    std::unique_ptr<content::FileSelectListener> listener,
 | 
						|
    const blink::mojom::FileChooserParams& params) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  CefFileDialogRunner::FileChooserParams cef_params;
 | 
						|
  static_cast<blink::mojom::FileChooserParams&>(cef_params) = params;
 | 
						|
 | 
						|
  CefFileDialogRunner::RunFileChooserCallback callback;
 | 
						|
  if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
 | 
						|
    callback = base::Bind(
 | 
						|
        &CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback,
 | 
						|
        weak_ptr_factory_.GetWeakPtr(), params.mode,
 | 
						|
        base::Passed(std::move(listener)));
 | 
						|
  } else {
 | 
						|
    callback =
 | 
						|
        base::Bind(&CefFileDialogManager::OnRunFileChooserDelegateCallback,
 | 
						|
                   weak_ptr_factory_.GetWeakPtr(), params.mode,
 | 
						|
                   base::Passed(std::move(listener)));
 | 
						|
  }
 | 
						|
 | 
						|
  RunFileChooserInternal(cef_params, callback);
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::RunFileChooser(
 | 
						|
    const CefFileDialogRunner::FileChooserParams& params,
 | 
						|
    const CefFileDialogRunner::RunFileChooserCallback& callback) {
 | 
						|
  const CefFileDialogRunner::RunFileChooserCallback& host_callback =
 | 
						|
      base::Bind(&CefFileDialogManager::OnRunFileChooserCallback,
 | 
						|
                 weak_ptr_factory_.GetWeakPtr(), callback);
 | 
						|
  RunFileChooserInternal(params, host_callback);
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::RunFileChooserInternal(
 | 
						|
    const CefFileDialogRunner::FileChooserParams& params,
 | 
						|
    const CefFileDialogRunner::RunFileChooserCallback& callback) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  if (file_chooser_pending_) {
 | 
						|
    // Dismiss the new dialog immediately.
 | 
						|
    callback.Run(0, std::vector<base::FilePath>());
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  file_chooser_pending_ = true;
 | 
						|
 | 
						|
  bool handled = false;
 | 
						|
 | 
						|
  if (browser_->client().get()) {
 | 
						|
    CefRefPtr<CefDialogHandler> handler =
 | 
						|
        browser_->client()->GetDialogHandler();
 | 
						|
    if (handler.get()) {
 | 
						|
      int mode = FILE_DIALOG_OPEN;
 | 
						|
      switch (params.mode) {
 | 
						|
        case blink::mojom::FileChooserParams::Mode::kOpen:
 | 
						|
          mode = FILE_DIALOG_OPEN;
 | 
						|
          break;
 | 
						|
        case blink::mojom::FileChooserParams::Mode::kOpenMultiple:
 | 
						|
          mode = FILE_DIALOG_OPEN_MULTIPLE;
 | 
						|
          break;
 | 
						|
        case blink::mojom::FileChooserParams::Mode::kUploadFolder:
 | 
						|
          mode = FILE_DIALOG_OPEN_FOLDER;
 | 
						|
          break;
 | 
						|
        case blink::mojom::FileChooserParams::Mode::kSave:
 | 
						|
          mode = FILE_DIALOG_SAVE;
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          NOTREACHED();
 | 
						|
          break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (params.overwriteprompt)
 | 
						|
        mode |= FILE_DIALOG_OVERWRITEPROMPT_FLAG;
 | 
						|
      if (params.hidereadonly)
 | 
						|
        mode |= FILE_DIALOG_HIDEREADONLY_FLAG;
 | 
						|
 | 
						|
      std::vector<base::string16>::const_iterator it;
 | 
						|
 | 
						|
      std::vector<CefString> accept_filters;
 | 
						|
      it = params.accept_types.begin();
 | 
						|
      for (; it != params.accept_types.end(); ++it)
 | 
						|
        accept_filters.push_back(*it);
 | 
						|
 | 
						|
      CefRefPtr<CefFileDialogCallbackImpl> callbackImpl(
 | 
						|
          new CefFileDialogCallbackImpl(callback));
 | 
						|
      handled = handler->OnFileDialog(
 | 
						|
          browser_, static_cast<cef_file_dialog_mode_t>(mode), params.title,
 | 
						|
          params.default_file_name.value(), accept_filters,
 | 
						|
          params.selected_accept_filter, callbackImpl.get());
 | 
						|
      if (!handled) {
 | 
						|
        if (callbackImpl->IsConnected()) {
 | 
						|
          callbackImpl->Disconnect();
 | 
						|
        } else {
 | 
						|
          // User executed the callback even though they returned false.
 | 
						|
          NOTREACHED();
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!handled) {
 | 
						|
    if (runner_.get()) {
 | 
						|
      runner_->Run(browser_, params, callback);
 | 
						|
    } else {
 | 
						|
      LOG(WARNING) << "No file dialog runner available for this platform";
 | 
						|
      callback.Run(0, std::vector<base::FilePath>());
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::OnRunFileChooserCallback(
 | 
						|
    const CefFileDialogRunner::RunFileChooserCallback& callback,
 | 
						|
    int selected_accept_filter,
 | 
						|
    const std::vector<base::FilePath>& file_paths) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  Cleanup();
 | 
						|
 | 
						|
  // Execute the callback asynchronously.
 | 
						|
  CEF_POST_TASK(CEF_UIT,
 | 
						|
                base::Bind(callback, selected_accept_filter, file_paths));
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback(
 | 
						|
    const blink::mojom::FileChooserParams::Mode mode,
 | 
						|
    std::unique_ptr<content::FileSelectListener> listener,
 | 
						|
    int selected_accept_filter,
 | 
						|
    const std::vector<base::FilePath>& file_paths) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  DCHECK_EQ(mode, blink::mojom::FileChooserParams::Mode::kUploadFolder);
 | 
						|
 | 
						|
  if (file_paths.size() == 0) {
 | 
						|
    // Client canceled the file chooser.
 | 
						|
    OnRunFileChooserDelegateCallback(mode, std::move(listener),
 | 
						|
                                     selected_accept_filter, file_paths);
 | 
						|
  } else {
 | 
						|
    lister_.reset(new net::DirectoryLister(
 | 
						|
        file_paths[0], net::DirectoryLister::NO_SORT_RECURSIVE,
 | 
						|
        new UploadFolderHelper(
 | 
						|
            base::Bind(&CefFileDialogManager::OnRunFileChooserDelegateCallback,
 | 
						|
                       weak_ptr_factory_.GetWeakPtr(), mode,
 | 
						|
                       base::Passed(std::move(listener))))));
 | 
						|
    lister_->Start();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::OnRunFileChooserDelegateCallback(
 | 
						|
    blink::mojom::FileChooserParams::Mode mode,
 | 
						|
    std::unique_ptr<content::FileSelectListener> listener,
 | 
						|
    int selected_accept_filter,
 | 
						|
    const std::vector<base::FilePath>& file_paths) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
 | 
						|
  base::FilePath base_dir;
 | 
						|
  std::vector<blink::mojom::FileChooserFileInfoPtr> selected_files;
 | 
						|
 | 
						|
  if (!file_paths.empty()) {
 | 
						|
    if (mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
 | 
						|
      base_dir = file_paths[0];
 | 
						|
    } else {
 | 
						|
      // Convert FilePath list to SelectedFileInfo list.
 | 
						|
      for (size_t i = 0; i < file_paths.size(); ++i) {
 | 
						|
        auto info = blink::mojom::FileChooserFileInfo::NewNativeFile(
 | 
						|
            blink::mojom::NativeFileInfo::New(file_paths[i], base::string16()));
 | 
						|
        selected_files.push_back(std::move(info));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  listener->FileSelected(std::move(selected_files), base_dir, mode);
 | 
						|
 | 
						|
  Cleanup();
 | 
						|
}
 | 
						|
 | 
						|
void CefFileDialogManager::Cleanup() {
 | 
						|
  if (lister_)
 | 
						|
    lister_.reset();
 | 
						|
 | 
						|
  file_chooser_pending_ = false;
 | 
						|
}
 |