Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: menu should not be garbage-collected when popuping (6-1-x) #21226

Merged
merged 1 commit into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 13 additions & 1 deletion atom/browser/api/atom_api_menu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

#include "atom/browser/api/atom_api_menu.h"

#include <utility>

#include "atom/browser/native_window.h"
#include "atom/common/native_mate_converters/accelerator_converter.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/once_callback.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/constructor.h"
Expand Down Expand Up @@ -199,6 +201,16 @@ void Menu::OnMenuWillShow() {
Emit("menu-will-show");
}

base::OnceClosure Menu::BindSelfToClosure(base::OnceClosure callback) {
// return ((callback, ref) => { callback() }).bind(null, callback, this)
v8::Global<v8::Value> ref(isolate(), GetWrapper());
return base::BindOnce(
[](base::OnceClosure callback, v8::Global<v8::Value> ref) {
std::move(callback).Run();
},
std::move(callback), std::move(ref));
}

// static
void Menu::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
Expand Down
7 changes: 6 additions & 1 deletion atom/browser/api/atom_api_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class Menu : public mate::TrackableObject<Menu>,
int x,
int y,
int positioning_item,
const base::Closure& callback) = 0;
base::OnceClosure callback) = 0;
virtual void ClosePopupAt(int32_t window_id) = 0;

std::unique_ptr<AtomMenuModel> model_;
Expand All @@ -71,6 +71,11 @@ class Menu : public mate::TrackableObject<Menu>,
void OnMenuWillClose() override;
void OnMenuWillShow() override;

protected:
// Returns a new callback which keeps references of the JS wrapper until the
// passed |callback| is called.
base::OnceClosure BindSelfToClosure(base::OnceClosure callback);

private:
void InsertItemAt(int index, int command_id, const base::string16& label);
void InsertSeparatorAt(int index);
Expand Down
6 changes: 3 additions & 3 deletions atom/browser/api/atom_api_menu_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ class MenuMac : public Menu {
int x,
int y,
int positioning_item,
const base::Closure& callback) override;
base::OnceClosure callback) override;
void PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
int32_t window_id,
int x,
int y,
int positioning_item,
base::Closure callback);
base::OnceClosure callback);
void ClosePopupAt(int32_t window_id) override;

private:
friend class Menu;

void OnClosed(int32_t window_id, base::Closure callback);
void OnClosed(int32_t window_id, base::OnceClosure callback);

scoped_nsobject<AtomMenuController> menu_controller_;

Expand Down
31 changes: 17 additions & 14 deletions atom/browser/api/atom_api_menu_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,35 @@
int x,
int y,
int positioning_item,
const base::Closure& callback) {
base::OnceClosure callback) {
NativeWindow* native_window = window->window();
if (!native_window)
return;

auto popup = base::Bind(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(),
native_window->GetWeakPtr(), window->weak_map_id(), x,
y, positioning_item, callback);
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, popup);
// Make sure the Menu object would not be garbage-collected until the callback
// has run.
base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback));

auto popup =
base::BindOnce(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(),
native_window->GetWeakPtr(), window->weak_map_id(), x, y,
positioning_item, std::move(callback_with_ref));
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, std::move(popup));
}

void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
int32_t window_id,
int x,
int y,
int positioning_item,
base::Closure callback) {
mate::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());

base::OnceClosure callback) {
if (!native_window)
return;
NSWindow* nswindow = native_window->GetNativeWindow().GetNativeNSWindow();

auto close_callback = base::Bind(
&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback);
base::OnceClosure close_callback =
base::BindOnce(&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id,
std::move(callback));
popup_controllers_[window_id] = base::scoped_nsobject<AtomMenuController>(
[[AtomMenuController alloc] initWithModel:model()
useDefaultAccelerator:NO]);
Expand Down Expand Up @@ -99,7 +102,7 @@
if (rightmostMenuPoint > screenRight)
position.x = position.x - [menu size].width;

[popup_controllers_[window_id] setCloseCallback:close_callback];
[popup_controllers_[window_id] setCloseCallback:std::move(close_callback)];
// Make sure events can be pumped while the menu is up.
base::MessageLoopCurrent::ScopedNestableTaskAllower allow;

Expand Down Expand Up @@ -130,9 +133,9 @@
}
}

void MenuMac::OnClosed(int32_t window_id, base::Closure callback) {
void MenuMac::OnClosed(int32_t window_id, base::OnceClosure callback) {
popup_controllers_.erase(window_id);
callback.Run();
std::move(callback).Run();
}

// static
Expand Down
25 changes: 16 additions & 9 deletions atom/browser/api/atom_api_menu_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "atom/browser/api/atom_api_menu_views.h"

#include <memory>
#include <utility>

#include "atom/browser/native_window_views.h"
#include "atom/browser/unresponsive_suppressor.h"
Expand All @@ -25,10 +26,7 @@ void MenuViews::PopupAt(TopLevelWindow* window,
int x,
int y,
int positioning_item,
const base::Closure& callback) {
mate::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());

base::OnceClosure callback) {
auto* native_window = static_cast<NativeWindowViews*>(window->window());
if (!native_window)
return;
Expand All @@ -47,12 +45,21 @@ void MenuViews::PopupAt(TopLevelWindow* window,
// Don't emit unresponsive event when showing menu.
atom::UnresponsiveSuppressor suppressor;

// Make sure the Menu object would not be garbage-collected until the callback
// has run.
base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback));

