 Chromium Code Reviews
 Chromium Code Reviews Issue 2710763003:
  Work around an AppKit bug that can leave two windows in a key state.
    
  
    Issue 2710763003:
  Work around an AppKit bug that can leave two windows in a key state. 
  | 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 |