-
Notifications
You must be signed in to change notification settings - Fork 15k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: transparently package bundles as zip archives (#25030)
- Loading branch information
1 parent
7cdc42f
commit 284c1b9
Showing
6 changed files
with
547 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
// Copyright (c) 2020 Microsoft, Inc. All rights reserved. | ||
// Use of this source code is governed by the MIT license that can be | ||
// found in the LICENSE file. | ||
|
||
#include <memory> | ||
|
||
#include <string> | ||
#include <utility> | ||
#include <vector> | ||
|
||
#include "shell/browser/file_select_helper.h" | ||
|
||
#include "base/bind.h" | ||
#include "base/files/file_enumerator.h" | ||
#include "base/files/file_util.h" | ||
#include "base/strings/utf_string_conversions.h" | ||
#include "chrome/common/pref_names.h" | ||
#include "components/prefs/pref_service.h" | ||
#include "shell/browser/electron_browser_context.h" | ||
#include "shell/browser/javascript_environment.h" | ||
#include "shell/browser/ui/file_dialog.h" | ||
#include "shell/common/gin_converters/callback_converter.h" | ||
#include "shell/common/gin_converters/file_path_converter.h" | ||
|
||
using blink::mojom::FileChooserFileInfo; | ||
using blink::mojom::FileChooserFileInfoPtr; | ||
using blink::mojom::FileChooserParams; | ||
using blink::mojom::NativeFileInfo; | ||
|
||
namespace { | ||
void DeleteFiles(std::vector<base::FilePath> paths) { | ||
for (auto& file_path : paths) | ||
base::DeleteFile(file_path); | ||
} | ||
} // namespace | ||
|
||
FileSelectHelper::FileSelectHelper( | ||
content::RenderFrameHost* render_frame_host, | ||
scoped_refptr<content::FileSelectListener> listener, | ||
FileChooserParams::Mode mode) | ||
: render_frame_host_(render_frame_host), | ||
listener_(std::move(listener)), | ||
mode_(mode) { | ||
DCHECK(render_frame_host_); | ||
DCHECK(listener_); | ||
|
||
web_contents_ = content::WebContents::FromRenderFrameHost(render_frame_host); | ||
DCHECK(web_contents_); | ||
|
||
content::WebContentsObserver::Observe(web_contents_); | ||
observer_.Add(render_frame_host_->GetRenderViewHost()->GetWidget()); | ||
} | ||
|
||
FileSelectHelper::~FileSelectHelper() = default; | ||
|
||
void FileSelectHelper::ShowOpenDialog( | ||
const file_dialog::DialogSettings& settings) { | ||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); | ||
v8::HandleScope scope(isolate); | ||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate); | ||
|
||
auto callback = base::BindOnce(&FileSelectHelper::OnOpenDialogDone, | ||
weak_ptr_factory_.GetWeakPtr()); | ||
ignore_result(promise.Then(std::move(callback))); | ||
|
||
file_dialog::ShowOpenDialog(settings, std::move(promise)); | ||
} | ||
|
||
void FileSelectHelper::ShowSaveDialog( | ||
const file_dialog::DialogSettings& settings) { | ||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate(); | ||
v8::HandleScope scope(isolate); | ||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate); | ||
|
||
auto callback = base::BindOnce(&FileSelectHelper::OnSaveDialogDone, | ||
weak_ptr_factory_.GetWeakPtr()); | ||
ignore_result(promise.Then(std::move(callback))); | ||
|
||
file_dialog::ShowSaveDialog(settings, std::move(promise)); | ||
} | ||
|
||
// net::DirectoryLister::DirectoryListerDelegate | ||
void FileSelectHelper::OnListFile( | ||
const net::DirectoryLister::DirectoryListerData& data) { | ||
if (!render_frame_host_ || !web_contents_) { | ||
// If the frame or webcontents was destroyed under us. We | ||
// must notify |listener_| and release our reference to | ||
// ourself. RunFileChooserEnd() performs this. | ||
RunFileChooserEnd(); | ||
return; | ||
} | ||
// We don't want to return directory paths, only file paths | ||
if (data.info.IsDirectory()) | ||
return; | ||
|
||
lister_paths_.push_back(data.path); | ||
} | ||
|
||
void FileSelectHelper::RunFileChooserEnd() { | ||
// If there are temporary files, then this instance needs to stick around | ||
// until web_contents_ is destroyed, so that this instance can delete the | ||
// temporary files. | ||
if (!temporary_files_.empty()) | ||
return; | ||
|
||
if (listener_) | ||
listener_->FileSelectionCanceled(); | ||
|
||
render_frame_host_ = nullptr; | ||
web_contents_ = nullptr; | ||
|
||
delete this; | ||
} | ||
|
||
// net::DirectoryLister::DirectoryListerDelegate | ||
void FileSelectHelper::OnListDone(int error) { | ||
if (!render_frame_host_ || !web_contents_) { | ||
// If the frame or webcontents was destroyed under us. We | ||
// must notify |listener_| and release our reference to | ||
// ourself. RunFileChooserEnd() performs this. | ||
RunFileChooserEnd(); | ||
return; | ||
} | ||
|
||
std::vector<FileChooserFileInfoPtr> file_info; | ||
for (const auto& path : lister_paths_) | ||
file_info.push_back(FileChooserFileInfo::NewNativeFile( | ||
NativeFileInfo::New(path, base::string16()))); | ||
|
||
OnFilesSelected(std::move(file_info), lister_base_dir_); | ||
} | ||
|
||
void FileSelectHelper::DeleteTemporaryFiles() { | ||
base::ThreadPool::PostTask( | ||
FROM_HERE, | ||
{base::MayBlock(), base::TaskPriority::BEST_EFFORT, | ||
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}, | ||
base::BindOnce(&DeleteFiles, std::move(temporary_files_))); | ||
} | ||
|
||
void FileSelectHelper::EnumerateDirectory() { | ||
// Ensure that this fn is only called once | ||
DCHECK(!lister_); | ||
DCHECK(!lister_base_dir_.empty()); | ||
DCHECK(lister_paths_.empty()); | ||
|
||
lister_ = std::make_unique<net::DirectoryLister>( | ||
lister_base_dir_, net::DirectoryLister::NO_SORT_RECURSIVE, this); | ||
lister_->Start(); | ||
} | ||
|
||
void FileSelectHelper::OnOpenDialogDone(gin_helper::Dictionary result) { | ||
bool canceled = true; | ||
result.Get("canceled", &canceled); | ||
|
||
if (!render_frame_host_ || canceled) { | ||
RunFileChooserEnd(); | ||
} else { | ||
std::vector<base::FilePath> paths; | ||
if (result.Get("filePaths", &paths)) { | ||
std::vector<ui::SelectedFileInfo> files = | ||
ui::FilePathListToSelectedFileInfoList(paths); | ||
// If we are uploading a folder we need to enumerate its contents | ||
if (mode_ == FileChooserParams::Mode::kUploadFolder && | ||
paths.size() >= 1) { | ||
lister_base_dir_ = paths[0]; | ||
EnumerateDirectory(); | ||
} else { | ||
#if defined(OS_MAC) | ||
base::ThreadPool::PostTask( | ||
FROM_HERE, | ||
{base::MayBlock(), | ||
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, | ||
base::BindOnce(&FileSelectHelper::ProcessSelectedFilesMac, | ||
base::Unretained(this), files)); | ||
#else | ||
ConvertToFileChooserFileInfoList(files); | ||
#endif | ||
} | ||
|
||
if (render_frame_host_ && !paths.empty()) { | ||
auto* browser_context = static_cast<electron::ElectronBrowserContext*>( | ||
render_frame_host_->GetProcess()->GetBrowserContext()); | ||
browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory, | ||
paths[0].DirName()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void FileSelectHelper::ConvertToFileChooserFileInfoList( | ||
const std::vector<ui::SelectedFileInfo>& files) { | ||
std::vector<FileChooserFileInfoPtr> file_info; | ||
|
||
for (const auto& file : files) { | ||
file_info.push_back(FileChooserFileInfo::NewNativeFile(NativeFileInfo::New( | ||
file.local_path, base::FilePath(file.display_name).AsUTF16Unsafe()))); | ||
} | ||
|
||
OnFilesSelected(std::move(file_info), lister_base_dir_); | ||
} | ||
|
||
void FileSelectHelper::OnSaveDialogDone(gin_helper::Dictionary result) { | ||
std::vector<FileChooserFileInfoPtr> file_info; | ||
bool canceled = true; | ||
result.Get("canceled", &canceled); | ||
|
||
if (!render_frame_host_ || canceled) { | ||
RunFileChooserEnd(); | ||
} else { | ||
base::FilePath path; | ||
if (result.Get("filePath", &path)) { | ||
file_info.push_back(FileChooserFileInfo::NewNativeFile( | ||
NativeFileInfo::New(path, path.BaseName().AsUTF16Unsafe()))); | ||
} | ||
// We should only call this if we have not cancelled the dialog. | ||
OnFilesSelected(std::move(file_info), base::FilePath()); | ||
} | ||
} | ||
|
||
void FileSelectHelper::OnFilesSelected( | ||
std::vector<FileChooserFileInfoPtr> file_info, | ||
base::FilePath base_dir) { | ||
if (listener_) { | ||
listener_->FileSelected(std::move(file_info), base_dir, mode_); | ||
listener_.reset(); | ||
} | ||
|
||
render_frame_host_ = nullptr; | ||
|
||
delete this; | ||
} | ||
|
||
void FileSelectHelper::RenderWidgetHostDestroyed( | ||
content::RenderWidgetHost* widget_host) { | ||
render_frame_host_ = nullptr; | ||
observer_.Remove(widget_host); | ||
} | ||
|
||
// content::WebContentsObserver: | ||
void FileSelectHelper::RenderFrameHostChanged( | ||
content::RenderFrameHost* old_host, | ||
content::RenderFrameHost* new_host) { | ||
if (!render_frame_host_) | ||
return; | ||
// The |old_host| and its children are now pending deletion. Do not give | ||
// them file access past this point. | ||
if (render_frame_host_ == old_host || | ||
render_frame_host_->IsDescendantOf(old_host)) { | ||
render_frame_host_ = nullptr; | ||
} | ||
} | ||
|
||
// content::WebContentsObserver: | ||
void FileSelectHelper::RenderFrameDeleted( | ||
content::RenderFrameHost* deleted_host) { | ||
if (deleted_host == render_frame_host_) | ||
render_frame_host_ = nullptr; | ||
} | ||
|
||
// content::WebContentsObserver: | ||
void FileSelectHelper::WebContentsDestroyed() { | ||
render_frame_host_ = nullptr; | ||
web_contents_ = nullptr; | ||
|
||
DeleteTemporaryFiles(); | ||
|
||
if (!lister_) | ||
delete this; | ||
} |
Oops, something went wrong.