Commit 24ab401b authored by Trent Apted's avatar Trent Apted Committed by Commit Bot

Re-enable listening on private runloop modes, for a whitelist.

r470769 started observing private AppKit run loop modes to keep
UI and web pages responsive while menus were fading out, but it
interacted badly with app-modal dialogs.

This re-enables r470769, but only in 3 places:
 - Web page context menus
 - Web page <select> menus
 - menus run from toolkit-views UI.

      pause briefly when either the <select> menu or the right-
      click context menu are fading out. (The main menubar and the
      Chrome app/wrench menu still cause a brief pause when dismissed).

Bug: 726200, 602914, 640466, 652007
Test: https://jsfiddle.net/kvmy8q9d/5/ - the animation shouldn't
Change-Id: Idf769c1f830ccb3ddc67205acb9e86aac2ec0460
Reviewed-on: https://chromium-review.googlesource.com/647836Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Commit-Queue: Trent Apted <tapted@chromium.org>
Cr-Commit-Position: refs/heads/master@{#503112}
parent d8336a85
...@@ -91,9 +91,10 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump { ...@@ -91,9 +91,10 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
friend class MessagePumpScopedAutoreleasePool; friend class MessagePumpScopedAutoreleasePool;
friend class TestMessagePumpCFRunLoopBase; friend class TestMessagePumpCFRunLoopBase;
// Tasks will be pumped in the run loop modes described by |mode_mask|, which // Tasks will be pumped in the run loop modes described by
// maps bits to the index of an internal array of run loop mode identifiers. // |initial_mode_mask|, which maps bits to the index of an internal array of
explicit MessagePumpCFRunLoopBase(int mode_mask); // run loop mode identifiers.
explicit MessagePumpCFRunLoopBase(int initial_mode_mask);
~MessagePumpCFRunLoopBase() override; ~MessagePumpCFRunLoopBase() override;
// Subclasses should implement the work they need to do in MessagePump::Run // Subclasses should implement the work they need to do in MessagePump::Run
...@@ -117,12 +118,18 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump { ...@@ -117,12 +118,18 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// objects autoreleased by work to fall into the current autorelease pool. // objects autoreleased by work to fall into the current autorelease pool.
virtual AutoreleasePoolType* CreateAutoreleasePool(); virtual AutoreleasePoolType* CreateAutoreleasePool();
// Invokes function(run_loop_, arg, mode) for all the modes in |mode_mask_|. // Enable and disable entries in |enabled_modes_| to match |mode_mask|.
template <typename Argument> void SetModeMask(int mode_mask);
void InvokeForEnabledModes(void function(CFRunLoopRef, Argument, CFStringRef),
Argument argument); // Get the current mode mask from |enabled_modes_|.
int GetModeMask() const;
private: private:
class ScopedModeEnabler;
// The maximum number of run loop modes that can be monitored.
static constexpr int kNumModes = 4;
// Marking timers as invalid at the right time helps significantly reduce // Marking timers as invalid at the right time helps significantly reduce
// power use (see the comment in RunDelayedWorkTimer()), however there is no // power use (see the comment in RunDelayedWorkTimer()), however there is no
// public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
...@@ -198,8 +205,8 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump { ...@@ -198,8 +205,8 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// The thread's run loop. // The thread's run loop.
CFRunLoopRef run_loop_; CFRunLoopRef run_loop_;
// Bitmask controlling the run loop modes in which posted tasks may run. // The enabled modes. Posted tasks may run in any non-null entry.
const int mode_mask_; std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes];
// The timer, sources, and observers are described above alongside their // The timer, sources, and observers are described above alongside their
// callbacks. // callbacks.
...@@ -308,6 +315,17 @@ class MessagePumpUIApplication : public MessagePumpCFRunLoopBase { ...@@ -308,6 +315,17 @@ class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
#else #else
// While in scope, permits posted tasks to be run in private AppKit run loop
// modes that would otherwise make the UI unresponsive. E.g., menu fade out.
class BASE_EXPORT ScopedPumpMessagesInPrivateModes {
public:
ScopedPumpMessagesInPrivateModes();
~ScopedPumpMessagesInPrivateModes();
private:
DISALLOW_COPY_AND_ASSIGN(ScopedPumpMessagesInPrivateModes);
};
class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
public: public:
MessagePumpNSApplication(); MessagePumpNSApplication();
...@@ -317,6 +335,8 @@ class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { ...@@ -317,6 +335,8 @@ class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
void Quit() override; void Quit() override;
private: private:
friend class ScopedPumpMessagesInPrivateModes;
// False after Quit is called. // False after Quit is called.
bool keep_running_; bool keep_running_;
......
This diff is collapsed.
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace base { namespace base {
...@@ -105,4 +107,67 @@ TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) { ...@@ -105,4 +107,67 @@ TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) {
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer, CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer,
kMessageLoopExclusiveRunLoopMode); kMessageLoopExclusiveRunLoopMode);
} }
namespace {
// PostedTasks are only executed while the message pump has a delegate. That is,
// when a base::RunLoop is running, so in order to test whether posted tasks
// are run by CFRunLoopRunInMode and *not* by the regular RunLoop, we need to
// be inside a task that is also calling CFRunLoopRunInMode. This task runs the
// given |mode| after posting a task to increment a counter, then checks whether
// the counter incremented after emptying that run loop mode.
void IncrementInModeAndExpect(CFRunLoopMode mode, int result) {
// Since this task is "ours" rather than a system task, allow nesting.
MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
int counter = 0;
auto increment = BindRepeating([](int* i) { ++*i; }, &counter);
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, increment);
while (CFRunLoopRunInMode(mode, 0, true) == kCFRunLoopRunHandledSource)
;
ASSERT_EQ(result, counter);
}
} // namespace
// Tests the correct behavior of ScopedPumpMessagesInPrivateModes.
TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) {
MessageLoopForUI message_loop;
CFRunLoopMode kRegular = kCFRunLoopDefaultMode;
CFRunLoopMode kPrivate = CFSTR("NSUnhighlightMenuRunLoopMode");
// Work is seen when running in the default mode.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
// But not seen when running in a private mode.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
{
ScopedPumpMessagesInPrivateModes allow_private;
// Now the work should be seen.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 1));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
// The regular mode should also work the same.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
}
// And now the scoper is out of scope, private modes should no longer see it.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
// Only regular modes see it.
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1));
EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle());
}
} // namespace base } // namespace base
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#import "base/mac/scoped_sending_event.h" #import "base/mac/scoped_sending_event.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#import "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_command_ids.h"
#import "chrome/browser/mac/nsprocessinfo_additions.h" #import "chrome/browser/mac/nsprocessinfo_additions.h"
...@@ -224,6 +225,9 @@ void RenderViewContextMenuMac::Show() { ...@@ -224,6 +225,9 @@ void RenderViewContextMenuMac::Show() {
base::MessageLoop::ScopedNestableTaskAllower allow( base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoop::current()); base::MessageLoop::current());
// Ensure the UI can update while the menu is fading out.
base::ScopedPumpMessagesInPrivateModes pump_private;
// One of the events that could be pumped is |window.close()|. // One of the events that could be pumped is |window.close()|.
// User-initiated event-tracking loops protect against this by // User-initiated event-tracking loops protect against this by
// setting flags in -[CrApplication sendEvent:], but since // setting flags in -[CrApplication sendEvent:], but since
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#import "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
@interface WebMenuRunner (PrivateAPI) @interface WebMenuRunner (PrivateAPI)
...@@ -155,7 +156,11 @@ ...@@ -155,7 +156,11 @@
// Display the menu, and set a flag if a menu item was chosen. // Display the menu, and set a flag if a menu item was chosen.
[cell attachPopUpWithFrame:[dummyView bounds] inView:dummyView]; [cell attachPopUpWithFrame:[dummyView bounds] inView:dummyView];
[cell performClickWithFrame:[dummyView bounds] inView:dummyView]; {
// Ensure the UI can update while the menu is fading out.
base::ScopedPumpMessagesInPrivateModes pump_private;
[cell performClickWithFrame:[dummyView bounds] inView:dummyView];
}
[dummyView removeFromSuperview]; [dummyView removeFromSuperview];
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import "ui/views/controls/menu/menu_runner_impl_cocoa.h" #import "ui/views/controls/menu/menu_runner_impl_cocoa.h"
#include "base/mac/sdk_forward_declarations.h" #include "base/mac/sdk_forward_declarations.h"
#import "base/message_loop/message_pump_mac.h"
#import "ui/base/cocoa/cocoa_base_utils.h" #import "ui/base/cocoa/cocoa_base_utils.h"
#import "ui/base/cocoa/menu_controller.h" #import "ui/base/cocoa/menu_controller.h"
#include "ui/base/models/menu_model.h" #include "ui/base/models/menu_model.h"
...@@ -164,6 +165,9 @@ void MenuRunnerImplCocoa::RunMenuAt(Widget* parent, ...@@ -164,6 +165,9 @@ void MenuRunnerImplCocoa::RunMenuAt(Widget* parent,
closing_event_time_ = base::TimeTicks(); closing_event_time_ = base::TimeTicks();
running_ = true; running_ = true;
// Ensure the UI can update while the menu is fading out.
base::ScopedPumpMessagesInPrivateModes pump_private;
NSWindow* window = parent->GetNativeWindow(); NSWindow* window = parent->GetNativeWindow();
if (run_types & MenuRunner::CONTEXT_MENU) { if (run_types & MenuRunner::CONTEXT_MENU) {
[NSMenu popUpContextMenu:[menu_controller_ menu] [NSMenu popUpContextMenu:[menu_controller_ menu]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment