Commit a80419dc authored by mitchelljones's avatar mitchelljones Committed by Commit bot

Added 'Find'/'Paste and Match Style' menu items for hosted apps on Mac.

This feature should exist for hosted apps, currently there is no option
to search a page. Instead of removing these two menu items, they are now
hidden if the app that is currently focussed is not a hosted app.

BUG=450834

Review URL: https://codereview.chromium.org/864153003

Cr-Commit-Position: refs/heads/master@{#314272}
parent f2bf2b08
...@@ -75,6 +75,44 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item, ...@@ -75,6 +75,44 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item,
[submenu removeItem:nextItem]; [submenu removeItem:nextItem];
} }
// Sets the menu item with |item_tag| in |top_level_item| visible.
// If |has_alternate| is true, the item immediately following |item_tag| is
// assumed to be its (only) alternate. Since AppKit is unable to hide items
// with alternates, |has_alternate| will cause -[NSMenuItem alternate] to be
// removed when hiding and restored when showing.
void SetItemWithTagVisible(NSMenuItem* top_level_item,
NSInteger item_tag,
bool visible,
bool has_alternate) {
NSMenu* submenu = [top_level_item submenu];
NSMenuItem* menu_item = [submenu itemWithTag:item_tag];
DCHECK(menu_item);
if (visible != [menu_item isHidden])
return;
if (!has_alternate) {
[menu_item setHidden:!visible];
return;
}
NSInteger next_index = [submenu indexOfItem:menu_item] + 1;
DCHECK_LT(next_index, [submenu numberOfItems]);
NSMenuItem* alternate_item = [submenu itemAtIndex:next_index];
if (!visible) {
// When hiding (only), we can verify the assumption that the item following
// |item_tag| is actually an alternate.
DCHECK([alternate_item isAlternate]);
}
// The alternate item visibility should always be in sync.
DCHECK_EQ([alternate_item isHidden], [menu_item isHidden]);
[alternate_item setAlternate:visible];
[alternate_item setHidden:!visible];
[menu_item setHidden:!visible];
}
} // namespace } // namespace
// Used by AppShimMenuController to manage menu items that are a copy of a // Used by AppShimMenuController to manage menu items that are a copy of a
...@@ -270,14 +308,10 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item, ...@@ -270,14 +308,10 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item,
[closeWindowMenuItem setKeyEquivalent:@"w"]; [closeWindowMenuItem setKeyEquivalent:@"w"];
[closeWindowMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; [closeWindowMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
// Edit menu. This copies the menu entirely and removes // Edit menu. We copy the menu because the last two items, "Start Dictation"
// "Paste and Match Style" and "Find". This is because the last two items, // and "Special Characters" are added by OSX, so we can't copy them
// "Start Dictation" and "Special Characters" are added by OSX, so we can't // explicitly.
// copy them explicitly.
editMenuItem_.reset([[[NSApp mainMenu] itemWithTag:IDC_EDIT_MENU] copy]); editMenuItem_.reset([[[NSApp mainMenu] itemWithTag:IDC_EDIT_MENU] copy]);
RemoveMenuItemWithTag(editMenuItem_,
IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE, NO);
RemoveMenuItemWithTag(editMenuItem_, IDC_FIND_MENU, NO);
// View menu. Remove "Always Show Bookmark Bar" and separator. // View menu. Remove "Always Show Bookmark Bar" and separator.
viewMenuItem_.reset([[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] copy]); viewMenuItem_.reset([[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] copy]);
...@@ -379,7 +413,14 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item, ...@@ -379,7 +413,14 @@ void RemoveMenuItemWithTag(NSMenuItem* top_level_item,
[mainMenu addItem:appMenuItem_]; [mainMenu addItem:appMenuItem_];
[mainMenu addItem:fileMenuItem_]; [mainMenu addItem:fileMenuItem_];
SetItemWithTagVisible(editMenuItem_,
IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
app->is_hosted_app(), true);
SetItemWithTagVisible(editMenuItem_, IDC_FIND_MENU, app->is_hosted_app(),
false);
[mainMenu addItem:editMenuItem_]; [mainMenu addItem:editMenuItem_];
if (app->is_hosted_app()) { if (app->is_hosted_app()) {
[mainMenu addItem:viewMenuItem_]; [mainMenu addItem:viewMenuItem_];
[mainMenu addItem:historyMenuItem_]; [mainMenu addItem:historyMenuItem_];
......
...@@ -9,8 +9,11 @@ ...@@ -9,8 +9,11 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/apps/app_browsertest_util.h" #include "chrome/browser/apps/app_browsertest_util.h"
#include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
...@@ -26,23 +29,43 @@ namespace { ...@@ -26,23 +29,43 @@ namespace {
class AppShimMenuControllerBrowserTest class AppShimMenuControllerBrowserTest
: public extensions::PlatformAppBrowserTest { : public extensions::PlatformAppBrowserTest {
protected: protected:
// The apps that can be installed and launched by SetUpApps().
enum AvailableApps { PACKAGED_1 = 0x1, PACKAGED_2 = 0x2, HOSTED = 0x4 };
AppShimMenuControllerBrowserTest() AppShimMenuControllerBrowserTest()
: app_1_(NULL), : app_1_(nullptr),
app_2_(NULL), app_2_(nullptr),
hosted_app_(nullptr),
initial_menu_item_count_(0) {} initial_menu_item_count_(0) {}
void SetUpCommandLine(base::CommandLine* command_line) override { void SetUpCommandLine(base::CommandLine* command_line) override {
PlatformAppBrowserTest::SetUpCommandLine(command_line); PlatformAppBrowserTest::SetUpCommandLine(command_line);
} }
// Start two apps and wait for them to be launched. // Start testing apps and wait for them to launch. |flags| is a bitmask of
void SetUpApps() { // AvailableApps.
ExtensionTestMessageListener listener_1("Launched", false); void SetUpApps(int flags) {
app_1_ = InstallAndLaunchPlatformApp("minimal_id");
ASSERT_TRUE(listener_1.WaitUntilSatisfied()); if (flags & PACKAGED_1) {
ExtensionTestMessageListener listener_2("Launched", false); ExtensionTestMessageListener listener_1("Launched", false);
app_2_ = InstallAndLaunchPlatformApp("minimal"); app_1_ = InstallAndLaunchPlatformApp("minimal_id");
ASSERT_TRUE(listener_2.WaitUntilSatisfied()); ASSERT_TRUE(listener_1.WaitUntilSatisfied());
}
if (flags & PACKAGED_2) {
ExtensionTestMessageListener listener_2("Launched", false);
app_2_ = InstallAndLaunchPlatformApp("minimal");
ASSERT_TRUE(listener_2.WaitUntilSatisfied());
}
if (flags & HOSTED) {
hosted_app_ = InstallHostedApp();
// Explicitly set the launch type to open in a new window.
extensions::SetLaunchType(profile(), hosted_app_->id(),
extensions::LAUNCH_TYPE_WINDOW);
LaunchHostedApp(hosted_app_);
}
initial_menu_item_count_ = [[[NSApp mainMenu] itemArray] count]; initial_menu_item_count_ = [[[NSApp mainMenu] itemArray] count];
} }
...@@ -74,8 +97,35 @@ class AppShimMenuControllerBrowserTest ...@@ -74,8 +97,35 @@ class AppShimMenuControllerBrowserTest
EXPECT_FALSE([[item_array objectAtIndex:i] isHidden]); EXPECT_FALSE([[item_array objectAtIndex:i] isHidden]);
} }
void CheckEditMenu(const extensions::Extension* app) const {
const int edit_menu_index = initial_menu_item_count_ + 2;
NSMenuItem* edit_menu =
[[[NSApp mainMenu] itemArray] objectAtIndex:edit_menu_index];
NSMenu* edit_submenu = [edit_menu submenu];
NSMenuItem* paste_match_style_menu_item =
[edit_submenu itemWithTag:IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE];
NSMenuItem* find_menu_item = [edit_submenu itemWithTag:IDC_FIND_MENU];
if (app->is_hosted_app()) {
EXPECT_FALSE([paste_match_style_menu_item isHidden]);
EXPECT_FALSE([find_menu_item isHidden]);
} else {
EXPECT_TRUE([paste_match_style_menu_item isHidden]);
EXPECT_TRUE([find_menu_item isHidden]);
}
}
extensions::AppWindow* FirstWindowForApp(const extensions::Extension* app) {
extensions::AppWindowRegistry::AppWindowList window_list =
extensions::AppWindowRegistry::Get(profile())
->GetAppWindowsForApp(app->id());
EXPECT_FALSE(window_list.empty());
return window_list.front();
}
const extensions::Extension* app_1_; const extensions::Extension* app_1_;
const extensions::Extension* app_2_; const extensions::Extension* app_2_;
const extensions::Extension* hosted_app_;
NSUInteger initial_menu_item_count_; NSUInteger initial_menu_item_count_;
private: private:
...@@ -85,21 +135,17 @@ class AppShimMenuControllerBrowserTest ...@@ -85,21 +135,17 @@ class AppShimMenuControllerBrowserTest
// Test that focusing an app window changes the menu bar. // Test that focusing an app window changes the menu bar.
IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest, IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
PlatformAppFocusUpdatesMenuBar) { PlatformAppFocusUpdatesMenuBar) {
SetUpApps(); SetUpApps(PACKAGED_1 | PACKAGED_2);
// When an app is focused, all Chrome menu items should be hidden, and a menu // When an app is focused, all Chrome menu items should be hidden, and a menu
// item for the app should be added. // item for the app should be added.
extensions::AppWindow* app_1_app_window = extensions::AppWindow* app_1_app_window = FirstWindowForApp(app_1_);
extensions::AppWindowRegistry::Get(profile())
->GetAppWindowsForApp(app_1_->id()).front();
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
postNotificationName:NSWindowDidBecomeMainNotification postNotificationName:NSWindowDidBecomeMainNotification
object:app_1_app_window->GetNativeWindow()]; object:app_1_app_window->GetNativeWindow()];
CheckHasAppMenus(app_1_); CheckHasAppMenus(app_1_);
// When another app is focused, the menu item for the app should change. // When another app is focused, the menu item for the app should change.
extensions::AppWindow* app_2_app_window = extensions::AppWindow* app_2_app_window = FirstWindowForApp(app_2_);
extensions::AppWindowRegistry::Get(profile())
->GetAppWindowsForApp(app_2_->id()).front();
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
postNotificationName:NSWindowDidBecomeMainNotification postNotificationName:NSWindowDidBecomeMainNotification
object:app_2_app_window->GetNativeWindow()]; object:app_2_app_window->GetNativeWindow()];
...@@ -123,27 +169,53 @@ IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest, ...@@ -123,27 +169,53 @@ IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
CheckNoAppMenus(); CheckNoAppMenus();
} }
// Test to check that hosted apps have "Find" and "Paste and Match Style" menu
// items under the "Edit" menu.
IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
HostedAppHasAdditionalEditMenuItems) {
SetUpApps(HOSTED | PACKAGED_1);
// Find the first hosted app window.
Browser* hosted_app_browser = nullptr;
BrowserList* browsers =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE);
for (Browser* browser : *browsers) {
const extensions::Extension* extension =
apps::ExtensionAppShimHandler::GetAppForBrowser(browser);
if (extension && extension->is_hosted_app()) {
hosted_app_browser = browser;
break;
}
}
EXPECT_TRUE(hosted_app_browser);
// Focus the hosted app.
[[NSNotificationCenter defaultCenter]
postNotificationName:NSWindowDidBecomeMainNotification
object:hosted_app_browser->window()->GetNativeWindow()];
CheckEditMenu(hosted_app_);
// Now focus a platform app, the Edit menu should not have the additional
// options.
[[NSNotificationCenter defaultCenter]
postNotificationName:NSWindowDidBecomeMainNotification
object:FirstWindowForApp(app_1_)->GetNativeWindow()];
CheckEditMenu(app_1_);
}
IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest, IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
ExtensionUninstallUpdatesMenuBar) { ExtensionUninstallUpdatesMenuBar) {
SetUpApps(); SetUpApps(PACKAGED_1 | PACKAGED_2);
// This essentially tests that a NSWindowWillCloseNotification gets fired when // This essentially tests that a NSWindowWillCloseNotification gets fired when
// an app is uninstalled. We need to close the other windows first since the // an app is uninstalled. We need to close the other windows first since the
// menu only changes on a NSWindowWillCloseNotification if there are no other // menu only changes on a NSWindowWillCloseNotification if there are no other
// windows. // windows.
extensions::AppWindow* app_2_app_window = FirstWindowForApp(app_2_)->GetBaseWindow()->Close();
extensions::AppWindowRegistry::Get(profile())
->GetAppWindowsForApp(app_2_->id()).front();
app_2_app_window->GetBaseWindow()->Close();
chrome::BrowserIterator()->window()->Close(); chrome::BrowserIterator()->window()->Close();
extensions::AppWindow* app_1_app_window =
extensions::AppWindowRegistry::Get(profile())
->GetAppWindowsForApp(app_1_->id()).front();
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
postNotificationName:NSWindowDidBecomeMainNotification postNotificationName:NSWindowDidBecomeMainNotification
object:app_1_app_window->GetNativeWindow()]; object:FirstWindowForApp(app_1_)->GetNativeWindow()];
CheckHasAppMenus(app_1_); CheckHasAppMenus(app_1_);
ExtensionService::UninstallExtensionHelper( ExtensionService::UninstallExtensionHelper(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment