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 {
friend class MessagePumpScopedAutoreleasePool;
friend class TestMessagePumpCFRunLoopBase;
// Tasks will be pumped in the run loop modes described by |mode_mask|, which
// maps bits to the index of an internal array of run loop mode identifiers.
explicit MessagePumpCFRunLoopBase(int mode_mask);
// Tasks will be pumped in the run loop modes described by
// |initial_mode_mask|, which maps bits to the index of an internal array of
// run loop mode identifiers.
explicit MessagePumpCFRunLoopBase(int initial_mode_mask);
~MessagePumpCFRunLoopBase() override;
// Subclasses should implement the work they need to do in MessagePump::Run
......@@ -117,12 +118,18 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// objects autoreleased by work to fall into the current autorelease pool.
virtual AutoreleasePoolType* CreateAutoreleasePool();
// Invokes function(run_loop_, arg, mode) for all the modes in |mode_mask_|.
template <typename Argument>
void InvokeForEnabledModes(void function(CFRunLoopRef, Argument, CFStringRef),
Argument argument);
// Enable and disable entries in |enabled_modes_| to match |mode_mask|.
void SetModeMask(int mode_mask);
// Get the current mode mask from |enabled_modes_|.
int GetModeMask() const;
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
// power use (see the comment in RunDelayedWorkTimer()), however there is no
// public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
......@@ -198,8 +205,8 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// The thread's run loop.
CFRunLoopRef run_loop_;
// Bitmask controlling the run loop modes in which posted tasks may run.
const int mode_mask_;
// The enabled modes. Posted tasks may run in any non-null entry.
std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes];
// The timer, sources, and observers are described above alongside their
// callbacks.
......@@ -308,6 +315,17 @@ class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
#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 {
public:
MessagePumpNSApplication();
......@@ -317,6 +335,8 @@ class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
void Quit() override;
private:
friend class ScopedPumpMessagesInPrivateModes;
// False after Quit is called.
bool keep_running_;
......
This diff is collapsed.
......@@ -6,6 +6,8 @@
#include "base/mac/scoped_cftyperef.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"
namespace base {
......@@ -105,4 +107,67 @@ TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) {
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer,
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
......@@ -12,6 +12,7 @@
#import "base/mac/scoped_sending_event.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#import "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#import "chrome/browser/mac/nsprocessinfo_additions.h"
......@@ -224,6 +225,9 @@ void RenderViewContextMenuMac::Show() {
base::MessageLoop::ScopedNestableTaskAllower allow(
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()|.
// User-initiated event-tracking loops protect against this by
// setting flags in -[CrApplication sendEvent:], but since
......
......@@ -6,6 +6,7 @@
#include <stddef.h>
#import "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h"
@interface WebMenuRunner (PrivateAPI)
......@@ -155,7 +156,11 @@
// Display the menu, and set a flag if a menu item was chosen.
[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];
......
......@@ -5,6 +5,7 @@
#import "ui/views/controls/menu/menu_runner_impl_cocoa.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/menu_controller.h"
#include "ui/base/models/menu_model.h"
......@@ -164,6 +165,9 @@ void MenuRunnerImplCocoa::RunMenuAt(Widget* parent,
closing_event_time_ = base::TimeTicks();
running_ = true;
// Ensure the UI can update while the menu is fading out.
base::ScopedPumpMessagesInPrivateModes pump_private;
NSWindow* window = parent->GetNativeWindow();
if (run_types & MenuRunner::CONTEXT_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