by nathansobo » Mon Sep 24, 2012 3:14 pm
After exploring this, I've found that that other fix is related, but insufficient for handling command-`. Chrome actually has a custom subclass of UnderlayOpenGLHostingWindow that includes logic to address this case. Basically, if the browser or keyboard handling delegate don't handle the event, it needs to be "redispatched" on the window. They set a `_redispatchingEvent` flag and send the event back to the window, but this time they don't send it to the browser process. This allows the event to bubble up the standard Cocoa event handling chain, and I suppose it eventually reaches a layer that handles the command-` key. The main menu performKeyEquivalent is *not* where this occurs, so just calling that method on menu isn't enough:
Excerpt from chrome_event_processing_window.h:
// Sends a key event to |NSApp sendEvent:|, but also makes sure that it's not
// short-circuited to the RWHV. This is used to send keyboard events to the menu
// and the cmd-` handler if a keyboard event comes back unhandled from the
// renderer. The event must be of type |NSKeyDown|, |NSKeyUp|, or
// |NSFlagsChanged|.
// Returns |YES| if |event| has been handled.
- (BOOL)redispatchKeyEvent:(NSEvent*)event;
Excerpts from chrome_event_processing_window.mm
- (BOOL)redispatchKeyEvent:(NSEvent*)event {
DCHECK(event);
NSEventType eventType = [event type];
if (eventType != NSKeyDown &&
eventType != NSKeyUp &&
eventType != NSFlagsChanged) {
NOTREACHED();
return YES; // Pretend it's been handled in an effort to limit damage.
}
// Ordinarily, the event's window should be this window. However, when
// switching between normal and fullscreen mode, we switch out the window, and
// the event's window might be the previous window (or even an earlier one if
// the renderer is running slowly and several mode switches occur). In this
// rare case, we synthesize a new key event so that its associate window
// (number) is our own.
if ([event window] != self)
event = [self keyEventForWindow:self fromKeyEvent:event];
// Redispatch the event.
eventHandled_ = YES;
redispatchingEvent_ = YES; // see sendEvent method below for where this gets used
[NSApp sendEvent:event];
redispatchingEvent_ = NO;
// If the event was not handled by [NSApp sendEvent:], the sendEvent:
// method below will be called, and because |redispatchingEvent_| is YES,
// |eventHandled_| will be set to NO.
return eventHandled_;
}
- (void)sendEvent:(NSEvent*)event {
if (!redispatchingEvent_)
[super sendEvent:event];
else
eventHandled_ = NO;
}