// Copyright (c) 2012 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_callback.h" #include "include/base/cef_logging.h" #include "include/cef_parser.h" #include "include/cef_v8.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "tests/ceftests/routing_test_handler.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" #include "tests/shared/browser/geometry_util.h" #include "tests/shared/browser/resource_util.h" #if defined(OS_MAC) #include // For character codes. #include "tests/ceftests/os_rendering_unittest_mac.h" #elif defined(OS_LINUX) #include #elif defined(OS_WIN) // Required for resource_util_win, which uses this as an extern HINSTANCE hInst = ::GetModuleHandle(nullptr); #endif // Set to 1 to enable verbose debugging info logging. #define VERBOSE_DEBUGGING 0 namespace { const char kTestUrl[] = "https://tests/osrtest"; // Must be both large enough for the drag/drop region to be visible and small // enough for a little vertical offset with scrollbar. const int kOsrWidth = 600; const int kOsrHeight = 450; // bounding client rects for edit box and navigate button #if defined(OS_WIN) const CefRect kExpandedSelectRect(462, 42, 81, 334); #elif defined(OS_MAC) const CefRect kExpandedSelectRect(462, 42, 75, 334); #elif defined(OS_LINUX) const CefRect kExpandedSelectRect(462, 42, 79, 334); #else #error "Unsupported platform" #endif // defined(OS_WIN) // word to be written into edit box const char kKeyTestWord[] = "done"; constexpr uint32_t kAllTouchHandleFlags = (CEF_THS_FLAG_ENABLED | CEF_THS_FLAG_ORIENTATION | CEF_THS_FLAG_ORIGIN | CEF_THS_FLAG_ALPHA); #if defined(OS_LINUX) // From ui/events/keycodes/keyboard_codes_posix.h #define VKEY_D 0x44 #define VKEY_O 0x4F #define VKEY_N 0x4E #define VKEY_E 0x45 #define VKEY_ESCAPE 0x1B #define VKEY_TAB 0x09 const unsigned int kNativeKeyTestCodes[] = {XK_d, XK_o, XK_n, XK_e}; const unsigned int kNativeKeyEscape = XK_Escape; const unsigned int kNativeKeyTab = XK_Tab; #elif defined(OS_MAC) // See kKeyCodesMap in ui/events/keycodes/keyboard_code_conversion_mac.mm #define VKEY_D 'd' #define VKEY_O 'o' #define VKEY_N 'n' #define VKEY_E 'e' #define VKEY_ESCAPE kEscapeCharCode #define VKEY_TAB kTabCharCode const unsigned int kNativeKeyTestCodes[] = {kVK_ANSI_D, kVK_ANSI_O, kVK_ANSI_N, kVK_ANSI_E}; const unsigned int kNativeKeyEscape = kVK_Escape; const unsigned int kNativeKeyTab = kVK_Tab; #endif #if defined(OS_MAC) || defined(OS_LINUX) const unsigned int kKeyTestCodes[] = {VKEY_D, VKEY_O, VKEY_N, VKEY_E}; #endif // test type enum OSRTestType { // IsWindowRenderingDisabled should be true OSR_TEST_IS_WINDOWLESS, // focusing webview, LI00 will get red & repainted OSR_TEST_FOCUS, // tab key traversal should iterate the focus across HTML element and // subsequently after last element CefFocusHandler::OnTakeFocus should be // called to allow giving focus to the next component. OSR_TEST_TAKE_FOCUS, // send focus event should set focus on the webview OSR_TEST_GOT_FOCUS, // loading webview should trigger a full paint (L01) OSR_TEST_PAINT, // same as OSR_TEST_PAINT but with alpha values OSR_TEST_TRANSPARENCY, // moving mouse over L02, OnCursorChange will be called OSR_TEST_CURSOR, // moving mouse on L03, OnPaint will be called for its bounding rectangle OSR_TEST_MOUSE_MOVE, // right clicking an element (L04), OnBeforeContextMenu should be called OSR_TEST_CLICK_RIGHT, // right clicking an element (L04), context menu will query screen point OSR_TEST_SCREEN_POINT, // left click in text box should query repainting edit box area OSR_TEST_CLICK_LEFT, // Resize should trigger a full repaint with the new given size OSR_TEST_RESIZE, // Invalidate should trigger repaint synchronously OSR_TEST_INVALIDATE, // write into editbox LI08, click to navigate on LI09 OSR_TEST_KEY_EVENTS, // mouse over LI10 will show a tooltip OSR_TEST_TOOLTIP, // mouse wheel will trigger a scroll event OSR_TEST_SCROLLING, // Right click will trigger a context menu, and on destroying the test, it // should not crash OSR_TEST_CONTEXT_MENU, // In certain scenarios, the quick menu should be shown instead of the context // menu OSR_TEST_QUICK_MENU, // clicking on dropdown box, PET_POPUP OnPaint is triggered OSR_TEST_POPUP_PAINT, // clicking on dropdown box, a popup will show up OSR_TEST_POPUP_SHOW, // clicking on dropdown box, OnPopupSize should be called OSR_TEST_POPUP_SIZE, // taking focus away from the webview, will close popup OSR_TEST_POPUP_HIDE_ON_BLUR, // clicking outside the popup widget will close popup OSR_TEST_POPUP_HIDE_ON_CLICK, // scrolling outside the popup widget will close popup OSR_TEST_POPUP_HIDE_ON_SCROLL, // pressing ESC will close popup OSR_TEST_POPUP_HIDE_ON_ESC, // scrolling inside the popup should trigger repaint for popup area OSR_TEST_POPUP_SCROLL_INSIDE, // clicking and moving the mouse will call StartDragging OSR_TEST_DRAG_DROP_START_DRAGGING, // starting dragging over the drop region will call UpdateDragCursor OSR_TEST_DRAG_DROP_UPDATE_CURSOR, // dropping element inside drop region will move the element OSR_TEST_DRAG_DROP_DROP, // IMESetComposition will update the composition range OSR_TEST_IME_SET_COMPOSITION, // IMECommitText inserts the specified text OSR_TEST_IME_COMMIT_TEXT, // IMEFinishComposition will commit the text present composition text OSR_TEST_IME_FINISH_COMPOSITION, // IMECancelComposition will update the composition range OSR_TEST_IME_CANCEL_COMPOSITION, // text selection range changed OSR_TEST_TEXT_SELECTION_CHANGE, // clicking on text input will call OnVirtualKeyboardRequested OSR_TEST_VIRTUAL_KEYBOARD, // touchStart event is triggered and contains the touch point count OSR_TEST_TOUCH_START, // touchMove event is triggered and contains the changed touch points OSR_TEST_TOUCH_MOVE, // touchEnd is triggered on completion OSR_TEST_TOUCH_END, // touchCancel is triggered on dismissing OSR_TEST_TOUCH_CANCEL, // CEF_POINTER_TYPE_PEN is mapped to pen pointer event OSR_TEST_PEN, // Define the range for popup tests. OSR_TEST_POPUP_FIRST = OSR_TEST_POPUP_PAINT, OSR_TEST_POPUP_LAST = OSR_TEST_POPUP_SCROLL_INSIDE, }; // Used in the browser process. class OSRTestHandler : public RoutingTestHandler, public CefFocusHandler, public CefRenderHandler, public CefContextMenuHandler { public: OSRTestHandler(OSRTestType test_type, float scale_factor) : test_type_(test_type), scale_factor_(scale_factor) {} // TestHandler methods void RunTest() override { CreateOSRBrowser(kTestUrl); // Time out the test after a reasonable period of time. SetTestTimeout(); } void OnAfterCreated(CefRefPtr browser) override { if (test_type_ == OSR_TEST_IS_WINDOWLESS) { EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled()); DestroySucceededTestSoon(); } RoutingTestHandler::OnAfterCreated(browser); } void OnLoadStart(CefRefPtr browser, CefRefPtr frame, TransitionType transition_type) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnLoadStart started=" << started() << " finished=" << finished(); #endif // Only interested in the 2nd+ load. if (!started()) { return; } switch (test_type_) { case OSR_TEST_KEY_EVENTS: { const std::string& expected_url = std::string(kTestUrl) + "?k=" + kKeyTestWord; EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str()); DestroySucceededTestSoon(); } break; case OSR_TEST_IME_COMMIT_TEXT: { const std::string& expected_url = std::string(kTestUrl) + "?k=osrimecommit"; EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str()); DestroySucceededTestSoon(); } break; case OSR_TEST_IME_FINISH_COMPOSITION: { const std::string& expected_url = std::string(kTestUrl) + "?k=" + kKeyTestWord; EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str()); DestroySucceededTestSoon(); } break; case OSR_TEST_IME_CANCEL_COMPOSITION: { const std::string& expected_url = std::string(kTestUrl) + "?k="; EXPECT_STREQ(expected_url.c_str(), frame->GetURL().ToString().c_str()); DestroySucceededTestSoon(); } break; default: // Intentionally left blank break; } } void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnLoadEnd started=" << started() << " finished=" << finished(); #endif // Only interested in the first load. if (started()) { return; } if (test_type_ >= OSR_TEST_POPUP_FIRST && test_type_ <= OSR_TEST_POPUP_LAST) { StartTest(); ExpandDropDown(); return; } switch (test_type_) { case OSR_TEST_TOOLTIP: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI10"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } break; case OSR_TEST_FOCUS: if (StartTest()) { // body.onfocus will make LI00 red browser->GetHost()->SetFocus(true); } break; case OSR_TEST_TAKE_FOCUS: if (StartTest()) { // Give focus to the last input element. browser->GetMainFrame()->ExecuteJavaScript( "document.getElementById('email').focus()", browser->GetMainFrame()->GetURL(), 0); // Tab traversal across HTML element. #if defined(OS_WIN) CefPostDelayedTask(TID_UI, base::BindOnce(&OSRTestHandler::SendKeyEvent, this, browser, VK_TAB), 50); #elif defined(OS_MAC) || defined(OS_LINUX) CefPostDelayedTask(TID_UI, base::BindOnce(&OSRTestHandler::SendKeyEvent, this, browser, kNativeKeyTab, VKEY_TAB), 50); #else #error "Unsupported platform" #endif } break; case OSR_TEST_GOT_FOCUS: if (StartTest()) { browser->GetHost()->SetFocus(true); } break; case OSR_TEST_CURSOR: if (StartTest()) { // enter mouse in the LI2 element having hand cursor CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI02"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } break; case OSR_TEST_MOUSE_MOVE: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI03"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } break; case OSR_TEST_CLICK_RIGHT: case OSR_TEST_SCREEN_POINT: case OSR_TEST_CONTEXT_MENU: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI04"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event, MBT_RIGHT); } break; case OSR_TEST_QUICK_MENU: if (StartTest()) { CefTouchEvent touch_event_pressed; touch_event_pressed.type = CEF_TET_PRESSED; const CefRect& expected_rect = GetElementBounds("quickmenu"); touch_event_pressed.x = MiddleX(expected_rect); touch_event_pressed.y = MiddleY(expected_rect); browser->GetHost()->SendTouchEvent(touch_event_pressed); } break; case OSR_TEST_CLICK_LEFT: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI00"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } break; case OSR_TEST_DRAG_DROP_START_DRAGGING: case OSR_TEST_DRAG_DROP_UPDATE_CURSOR: case OSR_TEST_DRAG_DROP_DROP: { // trigger the StartDragging event if (StartTest()) { // move the mouse over the element to drag CefMouseEvent mouse_event; const CefRect& dragdiv = GetElementBounds("dragdiv"); mouse_event.x = MiddleX(dragdiv); mouse_event.y = MiddleY(dragdiv); mouse_event.modifiers = 0; // The div drag point must be visible. EXPECT_LT(mouse_event.y, kOsrHeight); browser->GetHost()->SendMouseMoveEvent(mouse_event, false); // click on the element to drag mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; CefPostDelayedTask( TID_UI, base::BindOnce(&CefBrowserHost::SendMouseClickEvent, browser->GetHost(), mouse_event, MBT_LEFT, false, 1), 50); // move the mouse to start dragging mouse_event.x -= 5; mouse_event.y -= 5; CefPostDelayedTask( TID_UI, base::BindOnce(&CefBrowserHost::SendMouseMoveEvent, browser->GetHost(), mouse_event, false), 100); } } break; case OSR_TEST_KEY_EVENTS: case OSR_TEST_IME_COMMIT_TEXT: case OSR_TEST_IME_FINISH_COMPOSITION: case OSR_TEST_IME_CANCEL_COMPOSITION: case OSR_TEST_IME_SET_COMPOSITION: if (StartTest()) { // Results in a call to OnQuery. FocusEditBox(browser); } break; case OSR_TEST_TEXT_SELECTION_CHANGE: { // trigger the text selection changed event if (StartTest()) { // click inside list element so text range will be selected. CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI11"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } } break; case OSR_TEST_VIRTUAL_KEYBOARD: { if (StartTest()) { CefMouseEvent mouse_event; const CefRect& input = GetElementBounds("email"); mouse_event.x = MiddleX(input); mouse_event.y = MiddleY(input); mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } } break; case OSR_TEST_TOUCH_START: case OSR_TEST_TOUCH_MOVE: case OSR_TEST_TOUCH_END: case OSR_TEST_TOUCH_CANCEL: { // We trigger a valid Touch workflow sequence and close the tests // at seperate points for the 4 cases if (StartTest()) { const CefRect& touchdiv = GetElementBounds("touchdiv"); std::vector touch_events; // click inside edit box so that text could be entered CefTouchEvent touch_event1; touch_event1.id = 0; touch_event1.x = MiddleX(touchdiv) - 45; touch_event1.y = MiddleY(touchdiv); touch_event1.modifiers = 0; touch_event1.type = CEF_TET_PRESSED; touch_events.push_back(touch_event1); CefTouchEvent touch_event2; touch_event2.id = 1; touch_event2.x = MiddleX(touchdiv) + 45; touch_event2.y = MiddleY(touchdiv); touch_event2.modifiers = 0; touch_event2.type = CEF_TET_PRESSED; touch_events.push_back(touch_event2); if (test_type_ >= OSR_TEST_TOUCH_MOVE) { // Move the Touch fingers closer touch_event1.type = touch_event2.type = CEF_TET_MOVED; for (size_t i = 0; i < 40; i++) { touch_event1.x++; touch_event2.x--; touch_events.push_back(touch_event1); touch_events.push_back(touch_event2); } } // Now release the Touch fingers or cancel them if (test_type_ == OSR_TEST_TOUCH_CANCEL) { touch_event1.type = touch_event2.type = CEF_TET_CANCELLED; } else { touch_event1.type = touch_event2.type = CEF_TET_RELEASED; } touch_events.push_back(touch_event1); touch_events.push_back(touch_event2); CefPostDelayedTask(TID_UI, base::BindOnce(&OSRTestHandler::SendTouchEvents, this, std::move(touch_events)), 100); } } break; case OSR_TEST_PEN: { if (StartTest()) { const CefRect& pointerdiv = GetElementBounds("pointerdiv"); std::vector touch_events; CefTouchEvent touch_event; touch_event.x = MiddleX(pointerdiv) - 45; touch_event.y = MiddleY(pointerdiv); touch_event.type = CEF_TET_PRESSED; touch_event.pointer_type = CEF_POINTER_TYPE_PEN; touch_events.push_back(touch_event); touch_event.type = CEF_TET_MOVED; for (size_t i = 0; i < 40; i++) { touch_event.x++; touch_events.push_back(touch_event); } touch_event.type = CEF_TET_RELEASED; touch_events.push_back(touch_event); CefPostDelayedTask(TID_UI, base::BindOnce(&OSRTestHandler::SendTouchEvents, this, std::move(touch_events)), 100); } } break; default: break; } } bool OnQuery(CefRefPtr browser, CefRefPtr frame, int64_t query_id, const CefString& request, bool persistent, CefRefPtr callback) override { EXPECT_TRUE(browser.get()); const std::string& messageStr = request; if (messageStr.length() > 0 && messageStr[0] == '{') { return handleBoundsQuery(browser, frame, query_id, request, persistent, callback); } #if VERBOSE_DEBUGGING LOG(INFO) << "OnQuery message=" << messageStr; #endif switch (test_type_) { case OSR_TEST_FOCUS: if (messageStr == "osrfocus") { DestroySucceededTestSoon(); } break; case OSR_TEST_CLICK_LEFT: if (messageStr == "osrclick0") { DestroySucceededTestSoon(); } break; case OSR_TEST_MOUSE_MOVE: if (messageStr == "osrmousemove") { DestroySucceededTestSoon(); } break; case OSR_TEST_DRAG_DROP_DROP: if (messageStr == "osrdrop") { DestroySucceededTestSoon(); } break; case OSR_TEST_KEY_EVENTS: if (messageStr == "osrfocuseditbox") { SendKeyEvents(); } break; case OSR_TEST_IME_COMMIT_TEXT: if (messageStr == "osrfocuseditbox") { SendIMECommitText(); } break; case OSR_TEST_IME_FINISH_COMPOSITION: if (messageStr == "osrfocuseditbox") { SendIMEFinishComposition(); } break; case OSR_TEST_IME_CANCEL_COMPOSITION: if (messageStr == "osrfocuseditbox") { SendIMECancelComposition(); } break; case OSR_TEST_IME_SET_COMPOSITION: if (messageStr == "osrfocuseditbox") { SendIMESetComposition(); } break; case OSR_TEST_TOUCH_START: case OSR_TEST_TOUCH_MOVE: case OSR_TEST_TOUCH_END: case OSR_TEST_TOUCH_CANCEL: { switch (touch_state_) { case CEF_TET_CANCELLED: { // The first message expected is touchstart. // We expect multitouch, so touches length should be 2. // Ignore intermediate touch start events. if (messageStr == "osrtouchstart1") { break; } EXPECT_STREQ(messageStr.c_str(), "osrtouchstart2"); // Close Touch Start Tests. if (test_type_ == OSR_TEST_TOUCH_START) { DestroySucceededTestSoon(); touch_state_ = CEF_TET_RELEASED; } else { touch_state_ = CEF_TET_PRESSED; } } break; case CEF_TET_PRESSED: { // Touch Move include the touches that changed, should be 2. EXPECT_STREQ(messageStr.c_str(), "osrtouchmove2"); if (test_type_ == OSR_TEST_TOUCH_MOVE) { DestroySucceededTestSoon(); touch_state_ = CEF_TET_RELEASED; } else { touch_state_ = CEF_TET_MOVED; } } break; case CEF_TET_MOVED: { // There might be multiple touchmove events, ignore. if (messageStr != "osrtouchmove2") { if (test_type_ == OSR_TEST_TOUCH_END) { EXPECT_STREQ(messageStr.c_str(), "osrtouchend"); DestroySucceededTestSoon(); touch_state_ = CEF_TET_RELEASED; } else if (test_type_ == OSR_TEST_TOUCH_CANCEL) { EXPECT_STREQ(messageStr.c_str(), "osrtouchcancel"); DestroySucceededTestSoon(); touch_state_ = CEF_TET_RELEASED; } } } break; default: break; } } break; case OSR_TEST_PEN: { switch (touch_state_) { case CEF_TET_CANCELLED: // The first message expected is pointerdown. EXPECT_STREQ(messageStr.c_str(), "osrpointerdown pen"); touch_state_ = CEF_TET_PRESSED; break; case CEF_TET_PRESSED: EXPECT_STREQ(messageStr.c_str(), "osrpointermove pen"); touch_state_ = CEF_TET_MOVED; break; case CEF_TET_MOVED: // There might be multiple pointermove events, ignore. if (messageStr != "osrpointermove pen") { EXPECT_STREQ(messageStr.c_str(), "osrpointerup pen"); DestroySucceededTestSoon(); } break; default: break; } } break; default: // Intentionally left blank break; } callback->Success(""); return true; } bool handleBoundsQuery(CefRefPtr browser, CefRefPtr frame, int64_t query_id, const CefString& request, bool persistent, CefRefPtr callback) { CefRefPtr jsonObj = CefParseJSON(request, JSON_PARSER_RFC); if (jsonObj.get()) { CefRefPtr dict = jsonObj->GetDictionary(); const std::string& type = dict->GetString("type"); if (type == "ElementBounds") { CefRefPtr elems = dict->GetList("elems"); for (size_t i = 0; i < elems->GetSize(); i++) { CefRefPtr elem = elems->GetDictionary(i); std::string elementId = elem->GetString("id"); CefRect bounds(elem->GetInt("x"), elem->GetInt("y"), elem->GetInt("width"), elem->GetInt("height")); element_bounds_.insert(std::make_pair(elementId, bounds)); } return true; } } return false; } // CefClient methods, providing handlers CefRefPtr GetFocusHandler() override { return this; } CefRefPtr GetRenderHandler() override { return this; } CefRefPtr GetContextMenuHandler() override { return this; } // CefResourceRequestHandler methods CefRefPtr GetResourceHandler( CefRefPtr browser, CefRefPtr frame, CefRefPtr request) override { std::string url = request->GetURL(); if (url.find(kTestUrl) == 0) { // Show the osr test contents CefRefPtr stream = client::GetBinaryResourceReader("osr_test.html"); return new CefStreamResourceHandler("text/html", stream); } return nullptr; } // CefRenderHandler methods void GetViewRect(CefRefPtr browser, CefRect& rect) override { if (test_type_ == OSR_TEST_RESIZE && started()) { rect = CefRect(0, 0, kOsrWidth * 2, kOsrHeight * 2); return; } rect = CefRect(0, 0, kOsrWidth, kOsrHeight); } bool GetScreenPoint(CefRefPtr browser, int viewX, int viewY, int& screenX, int& screenY) override { if (test_type_ == OSR_TEST_SCREEN_POINT && started()) { const CefRect& expected_rect = GetElementBounds("LI04"); EXPECT_EQ(viewX, MiddleX(expected_rect)); EXPECT_EQ(viewY, MiddleY(expected_rect)); DestroySucceededTestSoon(); } else if (test_type_ == OSR_TEST_CONTEXT_MENU && started()) { screenX = 0; screenY = 0; return true; } // we don't want to see a contextual menu. stop here. return false; } bool GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) override { screen_info.device_scale_factor = scale_factor_; // The screen info rectangles are used by the renderer to create and // position popups. If not overwritten in this function, the rectangle // returned from GetViewRect will be used to popuplate them. // The popup in the test fits without modifications in the test window, so // setting the screen to the test window size does not affect its // rectangle. screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight); screen_info.available_rect = screen_info.rect; return true; } void OnPopupShow(CefRefPtr browser, bool show) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnPopupShow show=" << show << " started=" << started() << " finished=" << finished(); #endif if (finished()) { // OnPopupShow(show=false) may arrive after DestroyTest. EXPECT_FALSE(show); return; } EXPECT_TRUE(started()); if (show) { switch (test_type_) { case OSR_TEST_POPUP_SHOW: DestroySucceededTestSoon(); break; default: break; } } else { switch (test_type_) { case OSR_TEST_POPUP_HIDE_ON_BLUR: case OSR_TEST_POPUP_HIDE_ON_CLICK: case OSR_TEST_POPUP_HIDE_ON_ESC: case OSR_TEST_POPUP_HIDE_ON_SCROLL: DestroySucceededTestSoon(); break; default: break; } } } void OnPopupSize(CefRefPtr browser, const CefRect& rect) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnPopupSize started=" << started() << " finished=" << finished(); #endif if (finished()) { // For OSR_TEST_POPUP_SHOW, OnPopupSize may arrive after DestroyTest. EXPECT_EQ(OSR_TEST_POPUP_SHOW, test_type_); return; } EXPECT_TRUE(started()); switch (test_type_) { case OSR_TEST_POPUP_SIZE: EXPECT_EQ(kExpandedSelectRect.x, rect.x); EXPECT_EQ(kExpandedSelectRect.y, rect.y); if (ExpectComputedPopupSize()) { EXPECT_EQ(kExpandedSelectRect.width, rect.width); EXPECT_EQ(kExpandedSelectRect.height, rect.height); } else { EXPECT_GE(rect.width, kExpandedSelectRect.width); EXPECT_GE(rect.height, kExpandedSelectRect.height); } DestroySucceededTestSoon(); break; default: break; } } void OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override { // bitmap must as big as GetViewRect said if (test_type_ != OSR_TEST_RESIZE && type == PET_VIEW) { EXPECT_EQ(GetScaledInt(kOsrWidth), width); EXPECT_EQ(GetScaledInt(kOsrHeight), height); } else if (type == PET_POPUP) { const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect); if (ExpectComputedPopupSize()) { EXPECT_EQ(expanded_select_rect.width, width); EXPECT_EQ(expanded_select_rect.height, height); } else { EXPECT_GT(width, kExpandedSelectRect.width); EXPECT_GT(height, kExpandedSelectRect.height); } } EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled()); // start test only when painting something else then background if (IsBackgroundInBuffer( reinterpret_cast(buffer), width * height, test_type_ == OSR_TEST_TRANSPARENCY ? 0x00000000 : 0xFFFFFFFF)) { return; } // Send events after the first full repaint switch (test_type_) { case OSR_TEST_PAINT: if (StartTest()) { // test that we have a full repaint EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth), GetScaledInt(kOsrHeight))); EXPECT_EQ(0xffff7f7fU, *(reinterpret_cast(buffer))); DestroySucceededTestSoon(); } break; case OSR_TEST_TRANSPARENCY: if (StartTest()) { // test that we have a full repaint EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth), GetScaledInt(kOsrHeight))); EXPECT_EQ(0x80800000U, *(reinterpret_cast(buffer))); DestroySucceededTestSoon(); } break; case OSR_TEST_RESIZE: if (StartTest()) { browser->GetHost()->WasResized(); } else { // There may be some partial repaints before the full repaint at the // desired size. const int desired_width = GetScaledInt(kOsrWidth) * 2; const int desired_height = GetScaledInt(kOsrHeight) * 2; EXPECT_EQ(dirtyRects.size(), 1U); if (width == desired_width && height == desired_height && IsFullRepaint(dirtyRects[0], width, height)) { DestroySucceededTestSoon(); } } break; case OSR_TEST_INVALIDATE: { if (StartTest()) { browser->GetHost()->Invalidate(PET_VIEW); } else { EXPECT_EQ(dirtyRects.size(), 1U); const CefRect& expected_rect = GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight)); // There may be some partial repaints before the full repaint. if (dirtyRects[0] == expected_rect) { DestroySucceededTestSoon(); } } break; } case OSR_TEST_SCROLLING: { static const int deltaY = 10; if (StartTest()) { // scroll down once CefMouseEvent mouse_event; const CefRect& expected_rect = GetElementBounds("LI00"); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -deltaY); } else { EXPECT_EQ(dirtyRects.size(), 1U); const CefRect& expected_rect = GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight)); // There may be some partial repaints before the full repaint. if (dirtyRects[0] == expected_rect) { DestroySucceededTestSoon(); } } break; } case OSR_TEST_POPUP_HIDE_ON_CLICK: // Wait for the first popup paint to occur if (type == PET_POPUP) { CefMouseEvent mouse_event; mouse_event.x = 1; mouse_event.y = 1; mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } break; case OSR_TEST_POPUP_HIDE_ON_SCROLL: // Wait for the first popup paint to occur if (type == PET_POPUP) { CefMouseEvent mouse_event; mouse_event.x = mouse_event.y = 1; mouse_event.modifiers = 0; browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10); } break; case OSR_TEST_POPUP_HIDE_ON_BLUR: // Wait for the first popup paint to occur if (type == PET_POPUP) { browser->GetHost()->SetFocus(false); } break; case OSR_TEST_POPUP_HIDE_ON_ESC: // Wait for the first popup paint to occur if (type == PET_POPUP) { #if defined(OS_WIN) SendKeyEvent(browser, VK_ESCAPE); #elif defined(OS_MAC) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyEscape, VKEY_ESCAPE); #else #error "Unsupported platform" #endif } break; case OSR_TEST_POPUP_PAINT: // Wait for the first popup paint to occur if (type == PET_POPUP) { EXPECT_EQ(dirtyRects.size(), 1U); const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect); EXPECT_EQ(0, dirtyRects[0].x); EXPECT_EQ(0, dirtyRects[0].y); if (ExpectComputedPopupSize()) { EXPECT_EQ(expanded_select_rect.width, dirtyRects[0].width); EXPECT_EQ(expanded_select_rect.height, dirtyRects[0].height); } else { EXPECT_GT(dirtyRects[0].width, kExpandedSelectRect.width); EXPECT_GT(dirtyRects[0].height, kExpandedSelectRect.height); } // Unselected option background color is cyan. // Go down 100 pixels to skip the selected option and over 5 pixels // to avoid hitting the border. const uint32_t offset = dirtyRects[0].width * 100 + 5; EXPECT_EQ(0xff00ffff, *(reinterpret_cast(buffer) + offset)); if (ExpectComputedPopupSize()) { EXPECT_EQ(expanded_select_rect.width, width); EXPECT_EQ(expanded_select_rect.height, height); } else { EXPECT_GT(width, kExpandedSelectRect.width); EXPECT_GT(height, kExpandedSelectRect.height); } DestroySucceededTestSoon(); } break; case OSR_TEST_POPUP_SCROLL_INSIDE: { static enum { Started, Scrolled } scroll_inside_state = Started; // Wait for the first popup paint to occur if (type == PET_POPUP) { if (scroll_inside_state == Started) { CefMouseEvent mouse_event; mouse_event.x = MiddleX(kExpandedSelectRect); mouse_event.y = MiddleY(kExpandedSelectRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10); scroll_inside_state = Scrolled; } else if (scroll_inside_state == Scrolled) { const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect); EXPECT_EQ(dirtyRects.size(), 1U); const int scaled_int_1 = GetScaledInt(1); EXPECT_NEAR(0, dirtyRects[0].x, scaled_int_1); EXPECT_NEAR(0, dirtyRects[0].y, scaled_int_1); if (ExpectComputedPopupSize()) { EXPECT_NEAR(expanded_select_rect.width, dirtyRects[0].width, scaled_int_1 * 2); EXPECT_NEAR(expanded_select_rect.height, dirtyRects[0].height, scaled_int_1 * 2); } else { EXPECT_GT(dirtyRects[0].width, kExpandedSelectRect.width); EXPECT_GT(dirtyRects[0].height, kExpandedSelectRect.height); } DestroySucceededTestSoon(); } } } break; default: break; } } bool OnSetFocus(CefRefPtr browser, FocusSource source) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnSetFocus started=" << started(); #endif if (source == FOCUS_SOURCE_NAVIGATION) { got_navigation_focus_event_.yes(); // Ignore focus from the original navigation when we're testing focus // event delivery. if (test_type_ == OSR_TEST_FOCUS) { return true; } return false; } EXPECT_EQ(source, FOCUS_SOURCE_SYSTEM); got_system_focus_event_.yes(); return false; } void OnTakeFocus(CefRefPtr browser, bool next) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnTakeFocus next=" << next << " started=" << started(); #endif if (test_type_ == OSR_TEST_TAKE_FOCUS) { DestroySucceededTestSoon(); } } void OnGotFocus(CefRefPtr browser) override { #if VERBOSE_DEBUGGING LOG(INFO) << "OnGotFocus started=" << started(); #endif if (test_type_ == OSR_TEST_GOT_FOCUS) { DestroySucceededTestSoon(); } } bool OnCursorChange(CefRefPtr browser, CefCursorHandle cursor, cef_cursor_type_t type, const CefCursorInfo& custom_cursor_info) override { if (test_type_ == OSR_TEST_CURSOR && started()) { EXPECT_EQ(CT_HAND, type); EXPECT_EQ(nullptr, custom_cursor_info.buffer); DestroySucceededTestSoon(); } return true; } void OnImeCompositionRangeChanged( CefRefPtr browser, const CefRange& range, const CefRenderHandler::RectList& bounds) override { if (test_type_ == OSR_TEST_IME_SET_COMPOSITION && started()) { EXPECT_EQ(range.from, 0U); EXPECT_EQ(range.to, 1U); EXPECT_EQ(1U, bounds.size()); DestroySucceededTestSoon(); } } bool StartDragging(CefRefPtr browser, CefRefPtr drag_data, CefRenderHandler::DragOperationsMask allowed_ops, int x, int y) override { if (test_type_ == OSR_TEST_DRAG_DROP_START_DRAGGING && started()) { // Verify the drag image representation. const CefRect& dragdiv = GetElementBounds("dragdiv"); EXPECT_TRUE(drag_data->HasImage()); CefRefPtr image = drag_data->GetImage(); EXPECT_TRUE(image.get() != nullptr); if (image.get()) { // Drag image height seems to always be + 1px greater than the drag // rect on Linux. Therefore allow it to be +/- 1px. EXPECT_NEAR(static_cast(image->GetWidth()), dragdiv.width, 1); EXPECT_NEAR(static_cast(image->GetHeight()), dragdiv.height, 1); } // During testing hotspot (x, y) was (15, 23) at 1x scale and (15, 18) // at 2x scale. Since the mechanism for determining this position is // unclear test only that it falls within the rect boundaries. CefPoint hotspot = drag_data->GetImageHotspot(); EXPECT_GT(hotspot.x, 0); EXPECT_LE(hotspot.x, GetScaledInt(dragdiv.width)); EXPECT_GT(hotspot.y, 0); EXPECT_LE(hotspot.y, GetScaledInt(dragdiv.height)); DestroySucceededTestSoon(); return false; } else if ((test_type_ == OSR_TEST_DRAG_DROP_UPDATE_CURSOR || test_type_ == OSR_TEST_DRAG_DROP_DROP) && started()) { // place the mouse over the drop area to trigger UpdateDragCursor CefRefPtr data = drag_data->Clone(); data->ResetFileContents(); CefMouseEvent ev; const CefRect& dragdiv = GetElementBounds("dragdiv"); ev.x = MiddleX(dragdiv) - 5; ev.y = MiddleY(dragdiv) - 5; ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; browser->GetHost()->DragTargetDragEnter(data, ev, allowed_ops); const CefRect& dropdiv = GetElementBounds("dropdiv"); ev.x = MiddleX(dropdiv); ev.y = MiddleY(dropdiv); browser->GetHost()->SendMouseMoveEvent(ev, false); browser->GetHost()->DragTargetDragOver(ev, allowed_ops); ev.x += 5; ev.y += 5; browser->GetHost()->SendMouseMoveEvent(ev, false); browser->GetHost()->DragTargetDragOver(ev, allowed_ops); return true; } return false; } void UpdateDragCursor(CefRefPtr browser, DragOperation operation) override { if (operation == DRAG_OPERATION_NONE) { return; } if (test_type_ == OSR_TEST_DRAG_DROP_UPDATE_CURSOR && started()) { const CefRect& dropdiv = GetElementBounds("dropdiv"); browser->GetHost()->DragSourceEndedAt(MiddleX(dropdiv), MiddleY(dropdiv), DRAG_OPERATION_NONE); browser->GetHost()->DragSourceSystemDragEnded(); DestroySucceededTestSoon(); } else if (test_type_ == OSR_TEST_DRAG_DROP_DROP && started()) { // Don't end the drag multiple times. if (got_update_cursor_) { return; } got_update_cursor_.yes(); CefMouseEvent ev; const CefRect& dropdiv = GetElementBounds("dropdiv"); ev.x = MiddleX(dropdiv); ev.y = MiddleY(dropdiv); ev.modifiers = 0; browser->GetHost()->SendMouseClickEvent(ev, MBT_LEFT, true, 1); browser->GetHost()->DragTargetDrop(ev); browser->GetHost()->DragSourceEndedAt(ev.x, ev.y, operation); browser->GetHost()->DragSourceSystemDragEnded(); } } void OnTextSelectionChanged(CefRefPtr browser, const CefString& selected_text, const CefRange& selected_range) override { if (test_type_ == OSR_TEST_TEXT_SELECTION_CHANGE && started()) { if (!got_initial_text_selection_event_) { got_initial_text_selection_event_.yes(); } else { EXPECT_STREQ("SELECTED_TEXT_RANGE", selected_text.ToString().c_str()); DestroySucceededTestSoon(); } } } void OnVirtualKeyboardRequested(CefRefPtr browser, TextInputMode input_mode) override { if (test_type_ == OSR_TEST_VIRTUAL_KEYBOARD && started()) { if (!got_virtual_keyboard_event_.isSet()) { got_virtual_keyboard_event_.yes(); EXPECT_EQ(CEF_TEXT_INPUT_MODE_EMAIL, input_mode); CefMouseEvent mouse_event; const CefRect& input = GetElementBounds("LI01"); mouse_event.x = MiddleX(input); mouse_event.y = MiddleY(input); mouse_event.modifiers = 0; SendMouseClickEvent(browser, mouse_event); } else { EXPECT_EQ(CEF_TEXT_INPUT_MODE_NONE, input_mode); DestroySucceededTestSoon(); } } } bool OnTooltip(CefRefPtr browser, CefString& text) override { if (test_type_ == OSR_TEST_TOOLTIP && started()) { EXPECT_STREQ("EXPECTED_TOOLTIP", text.ToString().c_str()); DestroySucceededTestSoon(); } return false; } void OnBeforeContextMenu(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model) override { if (!started()) { return; } if (test_type_ == OSR_TEST_CLICK_RIGHT) { const CefRect& expected_rect = GetElementBounds("LI04"); EXPECT_EQ(params->GetXCoord(), MiddleX(expected_rect)); EXPECT_EQ(params->GetYCoord(), MiddleY(expected_rect)); DestroySucceededTestSoon(); } else if (test_type_ == OSR_TEST_CONTEXT_MENU) { // This test will pass if it does not crash on destruction DestroySucceededTestSoon(); } } bool RunContextMenu(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model, CefRefPtr callback) override { if (!started()) { return false; } EXPECT_UI_THREAD(); auto current_browser = GetBrowser(); EXPECT_TRUE(current_browser->IsSame(browser)); EXPECT_STREQ( current_browser->GetFocusedFrame()->GetIdentifier().ToString().c_str(), frame->GetIdentifier().ToString().c_str()); if (test_type_ == OSR_TEST_QUICK_MENU) { EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_TRUE(got_quick_menu_); EXPECT_TRUE(got_quick_menu_command_); EXPECT_TRUE(got_quick_menu_dismissed_); EXPECT_EQ(2U, got_touch_handle_disabled_ct_); EXPECT_FALSE(got_context_menu_); got_context_menu_.yes(); // Got all expected callbacks. DestroySucceededTestSoon(); // Cancel the menu immediately. callback->Cancel(); return true; } return false; } bool RunQuickMenu(CefRefPtr browser, CefRefPtr frame, const CefPoint& location, const CefSize& size, QuickMenuEditStateFlags edit_state_flags, CefRefPtr callback) override { if (!started()) { return false; } EXPECT_UI_THREAD(); auto current_browser = GetBrowser(); EXPECT_TRUE(current_browser->IsSame(browser)); EXPECT_STREQ( current_browser->GetFocusedFrame()->GetIdentifier().ToString().c_str(), frame->GetIdentifier().ToString().c_str()); EXPECT_EQ(OSR_TEST_QUICK_MENU, test_type_); EXPECT_GT(location.x, 0); EXPECT_GT(location.y, 0); EXPECT_GT(size.width, 0); EXPECT_GT(size.height, 0); EXPECT_EQ(static_cast(QM_EDITFLAG_CAN_ELLIPSIS | QM_EDITFLAG_CAN_COPY), edit_state_flags); EXPECT_TRUE(callback.get()); EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_FALSE(got_quick_menu_); EXPECT_FALSE(got_quick_menu_command_); EXPECT_FALSE(got_quick_menu_dismissed_); EXPECT_EQ(0U, got_touch_handle_disabled_ct_); EXPECT_FALSE(got_context_menu_); got_quick_menu_.yes(); // Proceed to OnQuickMenuCommand. callback->Continue(QM_EDITFLAG_CAN_ELLIPSIS, static_cast(EVENTFLAG_SHIFT_DOWN)); return true; } bool OnQuickMenuCommand(CefRefPtr browser, CefRefPtr frame, int command_id, EventFlags event_flags) override { EXPECT_UI_THREAD(); auto current_browser = GetBrowser(); EXPECT_TRUE(current_browser->IsSame(browser)); EXPECT_STREQ( current_browser->GetFocusedFrame()->GetIdentifier().ToString().c_str(), frame->GetIdentifier().ToString().c_str()); EXPECT_EQ(OSR_TEST_QUICK_MENU, test_type_); // Values passed to Continue() in RunQuickMenu. EXPECT_EQ(QM_EDITFLAG_CAN_ELLIPSIS, command_id); EXPECT_EQ(EVENTFLAG_SHIFT_DOWN, event_flags); EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_TRUE(got_quick_menu_); EXPECT_FALSE(got_quick_menu_command_); EXPECT_FALSE(got_quick_menu_dismissed_); EXPECT_EQ(0U, got_touch_handle_disabled_ct_); EXPECT_FALSE(got_context_menu_); got_quick_menu_command_.yes(); // Proceed to OnQuickMenuDismissed and RunContextMenu. return false; } void OnQuickMenuDismissed(CefRefPtr browser, CefRefPtr frame) override { EXPECT_UI_THREAD(); auto current_browser = GetBrowser(); EXPECT_TRUE(current_browser->IsSame(browser)); EXPECT_STREQ( current_browser->GetFocusedFrame()->GetIdentifier().ToString().c_str(), frame->GetIdentifier().ToString().c_str()); EXPECT_EQ(OSR_TEST_QUICK_MENU, test_type_); EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_TRUE(got_quick_menu_); EXPECT_TRUE(got_quick_menu_command_); EXPECT_FALSE(got_quick_menu_dismissed_); EXPECT_EQ(0U, got_touch_handle_disabled_ct_); EXPECT_FALSE(got_context_menu_); EXPECT_EQ(kAllTouchHandleFlags, touch_handle_flags_); got_quick_menu_dismissed_.yes(); } void GetTouchHandleSize(CefRefPtr browser, cef_horizontal_alignment_t orientation, CefSize& size) override { size = CefSize(24, 24); got_touch_handle_size_ct_++; } void OnTouchHandleStateChanged(CefRefPtr browser, const CefTouchHandleState& state) override { if (!started()) { return; } EXPECT_UI_THREAD(); auto current_browser = GetBrowser(); EXPECT_TRUE(current_browser->IsSame(browser)); if (test_type_ == OSR_TEST_QUICK_MENU) { if (state.flags & CEF_THS_FLAG_ENABLED) { EXPECT_EQ(state.orientation, 0); EXPECT_EQ(state.origin.x, 0); EXPECT_EQ(state.origin.y, 0); EXPECT_EQ(state.alpha, 0); if (state.enabled) { got_touch_handle_enabled_ct_++; EXPECT_FALSE(got_quick_menu_); EXPECT_FALSE(got_quick_menu_command_); EXPECT_FALSE(got_quick_menu_dismissed_); EXPECT_EQ(0U, got_touch_handle_disabled_ct_); EXPECT_FALSE(got_context_menu_); touch_handle_flags_ |= CEF_THS_FLAG_ENABLED; } else { got_touch_handle_disabled_ct_++; EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_TRUE(got_quick_menu_); EXPECT_TRUE(got_quick_menu_command_); EXPECT_TRUE(got_quick_menu_dismissed_); EXPECT_FALSE(got_context_menu_); } } if (state.flags & CEF_THS_FLAG_ORIENTATION) { EXPECT_EQ(state.enabled, false); EXPECT_GE(state.orientation, 0); EXPECT_EQ(state.origin.x, 0); EXPECT_EQ(state.origin.y, 0); EXPECT_EQ(state.alpha, 0); touch_handle_flags_ |= CEF_THS_FLAG_ORIENTATION; } if (state.flags & CEF_THS_FLAG_ORIGIN) { EXPECT_EQ(state.enabled, false); EXPECT_EQ(state.orientation, 0); EXPECT_GT(state.origin.x, 0); EXPECT_GT(state.origin.y, 0); EXPECT_EQ(state.alpha, 0); touch_handle_flags_ |= CEF_THS_FLAG_ORIGIN; } if (state.flags & CEF_THS_FLAG_ALPHA) { EXPECT_EQ(state.enabled, false); EXPECT_EQ(state.orientation, 0); EXPECT_EQ(state.origin.x, 0); EXPECT_EQ(state.origin.y, 0); EXPECT_GE(state.alpha, 0); touch_handle_flags_ |= CEF_THS_FLAG_ALPHA; } } } // OSRTestHandler functions void CreateOSRBrowser(const CefString& url) { CefWindowInfo windowInfo; CefBrowserSettings settings; if (test_type_ != OSR_TEST_TRANSPARENCY) { // Explicitly set an opaque background color to disable transparency. settings.background_color = CefColorSetARGB(255, 255, 255, 255); } #if defined(OS_WIN) windowInfo.SetAsWindowless(GetDesktopWindow()); #elif defined(OS_MAC) // An actual vies is needed only for the ContextMenu test. The menu runner // checks if the view is not nil before showing the context menu. if (test_type_ == OSR_TEST_CONTEXT_MENU) { windowInfo.SetAsWindowless(osr_unittests::GetFakeView()); } else { windowInfo.SetAsWindowless(kNullWindowHandle); } #elif defined(OS_LINUX) windowInfo.SetAsWindowless(kNullWindowHandle); #else #error "Unsupported platform" #endif CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, nullptr, nullptr); } CefRect GetScaledRect(const CefRect& rect) const { return client::LogicalToDevice(rect, scale_factor_); } int GetScaledInt(int value) const { return client::LogicalToDevice(value, scale_factor_); } CefRect GetElementBounds(const std::string& id) { ElementBoundsMap::const_iterator it = element_bounds_.find(id); if (it != element_bounds_.end()) { return it->second; } return CefRect(); } static bool IsFullRepaint(const CefRect& rc, int width, int height) { return rc.width == width && rc.height == height; } static bool IsBackgroundInBuffer(const uint32_t* buffer, size_t size, uint32_t rgba) { for (size_t i = 0; i < size; i++) { if (buffer[i] != rgba) { return false; } } return true; } static inline int MiddleX(const CefRect& rect) { return rect.x + rect.width / 2; } static inline int MiddleY(const CefRect& rect) { return rect.y + rect.height / 2; } bool ExpectComputedPopupSize() const { // The device scale factor is ignored in Blink when computing // the default form control font size (see https://crbug.com/674663#c11). // This results in better font size display but also means that we won't // get the expected (scaled) width/height value for non-1.0 scale factor // select popups. // The non-1.0 scale factor size is off by a few pixels so we can't // perform an exact comparison. return scale_factor_ == 1.0; } static void FocusEditBox(CefRefPtr browser) { browser->GetMainFrame()->ExecuteJavaScript( "document.getElementById('editbox').focus()", browser->GetMainFrame()->GetURL(), 0); } static void ClickButtonToNavigate(CefRefPtr browser) { browser->GetMainFrame()->ExecuteJavaScript( "document.getElementById('btnnavigate').click()", browser->GetMainFrame()->GetURL(), 0); } void SendKeyEvents() { auto browser = GetBrowser(); // write "done" word CefKeyEvent event; event.is_system_key = false; event.modifiers = 0; size_t word_length = strlen(kKeyTestWord); for (size_t i = 0; i < word_length; ++i) { #if defined(OS_WIN) SendKeyEvent(browser, kKeyTestWord[i]); #elif defined(OS_MAC) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]); #else #error "Unsupported platform" #endif } ClickButtonToNavigate(browser); } void SendIMECommitText() { auto browser = GetBrowser(); size_t word_length = strlen(kKeyTestWord); // Add some input keys to edit box for (size_t i = 0; i < word_length; ++i) { #if defined(OS_WIN) SendKeyEvent(browser, kKeyTestWord[i]); #elif defined(OS_MAC) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]); #else #error "Unsupported platform" #endif } // This text should be honored instead of 'ka' added via key events CefString markedText("osrimecommit"); CefRange range(0, static_cast(markedText.length())); browser->GetHost()->ImeCommitText(markedText, range, 0); ClickButtonToNavigate(browser); } void SendIMEFinishComposition() { auto browser = GetBrowser(); size_t word_length = strlen(kKeyTestWord); // Add some input keys to edit box for (size_t i = 0; i < word_length; ++i) { #if defined(OS_WIN) SendKeyEvent(browser, kKeyTestWord[i]); #elif defined(OS_MAC) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]); #else #error "Unsupported platform" #endif } // Finish Composition should set the existing composition browser->GetHost()->ImeFinishComposingText(true); ClickButtonToNavigate(browser); } void SendIMECancelComposition() { auto browser = GetBrowser(); // Add some input keys to edit box CefString markedText(L"\u304B"); std::vector underlines; // Use a thin black underline by default. CefRange range(0, static_cast(markedText.length())); cef_composition_underline_t line = {range, 0xFF000000, 0, false}; underlines.push_back(line); CefRange replacement_range(0, static_cast(markedText.length())); CefRange selection_range(0, static_cast(markedText.length())); // Composition should be updated browser->GetHost()->ImeSetComposition(markedText, underlines, replacement_range, selection_range); // CancelComposition should clean up the edit text browser->GetHost()->ImeCancelComposition(); ClickButtonToNavigate(browser); } void SendIMESetComposition() { auto browser = GetBrowser(); // Now set some intermediate text composition CefString markedText(L"\u304B"); std::vector underlines; // Use a thin black underline by default. CefRange range(0, static_cast(markedText.length())); cef_composition_underline_t line = {range, 0xFF000000, 0, false}; underlines.push_back(line); CefRange replacement_range(0, static_cast(markedText.length())); CefRange selection_range(0, static_cast(markedText.length())); // This should update composition range and // trigger the compositionRangeChanged callback browser->GetHost()->ImeSetComposition(markedText, underlines, replacement_range, selection_range); } void SendTouchEvents(std::vector touch_events) { auto host = GetBrowser()->GetHost(); for (const auto& te : touch_events) { host->SendTouchEvent(te); } } void DestroySucceededTestSoon() { #if VERBOSE_DEBUGGING LOG(INFO) << "DestroySucceededTestSoon finished=" << finished(); #endif if (finished()) { return; } finished_ = true; CefPostTask(TID_UI, base::BindOnce(&OSRTestHandler::DestroyTest, this)); } void DestroyTest() override { #if VERBOSE_DEBUGGING LOG(INFO) << "DestroyTest"; #endif // Always get the OnSetFocus call for the initial navigation. EXPECT_TRUE(got_navigation_focus_event_); if (test_type_ == OSR_TEST_FOCUS) { // SetFocus is called by the system when we explicitly set the focus and // when popups are dismissed. EXPECT_TRUE(got_system_focus_event_); } else if (test_type_ == OSR_TEST_TEXT_SELECTION_CHANGE) { EXPECT_TRUE(got_initial_text_selection_event_); } else { EXPECT_FALSE(got_system_focus_event_); } if (test_type_ == OSR_TEST_QUICK_MENU) { EXPECT_EQ(2U, got_touch_handle_enabled_ct_); EXPECT_EQ(2U, got_touch_handle_size_ct_); EXPECT_TRUE(got_quick_menu_); EXPECT_TRUE(got_quick_menu_command_); EXPECT_TRUE(got_quick_menu_dismissed_); EXPECT_EQ(2U, got_touch_handle_disabled_ct_); EXPECT_TRUE(got_context_menu_); EXPECT_EQ(kAllTouchHandleFlags, touch_handle_flags_); } RoutingTestHandler::DestroyTest(); } void ExpandDropDown() { #if VERBOSE_DEBUGGING LOG(INFO) << "ExpandDropDown"; #endif EXPECT_TRUE(started()); EXPECT_FALSE(finished()); const CefRect& LI11select = GetElementBounds("LI11select"); CefMouseEvent mouse_event; mouse_event.x = MiddleX(LI11select); mouse_event.y = MiddleY(LI11select); mouse_event.modifiers = 0; SendMouseClickEvent(GetBrowser(), mouse_event); } void SendKeyEvent(CefRefPtr browser, #if defined(OS_LINUX) || defined(OS_MAC) unsigned int native_key_code, #endif int key_code) { CefKeyEvent event; event.is_system_key = false; event.modifiers = 0; #if defined(OS_WIN) BYTE VkCode = LOBYTE(VkKeyScanA(key_code)); UINT scanCode = MapVirtualKey(VkCode, MAPVK_VK_TO_VSC); event.native_key_code = (scanCode << 16) | // key scan code 1; // key repeat count event.windows_key_code = VkCode; #elif defined(OS_MAC) event.native_key_code = native_key_code; // Note that this is only correct for lower-case characters. If |key_code| // was an upper-case character then |event.character| would be the upper- // case character and |event.unmodified_character| would be the lower-case // character (e.g. the character without the shift modifier applied). event.character = event.unmodified_character = key_code; #elif defined(OS_LINUX) event.native_key_code = native_key_code; event.windows_key_code = key_code; event.character = event.unmodified_character = native_key_code; #else NOTREACHED(); #endif event.type = KEYEVENT_RAWKEYDOWN; browser->GetHost()->SendKeyEvent(event); #if defined(OS_WIN) event.windows_key_code = key_code; #endif event.type = KEYEVENT_CHAR; browser->GetHost()->SendKeyEvent(event); #if defined(OS_WIN) event.windows_key_code = VkCode; // bits 30 and 31 should be always 1 for WM_KEYUP event.native_key_code |= 0xC0000000; #endif event.type = KEYEVENT_KEYUP; browser->GetHost()->SendKeyEvent(event); } // true if the events for this test are already sent bool started() const { return started_; } // true if the exit point was reached, even the result is not // the expected one bool finished() const { return finished_; } // will mark test as started and will return true only the first time // it is called bool StartTest() { if (started_) { return false; } started_ = true; return true; } private: const OSRTestType test_type_; const float scale_factor_; bool started_ = false; bool finished_ = false; cef_touch_event_type_t touch_state_ = CEF_TET_CANCELLED; TrackCallback got_update_cursor_; TrackCallback got_navigation_focus_event_; TrackCallback got_system_focus_event_; TrackCallback got_initial_text_selection_event_; TrackCallback got_virtual_keyboard_event_; uint32_t touch_handle_flags_ = 0U; size_t got_touch_handle_enabled_ct_ = 0U; size_t got_touch_handle_size_ct_ = 0U; TrackCallback got_quick_menu_; TrackCallback got_quick_menu_command_; TrackCallback got_quick_menu_dismissed_; size_t got_touch_handle_disabled_ct_ = 0U; TrackCallback got_context_menu_; typedef std::map ElementBoundsMap; ElementBoundsMap element_bounds_; IMPLEMENT_REFCOUNTING(OSRTestHandler); }; } // namespace // generic test #define OSR_TEST(name, test_mode, scale_factor) \ TEST(OSRTest, name) { \ CefRefPtr handler = \ new OSRTestHandler(test_mode, scale_factor); \ handler->ExecuteTest(); \ EXPECT_TRUE(handler->finished()); \ ReleaseAndWaitForDestructor(handler); \ } // tests OSR_TEST(Windowless, OSR_TEST_IS_WINDOWLESS, 1.0f) OSR_TEST(Windowless2x, OSR_TEST_IS_WINDOWLESS, 2.0f) OSR_TEST(Focus, OSR_TEST_FOCUS, 1.0f) OSR_TEST(Focus2x, OSR_TEST_FOCUS, 2.0f) OSR_TEST(TakeFocus, OSR_TEST_TAKE_FOCUS, 1.0f) OSR_TEST(TakeFocus2x, OSR_TEST_TAKE_FOCUS, 2.0f) OSR_TEST(GotFocus, OSR_TEST_GOT_FOCUS, 1.0f) OSR_TEST(GotFocus2x, OSR_TEST_GOT_FOCUS, 2.0f) OSR_TEST(Paint, OSR_TEST_PAINT, 1.0f) OSR_TEST(Paint2x, OSR_TEST_PAINT, 2.0f) OSR_TEST(TransparentPaint, OSR_TEST_TRANSPARENCY, 1.0f) OSR_TEST(TransparentPaint2x, OSR_TEST_TRANSPARENCY, 2.0f) OSR_TEST(Cursor, OSR_TEST_CURSOR, 1.0f) OSR_TEST(Cursor2x, OSR_TEST_CURSOR, 2.0f) OSR_TEST(MouseMove, OSR_TEST_MOUSE_MOVE, 1.0f) OSR_TEST(MouseMove2x, OSR_TEST_MOUSE_MOVE, 2.0f) OSR_TEST(MouseRightClick, OSR_TEST_CLICK_RIGHT, 1.0f) OSR_TEST(MouseRightClick2x, OSR_TEST_CLICK_RIGHT, 2.0f) OSR_TEST(MouseLeftClick, OSR_TEST_CLICK_LEFT, 1.0f) OSR_TEST(MouseLeftClick2x, OSR_TEST_CLICK_LEFT, 2.0f) OSR_TEST(ScreenPoint, OSR_TEST_SCREEN_POINT, 1.0f) OSR_TEST(ScreenPoint2x, OSR_TEST_SCREEN_POINT, 2.0f) OSR_TEST(Resize, OSR_TEST_RESIZE, 1.0f) OSR_TEST(Resize2x, OSR_TEST_RESIZE, 2.0f) OSR_TEST(Invalidate, OSR_TEST_INVALIDATE, 1.0f) OSR_TEST(Invalidate2x, OSR_TEST_INVALIDATE, 2.0f) OSR_TEST(KeyEvents, OSR_TEST_KEY_EVENTS, 1.0f) OSR_TEST(KeyEvents2x, OSR_TEST_KEY_EVENTS, 2.0f) OSR_TEST(Tooltip, OSR_TEST_TOOLTIP, 1.0f) OSR_TEST(Tooltip2x, OSR_TEST_TOOLTIP, 2.0f) OSR_TEST(Scrolling, OSR_TEST_SCROLLING, 1.0f) OSR_TEST(Scrolling2x, OSR_TEST_SCROLLING, 2.0f) OSR_TEST(ContextMenu, OSR_TEST_CONTEXT_MENU, 1.0f) OSR_TEST(ContextMenu2x, OSR_TEST_CONTEXT_MENU, 2.0f) OSR_TEST(QuickMenu, OSR_TEST_QUICK_MENU, 1.0f) OSR_TEST(QuickMenu2x, OSR_TEST_CONTEXT_MENU, 2.0f) OSR_TEST(PopupPaint, OSR_TEST_POPUP_PAINT, 1.0f) OSR_TEST(PopupPaint2x, OSR_TEST_POPUP_PAINT, 2.0f) OSR_TEST(PopupShow, OSR_TEST_POPUP_SHOW, 1.0f) OSR_TEST(PopupShow2x, OSR_TEST_POPUP_SHOW, 2.0f) OSR_TEST(PopupSize, OSR_TEST_POPUP_SIZE, 1.0f) OSR_TEST(PopupSize2x, OSR_TEST_POPUP_SIZE, 2.0f) OSR_TEST(PopupHideOnBlur, OSR_TEST_POPUP_HIDE_ON_BLUR, 1.0f) OSR_TEST(PopupHideOnBlur2x, OSR_TEST_POPUP_HIDE_ON_BLUR, 2.0f) OSR_TEST(PopupHideOnClick, OSR_TEST_POPUP_HIDE_ON_CLICK, 1.0f) OSR_TEST(PopupHideOnClick2x, OSR_TEST_POPUP_HIDE_ON_CLICK, 2.0f) OSR_TEST(PopupHideOnScroll, OSR_TEST_POPUP_HIDE_ON_SCROLL, 1.0f) OSR_TEST(PopupHideOnScroll2x, OSR_TEST_POPUP_HIDE_ON_SCROLL, 2.0f) OSR_TEST(PopupHideOnEsc, OSR_TEST_POPUP_HIDE_ON_ESC, 1.0f) OSR_TEST(PopupHideOnEsc2x, OSR_TEST_POPUP_HIDE_ON_ESC, 2.0f) OSR_TEST(PopupScrollInside, OSR_TEST_POPUP_SCROLL_INSIDE, 1.0f) OSR_TEST(PopupScrollInside2x, OSR_TEST_POPUP_SCROLL_INSIDE, 2.0f) OSR_TEST(DragDropStartDragging, OSR_TEST_DRAG_DROP_START_DRAGGING, 1.0f) OSR_TEST(DragDropStartDragging2x, OSR_TEST_DRAG_DROP_START_DRAGGING, 2.0f) OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 1.0f) OSR_TEST(DragDropUpdateCursor2x, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 2.0f) OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP, 1.0f) OSR_TEST(DragDropDropElement2x, OSR_TEST_DRAG_DROP_DROP, 2.0f) OSR_TEST(IMESetComposition, OSR_TEST_IME_SET_COMPOSITION, 1.0f) OSR_TEST(IMESetComposition2x, OSR_TEST_IME_SET_COMPOSITION, 2.0f) OSR_TEST(IMECommitText, OSR_TEST_IME_COMMIT_TEXT, 1.0f) OSR_TEST(IMECommitText2x, OSR_TEST_IME_COMMIT_TEXT, 2.0f) OSR_TEST(IMEFinishComposition, OSR_TEST_IME_FINISH_COMPOSITION, 1.0f) OSR_TEST(IMEFinishComposition2x, OSR_TEST_IME_FINISH_COMPOSITION, 2.0f) OSR_TEST(IMECancelComposition, OSR_TEST_IME_CANCEL_COMPOSITION, 1.0f) OSR_TEST(IMECancelComposition2x, OSR_TEST_IME_CANCEL_COMPOSITION, 2.0f) OSR_TEST(TextSelectionChanged, OSR_TEST_TEXT_SELECTION_CHANGE, 1.0f) OSR_TEST(TextSelectionChanged2x, OSR_TEST_TEXT_SELECTION_CHANGE, 2.0f) OSR_TEST(VirtualKeyboard, OSR_TEST_VIRTUAL_KEYBOARD, 1.0f) OSR_TEST(TouchStart, OSR_TEST_TOUCH_START, 1.0f) OSR_TEST(TouchStart2x, OSR_TEST_TOUCH_START, 2.0f) OSR_TEST(TouchMove, OSR_TEST_TOUCH_MOVE, 1.0f) OSR_TEST(TouchMove2x, OSR_TEST_TOUCH_MOVE, 2.0f) OSR_TEST(TouchEnd, OSR_TEST_TOUCH_END, 1.0f) OSR_TEST(TouchEnd2x, OSR_TEST_TOUCH_END, 2.0f) OSR_TEST(TouchCancel, OSR_TEST_TOUCH_CANCEL, 1.0f) OSR_TEST(TouchCancel2x, OSR_TEST_TOUCH_CANCEL, 2.0f) OSR_TEST(PenEvent, OSR_TEST_PEN, 1.0f)