// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. #include "include/base/cef_bind.h" #include "include/views/cef_button.h" #include "include/views/cef_button_delegate.h" #include "include/views/cef_label_button.h" #include "include/views/cef_menu_button.h" #include "include/views/cef_menu_button_delegate.h" #include "include/wrapper/cef_closure_task.h" #include "tests/ceftests/image_util.h" #include "tests/ceftests/test_handler.h" #include "tests/ceftests/thread_helper.h" #include "tests/ceftests/views/test_window_delegate.h" #include "tests/gtest/include/gtest/gtest.h" #define BUTTON_TEST(name) UI_THREAD_TEST(ViewsButtonTest, name) #define BUTTON_TEST_ASYNC(name) UI_THREAD_TEST_ASYNC(ViewsButtonTest, name) namespace { CefRefPtr CreateIconImage() { CefRefPtr image = CefImage::CreateImage(); image_util::LoadIconImage(image, 1.0); image_util::LoadIconImage(image, 2.0); return image; } const char kButtonText[] = "My Button"; void VerifyButtonStyle(CefRefPtr button) { // Test state. EXPECT_EQ(CEF_BUTTON_STATE_NORMAL, button->GetState()); button->SetState(CEF_BUTTON_STATE_HOVERED); EXPECT_EQ(CEF_BUTTON_STATE_HOVERED, button->GetState()); button->SetState(CEF_BUTTON_STATE_PRESSED); EXPECT_EQ(CEF_BUTTON_STATE_PRESSED, button->GetState()); button->SetState(CEF_BUTTON_STATE_DISABLED); EXPECT_EQ(CEF_BUTTON_STATE_DISABLED, button->GetState()); button->SetState(CEF_BUTTON_STATE_NORMAL); button->SetTooltipText("Some tooltip text"); button->SetAccessibleName("MyButton"); } void VerifyLabelButtonImage(CefRefPtr button, cef_button_state_t state, CefRefPtr image) { EXPECT_FALSE(button->GetImage(state).get()) << "state = " << state; button->SetImage(state, image); EXPECT_TRUE(image->IsSame(button->GetImage(state))) << "state = " << state; button->SetImage(state, nullptr); EXPECT_FALSE(button->GetImage(state).get()) << "state = " << state; } void VerifyLabelButtonStyle(CefRefPtr button) { VerifyButtonStyle(button); // Test set/get text. EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str()); const char kText[] = "My text"; button->SetText(kText); EXPECT_STREQ(kText, button->GetText().ToString().c_str()); // Test images. CefRefPtr image = CreateIconImage(); VerifyLabelButtonImage(button, CEF_BUTTON_STATE_NORMAL, image); VerifyLabelButtonImage(button, CEF_BUTTON_STATE_HOVERED, image); VerifyLabelButtonImage(button, CEF_BUTTON_STATE_PRESSED, image); VerifyLabelButtonImage(button, CEF_BUTTON_STATE_DISABLED, image); // Test colors. const cef_color_t color = CefColorSetARGB(255, 255, 0, 255); button->SetTextColor(CEF_BUTTON_STATE_NORMAL, color); button->SetTextColor(CEF_BUTTON_STATE_HOVERED, color); button->SetTextColor(CEF_BUTTON_STATE_PRESSED, color); button->SetTextColor(CEF_BUTTON_STATE_DISABLED, color); button->SetEnabledTextColors(color); // Test alignment. button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_LEFT); button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_CENTER); button->SetHorizontalAlignment(CEF_HORIZONTAL_ALIGNMENT_RIGHT); // Test fonts. button->SetFontList("Arial, 14px"); // Test sizes. button->SetMinimumSize(CefSize(100, 100)); button->SetMaximumSize(CefSize(100, 100)); } void VerifyMenuButtonStyle(CefRefPtr button) { VerifyLabelButtonStyle(button); } class EmptyMenuButtonDelegate : public CefMenuButtonDelegate { public: EmptyMenuButtonDelegate() {} void OnMenuButtonPressed( CefRefPtr menu_button, const CefPoint& screen_point, CefRefPtr button_pressed_lock) override { EXPECT_TRUE(false); // Not reached. } void OnButtonPressed(CefRefPtr button) override { EXPECT_TRUE(false); // Not reached. } private: IMPLEMENT_REFCOUNTING(EmptyMenuButtonDelegate); DISALLOW_COPY_AND_ASSIGN(EmptyMenuButtonDelegate); }; void LabelButtonStyle() { CefRefPtr button = CefLabelButton::CreateLabelButton( new EmptyMenuButtonDelegate(), kButtonText); VerifyLabelButtonStyle(button); } void LabelButtonStyleFramelessImpl() { LabelButtonStyle(); } void MenuButtonStyle() { CefRefPtr button = CefMenuButton::CreateMenuButton( new EmptyMenuButtonDelegate(), kButtonText); VerifyMenuButtonStyle(button); } void MenuButtonStyleFramelessImpl() { MenuButtonStyle(); } } // namespace // Test Button getters/setters. BUTTON_TEST(LabelButtonStyleFrameless) BUTTON_TEST(MenuButtonStyleFrameless) namespace { // Mouse click delay in MS. const int kClickDelayMS = 100; const int kButtonID = 1; class TestButtonDelegate : public CefButtonDelegate { public: TestButtonDelegate() {} void OnButtonPressed(CefRefPtr button) override { EXPECT_TRUE(button.get()); EXPECT_EQ(button->GetID(), kButtonID); // Complete the test by closing the window. button->GetWindow()->Close(); } private: IMPLEMENT_REFCOUNTING(TestButtonDelegate); DISALLOW_COPY_AND_ASSIGN(TestButtonDelegate); }; void ClickButton(CefRefPtr window, int button_id) { CefRefPtr button = window->GetViewForID(button_id); EXPECT_TRUE(button->AsButton()); // Determine the middle of the button in screen coordinates. const CefRect& bounds = button->GetBoundsInScreen(); const CefPoint& click_point = CefPoint(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); // Click the button. window->SendMouseMove(click_point.x, click_point.y); window->SendMouseEvents(MBT_LEFT, true, true); } void AddImage(CefRefPtr button) { CefRefPtr image = CreateIconImage(); button->SetImage(CEF_BUTTON_STATE_NORMAL, image); } void RunLabelButtonClick(bool with_text, bool with_image, CefRefPtr window) { CefRefPtr button = CefLabelButton::CreateLabelButton( new TestButtonDelegate(), with_text ? kButtonText : ""); button->SetID(kButtonID); EXPECT_TRUE(button->AsButton()); EXPECT_TRUE(button->AsButton()->AsLabelButton()); EXPECT_EQ(kButtonID, button->GetID()); EXPECT_TRUE(button->IsVisible()); EXPECT_FALSE(button->IsDrawn()); if (with_text) EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str()); else EXPECT_TRUE(button->GetText().empty()); if (with_image) AddImage(button); window->AddChildView(button); window->Layout(); EXPECT_TRUE(window->IsSame(button->GetWindow())); EXPECT_TRUE(window->IsSame(button->GetParentView())); EXPECT_TRUE(button->IsSame(window->GetViewForID(kButtonID))); EXPECT_TRUE(button->IsVisible()); EXPECT_TRUE(button->IsDrawn()); window->Show(); // Wait a bit before trying to click the button. CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID), kClickDelayMS); } void LabelButtonClick(CefRefPtr event, bool with_button_frame, bool with_button_text, bool with_button_image) { TestWindowDelegate::Config config; config.on_window_created = base::Bind(RunLabelButtonClick, with_button_text, with_button_image); config.frameless = false; config.close_window = false; TestWindowDelegate::RunTest(event, config); } void LabelButtonClickFramedWithTextWithImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, true, true, true); } void LabelButtonClickFramedWithTextNoImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, true, true, false); } void LabelButtonClickFramedNoTextWithImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, true, false, true); } void LabelButtonClickFramedNoTextNoImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, true, false, false); } void LabelButtonClickFramelessWithTextWithImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, false, true, true); } void LabelButtonClickFramelessWithTextNoImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, false, true, false); } void LabelButtonClickFramelessNoTextWithImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, false, false, true); } void LabelButtonClickFramelessNoTextNoImageFramelessWindowImpl( CefRefPtr event) { LabelButtonClick(event, false, false, false); } } // namespace // Test LabelButton functionality. This is primarily to exercise exposed CEF // APIs and is not intended to comprehensively test button-related behavior // (which we presume that Chromium is testing). BUTTON_TEST_ASYNC(LabelButtonClickFramedWithTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramedWithTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramedNoTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramedNoTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramelessWithTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramelessWithTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramelessNoTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(LabelButtonClickFramelessNoTextNoImageFramelessWindow) namespace { const int kMenuItemID = 2; const char kMenuItemLabel[] = "My Menu Item"; void ClickMenuItem(CefRefPtr menu_button) { // Determine the lower-right corner of the menu button, then offset a bit to // hit the first menu item. const CefRect& bounds = menu_button->GetBoundsInScreen(); const CefPoint& click_point = CefPoint(bounds.x + bounds.width + 10, bounds.y + bounds.height + 10); // Click the menu item. CefRefPtr window = menu_button->GetWindow(); window->SendMouseMove(click_point.x, click_point.y); window->SendMouseEvents(MBT_LEFT, true, true); } class TestMenuButtonDelegate : public CefMenuButtonDelegate, public CefMenuModelDelegate { public: TestMenuButtonDelegate() {} void OnMenuButtonPressed( CefRefPtr menu_button, const CefPoint& screen_point, CefRefPtr button_pressed_lock) override { window_ = menu_button->GetWindow(); CefRefPtr model = CefMenuModel::CreateMenuModel(this); model->AddItem(kMenuItemID, kMenuItemLabel); // Verify color accessors. for (int i = 0; i < CEF_MENU_COLOR_COUNT; ++i) { cef_menu_color_type_t color_type = static_cast(i); cef_color_t color_out; cef_color_t color = CefColorSetARGB(255, 255, 255, i); // No color set yet. color_out = 1; EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out)); EXPECT_EQ(0U, color_out); color_out = 1; EXPECT_TRUE(model->GetColorAt(0, color_type, color_out)); EXPECT_EQ(0U, color_out); color_out = 1; EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out)); EXPECT_EQ(0U, color_out); // Set the default color. EXPECT_TRUE(model->SetColorAt(-1, color_type, color)); color_out = 1; EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out)); EXPECT_EQ(color, color_out); // Clear the default color. EXPECT_TRUE(model->SetColorAt(-1, color_type, 0)); color_out = 1; EXPECT_TRUE(model->GetColorAt(-1, color_type, color_out)); EXPECT_EQ(0U, color_out); // Set the index color. EXPECT_TRUE(model->SetColorAt(0, color_type, color)); color_out = 1; EXPECT_TRUE(model->GetColorAt(0, color_type, color_out)); EXPECT_EQ(color, color_out); // Clear the index color. EXPECT_TRUE(model->SetColorAt(0, color_type, 0)); color_out = 1; EXPECT_TRUE(model->GetColorAt(0, color_type, color_out)); EXPECT_EQ(0U, color_out); // Set the ID color. EXPECT_TRUE(model->SetColor(kMenuItemID, color_type, color)); color_out = 1; EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out)); EXPECT_EQ(color, color_out); // Clear the ID color. EXPECT_TRUE(model->SetColor(kMenuItemID, color_type, 0)); color_out = 1; EXPECT_TRUE(model->GetColor(kMenuItemID, color_type, color_out)); EXPECT_EQ(0U, color_out); // Index/ID doesn't exist. EXPECT_FALSE(model->SetColorAt(4, color_type, color)); EXPECT_FALSE(model->SetColor(4, color_type, color)); color_out = 1; EXPECT_FALSE(model->GetColorAt(4, color_type, color_out)); EXPECT_FALSE(model->GetColor(4, color_type, color_out)); EXPECT_EQ(1U, color_out); } // Verify font accessors. const std::string& font = "Tahoma, 12px"; EXPECT_TRUE(model->SetFontListAt(0, font)); EXPECT_TRUE(model->SetFontListAt(0, CefString())); EXPECT_TRUE(model->SetFontList(kMenuItemID, font)); EXPECT_TRUE(model->SetFontList(kMenuItemID, CefString())); // Index/ID doesn't exist. EXPECT_FALSE(model->SetFontListAt(4, font)); EXPECT_FALSE(model->SetFontList(4, font)); // Wait a bit before trying to click the menu item. CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button), kClickDelayMS); menu_button->ShowMenu(model, screen_point, CEF_MENU_ANCHOR_TOPLEFT); } void OnButtonPressed(CefRefPtr button) override {} void ExecuteCommand(CefRefPtr menu_model, int command_id, cef_event_flags_t event_flags) override { EXPECT_TRUE(menu_model.get()); EXPECT_EQ(command_id, kMenuItemID); // Complete the test by closing the window. window_->GetWindow()->Close(); window_ = nullptr; } private: CefRefPtr window_; IMPLEMENT_REFCOUNTING(TestMenuButtonDelegate); DISALLOW_COPY_AND_ASSIGN(TestMenuButtonDelegate); }; void RunMenuButtonClick(bool with_text, bool with_image, CefRefPtr window) { CefRefPtr button = CefMenuButton::CreateMenuButton( new TestMenuButtonDelegate(), with_text ? kButtonText : ""); button->SetID(kButtonID); EXPECT_TRUE(button->AsButton()); EXPECT_TRUE(button->AsButton()->AsLabelButton()); EXPECT_TRUE(button->AsButton()->AsLabelButton()->AsMenuButton()); EXPECT_EQ(kButtonID, button->GetID()); EXPECT_TRUE(button->IsVisible()); EXPECT_FALSE(button->IsDrawn()); if (with_text) EXPECT_STREQ(kButtonText, button->GetText().ToString().c_str()); else EXPECT_TRUE(button->GetText().empty()); if (with_image) AddImage(button); window->AddChildView(button); window->Layout(); EXPECT_TRUE(window->IsSame(button->GetWindow())); EXPECT_TRUE(window->IsSame(button->GetParentView())); EXPECT_TRUE(button->IsSame(window->GetViewForID(kButtonID))); EXPECT_TRUE(button->IsVisible()); EXPECT_TRUE(button->IsDrawn()); window->Show(); // Wait a bit before trying to click the button. CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID), kClickDelayMS); } void MenuButtonClick(CefRefPtr event, bool with_button_frame, bool with_button_text, bool with_button_image) { TestWindowDelegate::Config config; config.on_window_created = base::Bind(RunMenuButtonClick, with_button_text, with_button_image); config.frameless = false; config.close_window = false; TestWindowDelegate::RunTest(event, config); } void MenuButtonClickFramedWithTextWithImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, true, true, true); } void MenuButtonClickFramedWithTextNoImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, true, true, false); } void MenuButtonClickFramedNoTextWithImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, true, false, true); } void MenuButtonClickFramedNoTextNoImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, true, false, false); } void MenuButtonClickFramelessWithTextWithImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, false, true, true); } void MenuButtonClickFramelessWithTextNoImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, false, true, false); } void MenuButtonClickFramelessNoTextWithImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, false, false, true); } void MenuButtonClickFramelessNoTextNoImageFramelessWindowImpl( CefRefPtr event) { MenuButtonClick(event, false, false, false); } } // namespace // Test MenuButton functionality. This is primarily to exercise exposed CEF // APIs and is not intended to comprehensively test button-related behavior // (which we presume that Chromium is testing). BUTTON_TEST_ASYNC(MenuButtonClickFramedWithTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramedWithTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramedNoTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramedNoTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramelessWithTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramelessWithTextNoImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextWithImageFramelessWindow) BUTTON_TEST_ASYNC(MenuButtonClickFramelessNoTextNoImageFramelessWindow) namespace { class TestMenuButtonCustomPopupDelegate : public CefMenuButtonDelegate, public CefWindowDelegate { public: explicit TestMenuButtonCustomPopupDelegate(bool can_activate) : can_activate_(can_activate) {} void OnMenuButtonPressed( CefRefPtr menu_button, const CefPoint& screen_point, CefRefPtr button_pressed_lock) override { parent_window_ = menu_button->GetWindow(); button_pressed_lock_ = button_pressed_lock; popup_window_ = CefWindow::CreateTopLevelWindow(this); popup_window_->SetBounds(CefRect(screen_point.x, screen_point.y, 100, 100)); CefRefPtr button = CefLabelButton::CreateLabelButton(this, "Button"); button->SetFocusable(can_activate_); popup_window_->AddChildView(button); popup_window_->Show(); // Wait a bit before trying to click the popup button. CefPostDelayedTask(TID_UI, base::Bind(ClickMenuItem, menu_button), kClickDelayMS); } void OnButtonPressed(CefRefPtr button) override { EXPECT_TRUE(button->GetWindow()->IsSame(popup_window_)); popup_window_->Close(); popup_window_ = nullptr; button_pressed_lock_ = nullptr; } CefRefPtr GetParentWindow(CefRefPtr window, bool* is_menu, bool* can_activate_menu) override { EXPECT_TRUE(parent_window_); *is_menu = true; *can_activate_menu = can_activate_; return parent_window_; } bool IsFrameless(CefRefPtr window) override { return true; } void OnFocus(CefRefPtr view) override { if (popup_window_ && view->GetWindow()->IsSame(popup_window_)) { EXPECT_TRUE(can_activate_); got_focus_.yes(); } } void OnWindowDestroyed(CefRefPtr window) override { if (can_activate_) EXPECT_TRUE(got_focus_); else EXPECT_FALSE(got_focus_); // Complete the test by closing the parent window. parent_window_->Close(); parent_window_ = nullptr; } private: const bool can_activate_; CefRefPtr parent_window_; CefRefPtr popup_window_; CefRefPtr button_pressed_lock_; TrackCallback got_focus_; IMPLEMENT_REFCOUNTING(TestMenuButtonCustomPopupDelegate); DISALLOW_COPY_AND_ASSIGN(TestMenuButtonCustomPopupDelegate); }; void RunMenuButtonCustomPopupClick(bool can_activate, CefRefPtr window) { CefRefPtr button = CefMenuButton::CreateMenuButton( new TestMenuButtonCustomPopupDelegate(can_activate), "Custom"); button->SetID(kButtonID); window->AddChildView(button); window->Layout(); window->Show(); // Wait a bit before trying to click the button. CefPostDelayedTask(TID_UI, base::Bind(ClickButton, window, kButtonID), kClickDelayMS); } void MenuButtonCustomPopupClick(CefRefPtr event, bool can_activate) { TestWindowDelegate::Config config; config.on_window_created = base::Bind(RunMenuButtonCustomPopupClick, can_activate); config.close_window = false; TestWindowDelegate::RunTest(event, config); } void MenuButtonCustomPopupActivateImpl(CefRefPtr event) { MenuButtonCustomPopupClick(event, true); } void MenuButtonCustomPopupNoActivateImpl(CefRefPtr event) { MenuButtonCustomPopupClick(event, false); } } // namespace BUTTON_TEST_ASYNC(MenuButtonCustomPopupActivate) BUTTON_TEST_ASYNC(MenuButtonCustomPopupNoActivate)