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 #import <objc/runtime.h> | |
7 | |
8 // https://crbug.com/692408 | |
Avi (use Gerrit)
2017/02/28 16:24:24
Radar # too?
Sidney San Martín
2017/03/01 00:50:20
Done.
| |
9 // | |
10 // Back as far as macOS 10.9, and up to at least macOS 10.12.3, an AppKit bug | |
11 // can leave window A in what looks like a key state while window B is actually | |
12 // key. Key presses go to window B even after you click on window A. | |
13 // | |
14 // To trigger the bug, open a menu which has custom views (e.g. the wrench | |
15 // menu) and then, while the menu is open, click the titlebar of a different | |
16 // window to bring it to the front. Then, click the first window to bring it | |
17 // *back* to the front and try to type. Input will go to the other window. | |
18 // | |
19 // ## Cause | |
20 // When a menu has any custom views, AppKit uses a different window | |
21 // class (NSCarbonMenuWindow) to represent it than it does otherwise | |
22 // (NSLimitedMenuViewWindow). NSCarbonMenuWindow overrides -[NSWindow | |
23 // makeKeyWindow] to make itself the key window without telling the old key | |
24 // window that it's lost key status (so that its appearance doesn't change). It | |
25 // saves the old key window in an instance variable. | |
26 // | |
27 // When the menu closes, it doesn't just make the saved key window key again. | |
28 // Instead, it picks the frontmost window that can become key. When the | |
29 // frontmost window is different — and clicking a window's titlebar brings it | |
30 // to the front — it never sends -[NSWindow resignKeyWindow] to the old key | |
31 // window, so it never loses key appearance and doesn't ask NSApplication to | |
32 // make it key when you click on it (since it thinks it's already key). | |
33 // | |
34 // ## Solution | |
35 // This category replaces a method on NSCarbonMenuWindow to send | |
36 // -resignKeyWindow to the saved window if the AppKit implementation made a | |
37 // different window key. It degrades to a noop if NSCarbonMenuWindow, its | |
38 // _rememberedKeyWindow ivar, or its | |
39 // _restorePreviousKeyWindowFromSavedProperties method no longer exists. | |
40 | |
41 WEAK_IMPORT_ATTRIBUTE | |
tapted
2017/02/28 23:24:13
Is this needed? I don't see it elsewhere in Chrome
Sidney San Martín
2017/03/01 00:50:20
Added a comment — or did you just mean a comment h
| |
42 @interface NSCarbonMenuWindow : NSWindow | |
43 - (void)_restorePreviousKeyWindowFromSavedProperties; | |
44 @end | |
45 | |
46 static void (*_restorePreviousKeyWindowFromSavedProperties)(NSCarbonMenuWindow*, | |
47 SEL); | |
48 | |
49 static void _restorePreviousKeyWindowFromSavedProperties_patch( | |
50 NSCarbonMenuWindow* self, | |
51 SEL _cmd) { | |
52 NSWindow* rememberedKeyWindow = nil; | |
53 object_getInstanceVariable(self, "_rememberedKeyWindow", | |
tapted
2017/03/01 02:12:20
What happens if `_rememberedKeyWindow` gets rename
Sidney San Martín
2017/03/01 03:08:35
In that case, object_getInstanceVariable() returns
tapted
2017/03/01 07:56:02
The return value of object_getInstanceVariable isn
Sidney San Martín
2017/03/01 17:42:59
OK, done.
| |
54 (void**)&rememberedKeyWindow); | |
55 _restorePreviousKeyWindowFromSavedProperties(self, _cmd); | |
56 if ([NSApp keyWindow] != rememberedKeyWindow) { | |
tapted
2017/02/28 23:24:13
What happens when switching applications? I think
Sidney San Martín
2017/03/01 00:50:20
Switching applications is OK. This all happens in
| |
57 [rememberedKeyWindow resignKeyWindow]; | |
tapted
2017/02/28 23:24:13
Is the menu still open at this point? Can we do so
Sidney San Martín
2017/03/01 00:50:20
Every NSCarbonMenuWindow has this bug, not just th
tapted
2017/03/01 02:12:20
I was going to ask "does Chrome have any other men
Sidney San Martín
2017/03/01 03:08:35
That's awesome! I just updated the radar with repr
| |
58 } | |
59 } | |
60 | |
61 @implementation NSCarbonMenuWindow (KeyWindowPatch) | |
62 | |
63 + (void)load { | |
64 _restorePreviousKeyWindowFromSavedProperties = | |
tapted
2017/02/28 23:24:13
This needs to safely bail out (and DCHECK) if Appl
Sidney San Martín
2017/03/01 00:50:20
It should be safe in these cases; let me know if i
tapted
2017/03/01 02:12:20
Is that how WEAK_IMPORT_ATTRIBUTE works? Wouldn't
Sidney San Martín
2017/03/01 03:08:35
I'm as sure as I can be that this attribute affect
tapted
2017/03/01 07:56:02
Maybe we can make a little test project? My readin
tapted
2017/03/01 08:20:35
Is the API documented to do nothing? Is it really
Sidney San Martín
2017/03/01 17:42:59
It's not mentioned for this function, but many run
Sidney San Martín
2017/03/01 17:42:59
You're right that it'll fail at compile time if th
tapted
2017/03/01 22:56:46
I guess this thread falls under "general advice" (
tapted
2017/03/01 22:56:46
Does + (void)load behave different? I suspect it d
| |
65 (void (*)(NSCarbonMenuWindow*, SEL))method_setImplementation( | |
66 class_getInstanceMethod( | |
67 [self class], | |
68 @selector(_restorePreviousKeyWindowFromSavedProperties)), | |
69 (IMP)_restorePreviousKeyWindowFromSavedProperties_patch); | |
70 } | |
71 | |
72 @end | |
OLD | NEW |