1 // Copyright 2015 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.
5 #import "ui/base/cocoa/command_dispatcher.h"
7 #include "base/logging.h"
11 // Duplicate the given key event, but changing the associated window.
12 NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) {
13 NSEventType event_type = [event type];
15 // Convert the event's location from the original window's coordinates into
17 NSPoint location = [event locationInWindow];
18 location = [[event window] convertBaseToScreen:location];
19 location = [window convertScreenToBase:location];
21 // Various things *only* apply to key down/up.
22 bool is_a_repeat = false;
23 NSString* characters = nil;
24 NSString* charactors_ignoring_modifiers = nil;
25 if (event_type == NSKeyDown || event_type == NSKeyUp) {
26 is_a_repeat = [event isARepeat];
27 characters = [event characters];
28 charactors_ignoring_modifiers = [event charactersIgnoringModifiers];
31 // This synthesis may be slightly imperfect: we provide nil for the context,
32 // since I (viettrungluu) am sceptical that putting in the original context
33 // (if one is given) is valid.
34 return [NSEvent keyEventWithType:event_type
36 modifierFlags:[event modifierFlags]
37 timestamp:[event timestamp]
38 windowNumber:[window windowNumber]
41 charactersIgnoringModifiers:charactors_ignoring_modifiers
43 keyCode:[event keyCode]];
48 @implementation CommandDispatcher {
50 BOOL redispatchingEvent_;
52 NSWindow<CommandDispatchingWindow>* owner_; // Weak, owns us.
55 @synthesize delegate = delegate_;
57 - (instancetype)initWithOwner:(NSWindow<CommandDispatchingWindow>*)owner {
58 if ((self = [super init])) {
64 - (BOOL)performKeyEquivalent:(NSEvent*)event {
65 if ([delegate_ eventHandledByExtensionCommand:event
66 isRedispatch:redispatchingEvent_]) {
70 if (redispatchingEvent_)
73 // Give a CommandDispatcherTarget (e.g. a web site) a chance to handle the
74 // event. If it doesn't want to handle it, it will call us back with
75 // -redispatchKeyEvent:.
76 NSResponder* r = [owner_ firstResponder];
77 if ([r conformsToProtocol:@protocol(CommandDispatcherTarget)])
78 return [r performKeyEquivalent:event];
80 if ([delegate_ prePerformKeyEquivalent:event window:owner_])
83 if ([owner_ defaultPerformKeyEquivalent:event])
86 return [delegate_ postPerformKeyEquivalent:event window:owner_];
89 - (BOOL)redispatchKeyEvent:(NSEvent*)event {
91 NSEventType eventType = [event type];
92 if (eventType != NSKeyDown && eventType != NSKeyUp &&
93 eventType != NSFlagsChanged) {
95 return YES; // Pretend it's been handled in an effort to limit damage.
98 // Ordinarily, the event's window should be |owner_|. However, when switching
99 // between normal and fullscreen mode, we switch out the window, and the
100 // event's window might be the previous window (or even an earlier one if the
101 // renderer is running slowly and several mode switches occur). In this rare
102 // case, we synthesize a new key event so that its associate window (number)
103 // is our |owner_|'s.
104 if ([event window] != owner_)
105 event = KeyEventForWindow(owner_, event);
107 // Redispatch the event.
109 redispatchingEvent_ = YES;
110 [NSApp sendEvent:event];
111 redispatchingEvent_ = NO;
113 // If the event was not handled by [NSApp sendEvent:], the sendEvent:
114 // method below will be called, and because |redispatchingEvent_| is YES,
115 // |eventHandled_| will be set to NO.
116 return eventHandled_;
119 - (BOOL)preSendEvent:(NSEvent*)event {
120 if (redispatchingEvent_) {
121 // If we get here, then the event was not handled by NSApplication.
123 // Return YES to stop native -sendEvent handling.