chore: DirectoryLister memory management improvement (#18634)

* chore: small memory management improvement

Co-authored-by: Charles Kerr <ckerr@github.com>

* chore: fix code style

* use start-from-one ref count, check ref_counted.h for motivations
* reuse list_base_dir_
* net::DirectorLister offloads directory enumeration to a different
  task sequence in its implementation, use of sequence runner on
  our end is unnecessary
* Don't manually `Release` in `WebContentsDestroyed`, content::FileSelectListener
  already handles this case.
This commit is contained in:
Shelley Vohr 2019-07-01 07:58:06 -07:00 committed by GitHub
parent 3859244a79
commit 3038846f5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 77 deletions

View file

@ -21,6 +21,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "native_mate/dictionary.h"
#include "net/base/directory_lister.h"
#include "net/base/mime_util.h"
#include "shell/browser/atom_browser_context.h"
#include "shell/browser/native_window.h"
@ -36,8 +37,10 @@ namespace {
class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
public content::WebContentsObserver,
public electron::DirectoryListerHelperDelegate {
public net::DirectoryLister::DirectoryListerDelegate {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
FileSelectHelper(content::RenderFrameHost* render_frame_host,
std::unique_ptr<content::FileSelectListener> listener,
blink::mojom::FileChooserParams::Mode mode)
@ -73,28 +76,46 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
base::BindOnce(&FileSelectHelper::OnSaveDialogDone, this)))));
}
void OnDirectoryListerDone(std::vector<FileChooserFileInfoPtr> file_info,
base::FilePath base_dir) override {
OnFilesSelected(std::move(file_info), base_dir);
Release();
}
private:
friend class base::RefCounted<FileSelectHelper>;
~FileSelectHelper() override {}
void EnumerateDirectory(base::FilePath base_dir) {
auto* lister = new net::DirectoryLister(
base_dir, net::DirectoryLister::NO_SORT_RECURSIVE,
new electron::DirectoryListerHelper(base_dir, this));
lister->Start();
// net::DirectoryLister::DirectoryListerDelegate
void OnListFile(
const net::DirectoryLister::DirectoryListerData& data) override {
// We don't want to return directory paths, only file paths
if (data.info.IsDirectory())
return;
lister_paths_.push_back(data.path);
}
// net::DirectoryLister::DirectoryListerDelegate
void OnListDone(int error) override {
std::vector<FileChooserFileInfoPtr> file_info;
for (const auto& path : lister_paths_)
file_info.push_back(FileChooserFileInfo::NewNativeFile(
blink::mojom::NativeFileInfo::New(path, base::string16())));
OnFilesSelected(std::move(file_info), lister_base_dir_);
Release();
}
void EnumerateDirectory() {
// Ensure that this fn is only called once
DCHECK(!lister_);
DCHECK(!lister_base_dir_.empty());
DCHECK(lister_paths_.empty());
lister_.reset(new net::DirectoryLister(
lister_base_dir_, net::DirectoryLister::NO_SORT_RECURSIVE, this));
lister_->Start();
// 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. Once the directory lister is complete we
// Release() in OnDirectoryListerDone() and at that point we run
// OnFilesSelected() which will deref the last reference held by the
// listener.
// Release() & at that point we run OnFilesSelected() which will
// deref the last reference held by the listener.
AddRef();
}
@ -102,7 +123,6 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
std::vector<FileChooserFileInfoPtr> file_info;
bool canceled = true;
result.Get("canceled", &canceled);
base::FilePath base_dir;
// For certain file chooser modes (kUploadFolder) we need to do some async
// work before calling back to the listener. In that particular case the
// listener is called from the directory enumerator.
@ -114,12 +134,8 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
// If we are uploading a folder we need to enumerate its contents
if (mode_ == FileChooserParams::Mode::kUploadFolder &&
paths.size() >= 1) {
base_dir = paths[0];
// Actually enumerate soemwhere off-thread
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FileSelectHelper::EnumerateDirectory,
this, base_dir));
lister_base_dir_ = paths[0];
EnumerateDirectory();
} else {
for (auto& path : paths) {
file_info.push_back(FileChooserFileInfo::NewNativeFile(
@ -140,7 +156,7 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
}
if (ready_to_call_listener)
OnFilesSelected(std::move(file_info), base_dir);
OnFilesSelected(std::move(file_info), lister_base_dir_);
}
void OnSaveDialogDone(mate::Dictionary result) {
@ -187,6 +203,11 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
content::RenderFrameHost* render_frame_host_;
std::unique_ptr<content::FileSelectListener> listener_;
blink::mojom::FileChooserParams::Mode mode_;
// DirectoryLister-specific members
std::unique_ptr<net::DirectoryLister> lister_;
base::FilePath lister_base_dir_;
std::vector<base::FilePath> lister_paths_;
};
file_dialog::Filters GetFileTypesFromAcceptType(
@ -227,7 +248,7 @@ file_dialog::Filters GetFileTypesFromAcceptType(
valid_type_count++;
}
// If no valid exntesion is added, return empty filters.
// If no valid extension is added, return empty filters.
if (extensions.empty())
return filters;
@ -259,30 +280,6 @@ file_dialog::Filters GetFileTypesFromAcceptType(
namespace electron {
DirectoryListerHelper::DirectoryListerHelper(
base::FilePath base,
DirectoryListerHelperDelegate* helper)
: base_dir_(base), delegate_(helper) {}
DirectoryListerHelper::~DirectoryListerHelper() {}
void DirectoryListerHelper::OnListFile(
const net::DirectoryLister::DirectoryListerData& data) {
// We don't want to return directory paths, only file paths
if (data.info.IsDirectory())
return;
paths_.push_back(data.path);
}
void DirectoryListerHelper::OnListDone(int error) {
std::vector<FileChooserFileInfoPtr> file_info;
for (auto path : paths_)
file_info.push_back(FileChooserFileInfo::NewNativeFile(
blink::mojom::NativeFileInfo::New(path, base::string16())));
delegate_->OnDirectoryListerDone(std::move(file_info), base_dir_);
delete this;
}
WebDialogHelper::WebDialogHelper(NativeWindow* window, bool offscreen)
: window_(window), offscreen_(offscreen), weak_factory_(this) {}
@ -298,8 +295,8 @@ void WebDialogHelper::RunFileChooser(
settings.parent_window = window_;
settings.title = base::UTF16ToUTF8(params.title);
scoped_refptr<FileSelectHelper> file_select_helper(new FileSelectHelper(
render_frame_host, std::move(listener), params.mode));
auto file_select_helper = base::MakeRefCounted<FileSelectHelper>(
render_frame_host, std::move(listener), params.mode);
if (params.mode == FileChooserParams::Mode::kSave) {
settings.default_path = params.default_file_name;
file_select_helper->ShowSaveDialog(settings);

View file

@ -9,7 +9,6 @@
#include <vector>
#include "base/memory/weak_ptr.h"
#include "net/base/directory_lister.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
namespace base {
@ -24,32 +23,6 @@ class WebContents;
namespace electron {
class DirectoryListerHelperDelegate {
public:
virtual void OnDirectoryListerDone(
std::vector<blink::mojom::FileChooserFileInfoPtr> file_info,
base::FilePath base_dir) = 0;
};
class DirectoryListerHelper
: public net::DirectoryLister::DirectoryListerDelegate {
public:
DirectoryListerHelper(base::FilePath base,
DirectoryListerHelperDelegate* helper);
~DirectoryListerHelper() override;
private:
void OnListFile(
const net::DirectoryLister::DirectoryListerData& data) override;
void OnListDone(int error) override;
base::FilePath base_dir_;
DirectoryListerHelperDelegate* delegate_;
std::vector<base::FilePath> paths_;
DISALLOW_COPY_AND_ASSIGN(DirectoryListerHelper);
};
class NativeWindow;
class WebDialogHelper {