Skip to content

Commit

Permalink
fix: retain menu when popuping (#21226)
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz authored and John Kleinschmidt committed Nov 20, 2019
1 parent 3e52002 commit 0b2c61a
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 38 deletions.
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

0 comments on commit 0b2c61a

Please sign in to comment.