// Show the menu.
//
// Note that while views::MenuRunner accepts RepeatingCallback as close
// callback, it is fine passing OnceCallback to it because we reset the
// menu runner immediately when the menu is closed.
int32_t window_id = window->weak_map_id();
auto close_callback = base::Bind(
&MenuViews::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback);
auto close_callback = base::AdaptCallbackForRepeating(
base::BindOnce(&MenuViews::OnClosed, weak_factory_.GetWeakPtr(),
window_id, std::move(callback_with_ref)));
menu_runners_[window_id] =
std::make_unique<MenuRunner>(model(), flags, close_callback);
std::make_unique<MenuRunner>(model(), flags, std::move(close_callback));
menu_runners_[window_id]->RunMenuAt(
native_window->widget(), NULL, gfx::Rect(location, gfx::Size()),
views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE);
Expand All @@ -72,9 +79,9 @@ void MenuViews::ClosePopupAt(int32_t window_id) {
}
}

void MenuViews::OnClosed(int32_t window_id, base::Closure callback) {
void MenuViews::OnClosed(int32_t window_id, base::OnceClosure callback) {
menu_runners_.erase(window_id);
callback.Run();
std::move(callback).Run();
}

// static
Expand Down
4 changes: 2 additions & 2 deletions atom/browser/api/atom_api_menu_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ class MenuViews : public Menu {
int x,
int y,
int positioning_item,
const base::Closure& callback) override;
base::OnceClosure callback) override;
void ClosePopupAt(int32_t window_id) override;

private:
void OnClosed(int32_t window_id, base::Closure callback);
void OnClosed(int32_t window_id, base::OnceClosure callback);

// window ID -> open context menu
std::map<int32_t, std::unique_ptr<views::MenuRunner>> menu_runners_;
Expand Down
4 changes: 2 additions & 2 deletions atom/browser/ui/cocoa/atom_menu_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AtomMenuModel;
base::scoped_nsobject<NSMenu> menu_;
BOOL isMenuOpen_;
BOOL useDefaultAccelerator_;
base::Callback<void()> closeCallback;
base::OnceClosure closeCallback;
}

@property(nonatomic, assign) atom::AtomMenuModel* model;
Expand All @@ -37,7 +37,7 @@ class AtomMenuModel;
// to the contents of the model after calling this will not be noticed.
- (id)initWithModel:(atom::AtomMenuModel*)model useDefaultAccelerator:(BOOL)use;

- (void)setCloseCallback:(const base::Callback<void()>&)callback;
- (void)setCloseCallback:(base::OnceClosure)callback;

// Populate current NSMenu with |model|.
- (void)populateWithModel:(atom::AtomMenuModel*)model;
Expand Down
10 changes: 6 additions & 4 deletions atom/browser/ui/cocoa/atom_menu_controller.mm
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ - (void)dealloc {
[super dealloc];
}

- (void)setCloseCallback:(const base::Callback<void()>&)callback {
closeCallback = callback;
- (void)setCloseCallback:(base::OnceClosure)callback {
closeCallback = std::move(callback);
}

- (void)populateWithModel:(atom::AtomMenuModel*)model {
Expand Down Expand Up @@ -153,7 +153,8 @@ - (void)cancel {
isMenuOpen_ = NO;
model_->MenuWillClose();
if (!closeCallback.is_null()) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, closeCallback);
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
std::move(closeCallback));
}
}
}
Expand Down Expand Up @@ -391,7 +392,8 @@ - (void)menuDidClose:(NSMenu*)menu {
// Post async task so that itemSelected runs before the close callback
// deletes the controller from the map which deallocates it
if (!closeCallback.is_null()) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, closeCallback);
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
std::move(closeCallback));
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions native_mate/native_mate/function_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef NATIVE_MATE_FUNCTION_TEMPLATE_H_
#define NATIVE_MATE_FUNCTION_TEMPLATE_H_

#include <utility>

#include "base/callback.h"
#include "base/logging.h"
#include "native_mate/arguments.h"
Expand Down Expand Up @@ -193,7 +195,7 @@ class Invoker<IndicesHolder<indices...>, ArgTypes...>
void DispatchToCallback(base::Callback<ReturnType(ArgTypes...)> callback) {
v8::MicrotasksScope script_scope(
args_->isolate(), v8::MicrotasksScope::kRunMicrotasks);
args_->Return(callback.Run(ArgumentHolder<indices, ArgTypes>::value...));
args_->Return(callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...));
}

// In C++, you can declare the function foo(void), but you can't pass a void
Expand All @@ -202,7 +204,7 @@ class Invoker<IndicesHolder<indices...>, ArgTypes...>
void DispatchToCallback(base::Callback<void(ArgTypes...)> callback) {
v8::MicrotasksScope script_scope(
args_->isolate(), v8::MicrotasksScope::kRunMicrotasks);
callback.Run(ArgumentHolder<indices, ArgTypes>::value...);
callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...);
}

private:
Expand Down