Skip to content

Commit

Permalink
fix: transparently package bundles as zip archives (#26555)
Browse files Browse the repository at this point in the history
  • Loading branch information
codebytere committed Nov 18, 2020
1 parent c6979ca commit 9900e69
Show file tree
Hide file tree
Showing 6 changed files with 548 additions and 201 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Expand Up @@ -370,6 +370,7 @@ source_set("electron_lib") {
"//third_party/libyuv",
"//third_party/webrtc_overrides:webrtc_component",
"//third_party/widevine/cdm:headers",
"//third_party/zlib/google:zip",
"//ui/base/idle",
"//ui/events:dom_keycode_converter",
"//ui/gl",
Expand Down
3 changes: 3 additions & 0 deletions filenames.gni
Expand Up @@ -187,6 +187,9 @@ filenames = {
"shell/browser/extended_web_contents_observer.h",
"shell/browser/feature_list.cc",
"shell/browser/feature_list.h",
"shell/browser/file_select_helper.cc",
"shell/browser/file_select_helper.h",
"shell/browser/file_select_helper_mac.mm",
"shell/browser/font_defaults.cc",
"shell/browser/font_defaults.h",
"shell/browser/javascript_environment.cc",
Expand Down
270 changes: 270 additions & 0 deletions shell/browser/file_select_helper.cc
@@ -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,
std::unique_ptr<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_MACOSX)
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;
}

0 comments on commit 9900e69

Please sign in to comment.