OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #import <AppKit/AppKit.h> | |
6 | |
7 #include "base/logging.h" | |
8 #import "base/mac/scoped_objc_class_swizzler.h" | |
9 | |
10 // https://crbug.com/692408 | |
11 // rdar://30572648 | |
12 // | |
13 // Back as far as macOS 10.9, and up to at least macOS 10.12.3, an AppKit bug | |
14 // can leave window A in what looks like a key state while window B is actually | |
15 // key. Key presses go to window B even after you click on window A. | |
16 // | |
17 // To trigger the bug, open a menu which has custom views (e.g. the Help menu) | |
18 // and then, while the menu is open, click the titlebar of a different window | |
19 // to bring it to the front. Then, click the first window to bring it *back* to | |
20 // the front and try to type. Input will go to the other window. | |
21 // | |
22 // ## Cause | |
23 // When a menu has any custom views, AppKit uses a different class | |
24 // (NSCarbonMenuWindow) to represent its window than it does otherwise | |
25 // (NSLimitedMenuViewWindow). NSCarbonMenuWindow overrides -[NSWindow | |
26 // makeKeyWindow] to make itself the key window without telling the old key | |
27 // window that it's lost key status (so that its appearance doesn't change). It | |
28 // saves the old key window in an instance variable. | |
29 // | |
30 // When the menu closes, it doesn't just make the saved key window key again. | |
31 // Instead, it picks the frontmost window that can become key. When the | |
32 // frontmost window is different (clicking a window's titlebar brings it to the | |
33 // front) it never sends -[NSWindow resignKeyWindow] to the old key window, so | |
34 // it never loses key appearance and doesn't ask NSApplication to make it key | |
35 // when you click on it (since it thinks it's already key). | |
36 // | |
37 // ## Workaround | |
38 // This file hooks a method on NSCarbonMenuWindow to send -resignKeyWindow to | |
39 // the saved window if a different window has become key. | |
40 // | |
41 // ## If it's an AppKit bug, why work around it? | |
42 // 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
| |
43 // exposure to this bug than many apps, and users have run into it in the wild. | |
44 | |
45 @interface NSCarbonMenuWindowPatcher : NSObject | |
46 - (void)_restorePreviousKeyWindowFromSavedProperties; | |
47 @end | |
48 | |
49 @implementation NSCarbonMenuWindowPatcher | |
50 | |
51 static IMP g_original__restorePreviousKeyWindowFromSavedProperties; | |
52 | |
53 + (void)load { | |
54 Class nsCarbonMenuWindowClass = NSClassFromString(@"NSCarbonMenuWindow"); | |
55 DCHECK(nsCarbonMenuWindowClass); | |
56 if (nsCarbonMenuWindowClass == nil) | |
57 return; | |
58 CR_DEFINE_STATIC_LOCAL( | |
59 base::mac::ScopedObjCClassSwizzler, swizzler, | |
60 (nsCarbonMenuWindowClass, [self class], | |
61 @selector(_restorePreviousKeyWindowFromSavedProperties))); | |
62 g_original__restorePreviousKeyWindowFromSavedProperties = | |
63 swizzler.GetOriginalImplementation(); | |
64 } | |
65 | |
66 - (void)_restorePreviousKeyWindowFromSavedProperties { | |
67 NSWindow* rememberedKeyWindow = nil; | |
68 Ivar ivar = object_getInstanceVariable( | |
69 self, "_rememberedKeyWindow", | |
70 reinterpret_cast<void**>(&rememberedKeyWindow)); | |
71 DCHECK(ivar); | |
72 g_original__restorePreviousKeyWindowFromSavedProperties(self, _cmd); | |
73 if ([NSApp keyWindow] != rememberedKeyWindow) { | |
74 [rememberedKeyWindow resignKeyWindow]; | |
75 } | |
76 } | |
77 | |
78 @end | |
OLD | NEW |