Skip to content

Commit

Permalink
chrome: mac: cefclient: Support Find window commands (fixes #3462)
Browse files Browse the repository at this point in the history
  • Loading branch information
magreenblatt committed Apr 20, 2023
1 parent 7ade772 commit e006388
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 20 deletions.
2 changes: 2 additions & 0 deletions tests/cefclient/browser/client_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ bool IsAllowedAppMenuCommandId(int command_id) {

IDC_PRINT,
IDC_FIND,
IDC_FIND_NEXT,
IDC_FIND_PREVIOUS,

// "More tools" sub-menu and contents.
IDC_MORE_TOOLS_MENU,
Expand Down
186 changes: 171 additions & 15 deletions tests/cefclient/cefclient_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import <Cocoa/Cocoa.h>
#include "include/cef_app.h"
#import "include/cef_application_mac.h"
#include "include/cef_command_ids.h"
#import "include/wrapper/cef_library_loader.h"
#include "tests/cefclient/browser/main_context_impl.h"
#include "tests/cefclient/browser/resource.h"
Expand Down Expand Up @@ -240,15 +241,7 @@ - (void)orderFrontStandardAboutPanel:(id)sender {
}

- (void)testsItemSelected:(int)command_id {
// Retrieve the active RootWindow.
auto root_window =
client::MainContext::Get()->GetRootWindowManager()->GetActiveRootWindow();
if (!root_window) {
return;
}

CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
if (browser.get()) {
if (auto browser = [self getActiveBrowser]) {
client::test_runner::RunTest(browser, command_id);
}
}
Expand Down Expand Up @@ -321,16 +314,28 @@ - (IBAction)menuTestsOtherTests:(id)sender {
[self testsItemSelected:ID_TESTS_OTHER_TESTS];
}

- (void)enableAccessibility:(bool)bEnable {
// Retrieve the active RootWindow.
- (CefRefPtr<CefBrowser>)getActiveBrowser {
auto root_window =
client::MainContext::Get()->GetRootWindowManager()->GetActiveRootWindow();
if (!root_window) {
return;
if (root_window) {
return root_window->GetBrowser();
}

CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
if (browser.get()) {
return nullptr;
}

- (NSWindow*)getActiveBrowserNSWindow {
if (auto browser = [self getActiveBrowser]) {
if (auto view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(
browser->GetHost()->GetWindowHandle())) {
return [view window];
}
}
return nil;
}

- (void)enableAccessibility:(bool)bEnable {
if (auto browser = [self getActiveBrowser]) {
browser->GetHost()->SetAccessibilityState(bEnable ? STATE_ENABLED
: STATE_DISABLED);
}
Expand All @@ -341,6 +346,157 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:
return NSTerminateNow;
}

// Returns true if there is a modal window (either window- or application-
// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
// sheets) will not count as blocking the browser. But things like open/save
// dialogs that are window modal will block the browser.
- (BOOL)keyWindowIsModal {
if ([NSApp modalWindow]) {
return YES;
}

if (auto window = [self getActiveBrowserNSWindow]) {
return [[window attachedSheet] isKindOfClass:[NSWindow class]];
}

return NO;
}

// AppKit will call -[NSUserInterfaceValidations validateUserInterfaceItem:] to
// validate UI items. Any item whose target is FirstResponder, or nil, will
// traverse the responder chain looking for a responder that implements the
// item's selector. The top menu (configured in MainMenu.xib) can contain menu
// items with selectors that are implemented by Chromium's
// RenderWidgetHostViewCocoa or NativeWidgetMacNSWindow classes. These classes
// live in the Cocoa view hierarchy and will be triggered only if the browser
// window is focused. When the browser window is not focused these selectors
// will be forwarded (by Chromium's CommandDispatcher class) to `[NSApp
// delegate]` (this class). The particular selectors of interest here are
// |-commandDispatch:| and |-commandDispatchUsingKeyModifiers:| which will have
// a tag value from include/cef_command_ids.h. For example, 37000 is IDC_FIND
// and can be triggered via the "Find..." menu item or the Cmd+g keyboard
// shortcut:
//
// <menuItem title="Find..." tag="37000" keyEquivalent="g" id="209">
// <connections>
// <action selector="commandDispatch:" target="-1" id="241"/>
// </connections>
// </menuItem>
//
// If |-validateUserInterfaceItem:| returns YES then the menu item will be
// enabled and execution will trigger the associated selector.
//
// This implementation is based on Chromium's AppController class.
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
SEL action = [item action];
BOOL enable = NO;
// Whether opening a new browser window is allowed.
BOOL canOpenNewBrowser = YES;

// Commands from the menu bar are only handled by commandDispatch: if there is
// no key window.
if (action == @selector(commandDispatch:) ||
action == @selector(commandDispatchUsingKeyModifiers:)) {
switch ([item tag]) {
// Browser-level items that open in new tabs or perform an action in a
// current tab should not open if there's a window- or app-modal dialog.
case IDC_OPEN_FILE:
case IDC_NEW_TAB:
case IDC_FOCUS_LOCATION:
case IDC_FOCUS_SEARCH:
case IDC_SHOW_HISTORY:
case IDC_SHOW_BOOKMARK_MANAGER:
case IDC_CLEAR_BROWSING_DATA:
case IDC_SHOW_DOWNLOADS:
case IDC_IMPORT_SETTINGS:
case IDC_MANAGE_EXTENSIONS:
case IDC_HELP_PAGE_VIA_MENU:
case IDC_OPTIONS:
enable = canOpenNewBrowser && ![self keyWindowIsModal];
break;
// Browser-level items that open in new windows: allow the user to open
// a new window even if there's a window-modal dialog.
case IDC_NEW_WINDOW:
enable = canOpenNewBrowser;
break;
case IDC_TASK_MANAGER:
enable = YES;
break;
case IDC_NEW_INCOGNITO_WINDOW:
enable = canOpenNewBrowser;
break;
default:
enable = ![self keyWindowIsModal];
break;
}
} else if ([self respondsToSelector:action]) {
// All other selectors that this class implements.
enable = YES;
}

return enable;
}

// This will get called in the case where the frontmost window is not a browser
// window, and the user has command-clicked a button in a background browser
// window whose action is |-commandDispatch:|
- (void)commandDispatch:(id)sender {
// Handle the case where we're dispatching a command from a sender that's in a
// browser window. This means that the command came from a background window
// and is getting here because the foreground window is not a browser window.
DCHECK(sender);
if ([sender respondsToSelector:@selector(window)]) {
id delegate = [[sender window] windowController];
if ([delegate respondsToSelector:@selector(commandDispatch:)]) {
[delegate commandDispatch:sender];
return;
}
}

// Handle specific commands where we want to make the last active browser
// frontmost and then re-execute the command.
switch ([sender tag]) {
case IDC_FIND:
case IDC_FIND_NEXT:
case IDC_FIND_PREVIOUS:
if (id window = [self getActiveBrowserNSWindow]) {
[window makeKeyAndOrderFront:nil];
if ([window respondsToSelector:@selector(commandDispatch:)]) {
[window commandDispatch:sender];
return;
}
}
break;
default:
break;
}

LOG(INFO) << "Unhandled commandDispatch: for tag " << [sender tag];
}

// Same as |-commandDispatch:|, but executes commands using a disposition
// determined by the key flags. This will get called in the case where the
// frontmost window is not a browser window, and the user has command-clicked
// a button in a background browser window whose action is
// |-commandDispatchUsingKeyModifiers:|
- (void)commandDispatchUsingKeyModifiers:(id)sender {
// Handle the case where we're dispatching a command from a sender that's in a
// browser window. This means that the command came from a background window
// and is getting here because the foreground window is not a browser window.
DCHECK(sender);
if ([sender respondsToSelector:@selector(window)]) {
id delegate = [[sender window] windowController];
if ([delegate
respondsToSelector:@selector(commandDispatchUsingKeyModifiers:)]) {
[delegate commandDispatchUsingKeyModifiers:sender];
return;
}
}

LOG(INFO) << "Unhandled commandDispatchUsingKeyModifiers: for tag "
<< [sender tag];
}

@end

namespace client {
Expand Down
21 changes: 16 additions & 5 deletions tests/cefclient/resources/mac/English.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,27 @@
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<menuItem title="Find…" tag="37000" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
<action selector="commandDispatch:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<menuItem title="Find Next" tag="37001" keyEquivalent="g" id="208">
<connections>
<action selector="commandDispatch:" target="-1" id="242"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="37002" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="commandDispatch:" target="-1" id="243"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" keyEquivalent="e" id="221">
<connections>
<action selector="copyToFindPboard:" target="-1" id="244"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
Expand Down

0 comments on commit e006388

Please sign in to comment.