Chromium Code Reviews| Index: chrome/browser/mac/patch_nsmenu_key_window_bug.mm |
| diff --git a/chrome/browser/mac/patch_nsmenu_key_window_bug.mm b/chrome/browser/mac/patch_nsmenu_key_window_bug.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f0e32205c4fdb3a53b615324b0eb88018a262fa8 |
| --- /dev/null |
| +++ b/chrome/browser/mac/patch_nsmenu_key_window_bug.mm |
| @@ -0,0 +1,78 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#import <AppKit/AppKit.h> |
| + |
| +#include "base/logging.h" |
| +#import "base/mac/scoped_objc_class_swizzler.h" |
| + |
| +// https://crbug.com/692408 |
| +// rdar://30572648 |
| +// |
| +// Back as far as macOS 10.9, and up to at least macOS 10.12.3, an AppKit bug |
| +// can leave window A in what looks like a key state while window B is actually |
| +// key. Key presses go to window B even after you click on window A. |
| +// |
| +// To trigger the bug, open a menu which has custom views (e.g. the Help menu) |
| +// and then, while the menu is open, click the titlebar of a different window |
| +// to bring it to the front. Then, click the first window to bring it *back* to |
| +// the front and try to type. Input will go to the other window. |
| +// |
| +// ## Cause |
| +// When a menu has any custom views, AppKit uses a different class |
| +// (NSCarbonMenuWindow) to represent its window than it does otherwise |
| +// (NSLimitedMenuViewWindow). NSCarbonMenuWindow overrides -[NSWindow |
| +// makeKeyWindow] to make itself the key window without telling the old key |
| +// window that it's lost key status (so that its appearance doesn't change). It |
| +// saves the old key window in an instance variable. |
| +// |
| +// When the menu closes, it doesn't just make the saved key window key again. |
| +// Instead, it picks the frontmost window that can become key. When the |
| +// frontmost window is different (clicking a window's titlebar brings it to the |
| +// front) it never sends -[NSWindow resignKeyWindow] to the old key window, so |
| +// it never loses key appearance and doesn't ask NSApplication to make it key |
| +// when you click on it (since it thinks it's already key). |
| +// |
| +// ## Workaround |
| +// This file hooks a method on NSCarbonMenuWindow to send -resignKeyWindow to |
| +// the saved window if a different window has become key. |
| +// |
| +// ## If it's an AppKit bug, why work around it? |
| +// The app/wrench/hotdog menu uses custom views, which gives Chrome more |
|
Robert Sesek
2017/03/06 20:57:38
This CL makes sense, but I'm a little concerned ab
Sidney San Martín
2017/03/06 21:38:49
The bug also affects AppKit-provided menus like th
|
| +// exposure to this bug than many apps, and users have run into it in the wild. |
| + |
| +@interface NSCarbonMenuWindowPatcher : NSObject |
| +- (void)_restorePreviousKeyWindowFromSavedProperties; |
| +@end |
| + |
| +@implementation NSCarbonMenuWindowPatcher |
| + |
| +static IMP g_original__restorePreviousKeyWindowFromSavedProperties; |
| + |
| ++ (void)load { |
| + Class nsCarbonMenuWindowClass = NSClassFromString(@"NSCarbonMenuWindow"); |
| + DCHECK(nsCarbonMenuWindowClass); |
| + if (nsCarbonMenuWindowClass == nil) |
| + return; |
| + CR_DEFINE_STATIC_LOCAL( |
| + base::mac::ScopedObjCClassSwizzler, swizzler, |
| + (nsCarbonMenuWindowClass, [self class], |
| + @selector(_restorePreviousKeyWindowFromSavedProperties))); |
| + g_original__restorePreviousKeyWindowFromSavedProperties = |
| + swizzler.GetOriginalImplementation(); |
| +} |
| + |
| +- (void)_restorePreviousKeyWindowFromSavedProperties { |
| + NSWindow* rememberedKeyWindow = nil; |
| + Ivar ivar = object_getInstanceVariable( |
| + self, "_rememberedKeyWindow", |
| + reinterpret_cast<void**>(&rememberedKeyWindow)); |
| + DCHECK(ivar); |
| + g_original__restorePreviousKeyWindowFromSavedProperties(self, _cmd); |
| + if ([NSApp keyWindow] != rememberedKeyWindow) { |
| + [rememberedKeyWindow resignKeyWindow]; |
| + } |
| +} |
| + |
| +@end |