// 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_bind.h" #include "include/base/cef_logging.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/gtest/include/gtest/gtest.h" #include "tests/shared/browser/geometry_util.h" #include "tests/shared/browser/resource_util.h" #if defined(OS_MACOSX) #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(NULL); #endif namespace { const char kTestUrl[] = "http://tests/osrtest"; // this html should render on a 600 x 400 window with a little vertical // offset with scrollbar. // default osr widget size const int kOsrWidth = 600; const int kOsrHeight = 400; // precomputed bounding client rects for html elements (h1 and li). #if defined(OS_WIN) || defined(OS_LINUX) const CefRect kExpectedRectLI[] = { CefRect(8, 8, 567, 74), // LI00 CefRect(27, 103, 548, 20), // LI01 CefRect(27, 123, 548, 20), // LI02 CefRect(27, 143, 548, 20), // LI03 CefRect(27, 163, 548, 20), // LI04 CefRect(27, 183, 548, 20), // LI05 CefRect(27, 203, 548, 20), // LI06 CefRect(27, 223, 548, 20), // LI07 CefRect(27, 243, 548, 26), // LI08 CefRect(27, 269, 548, 26), // LI09 CefRect(27, 295, 548, 20), // LI10 }; #elif defined(OS_MACOSX) const CefRect kExpectedRectLI[] = { CefRect(8, 8, 584, 74), // LI00 CefRect(28, 103, 564, 18), // LI01 CefRect(28, 121, 564, 18), // LI02 CefRect(28, 139, 564, 18), // LI03 CefRect(28, 157, 564, 18), // LI04 CefRect(28, 175, 564, 18), // LI05 CefRect(28, 193, 564, 18), // LI06 CefRect(28, 211, 564, 18), // LI07 CefRect(28, 229, 564, 23), // LI08 CefRect(28, 252, 564, 26), // LI09 CefRect(18, 291, 360, 21), // LI10 }; #else #error "Unsupported platform" #endif // defined(OS_WIN) // bounding client rects for edit box and navigate button #if defined(OS_WIN) const CefRect kEditBoxRect(412, 245, 60, 22); const CefRect kNavigateButtonRect(360, 271, 140, 22); const CefRect kSelectRect(467, 22, 75, 20); const CefRect kExpandedSelectRect(463, 42, 81, 342); const CefRect kDropDivRect(8, 332, 52, 52); const CefRect kDragDivRect(71, 342, 30, 30); const int kDefaultVerticalScrollbarWidth = 17; const int kVerticalScrollbarWidth = GetSystemMetrics(SM_CXVSCROLL); #elif defined(OS_MACOSX) const CefRect kEditBoxRect(442, 251, 46, 16); const CefRect kNavigateButtonRect(375, 275, 130, 20); const CefRect kSelectRect(461, 21, 87, 26); const CefRect kExpandedSelectRect(463, 42, 75, 286); const CefRect kDropDivRect(9, 330, 52, 52); const CefRect kDragDivRect(60, 330, 52, 52); #elif defined(OS_LINUX) const CefRect kEditBoxRect(434, 246, 60, 20); const CefRect kNavigateButtonRect(380, 271, 140, 22); const CefRect kSelectRect(467, 22, 75, 20); const CefRect kExpandedSelectRect(463, 42, 81, 342); const CefRect kDropDivRect(8, 332, 52, 52); const CefRect kDragDivRect(71, 342, 30, 30); const int kDefaultVerticalScrollbarWidth = 14; const int kVerticalScrollbarWidth = 14; #else #error "Unsupported platform" #endif // defined(OS_WIN) // word to be written into edit box const char kKeyTestWord[] = "done"; #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 const unsigned int kNativeKeyTestCodes[] = { XK_d, XK_o, XK_n, XK_e }; const unsigned int kNativeKeyEscape = XK_Escape; #elif defined(OS_MACOSX) // 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 const unsigned int kNativeKeyTestCodes[] = { kVK_ANSI_D, kVK_ANSI_O, kVK_ANSI_N, kVK_ANSI_E }; const unsigned int kNativeKeyEscape = kVK_Escape; #endif #if defined(OS_MACOSX) || 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, // 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, // 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, // 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), event_count_(0), event_total_(1), started_(false) { } // 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 OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { 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; } } bool OnQuery(CefRefPtr browser, CefRefPtr frame, int64 query_id, const CefString& request, bool persistent, CefRefPtr callback) override { EXPECT_TRUE(browser.get()); if (!started()) return false; const std::string& messageStr = request; switch(test_type_) { case OSR_TEST_FOCUS: EXPECT_STREQ(messageStr.c_str(), "osrfocus"); DestroySucceededTestSoon(); break; case OSR_TEST_CLICK_LEFT: EXPECT_STREQ(messageStr.c_str(), "osrclick0"); DestroySucceededTestSoon(); break; case OSR_TEST_MOUSE_MOVE: EXPECT_STREQ(messageStr.c_str(), "osrmousemove"); DestroySucceededTestSoon(); break; case OSR_TEST_DRAG_DROP_DROP: EXPECT_STREQ(messageStr.c_str(), "osrdrop"); DestroySucceededTestSoon(); break; default: // Intentionally left blank break; } callback->Success(""); return true; } // CefClient methods, providing handlers CefRefPtr GetFocusHandler() override { return this; } CefRefPtr GetRenderHandler() override { return this; } CefRefPtr GetContextMenuHandler() override { return this; } CefRefPtr GetRequestHandler() override { return this; } 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 NULL; } // CefRenderHandler methods bool GetViewRect(CefRefPtr browser, CefRect& rect) override { if (test_type_ == OSR_TEST_RESIZE && started()) { rect = CefRect(0, 0, kOsrWidth * 2, kOsrHeight * 2); return true; } rect = CefRect(0, 0, kOsrWidth, kOsrHeight); return true; } 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 = GetExpectedRect(4); 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 (show && started()) { switch (test_type_) { case OSR_TEST_POPUP_SHOW: if (!succeeded()) { EXPECT_TRUE(show); DestroySucceededTestSoon(); } break; default: break; } } if (!show && started()) { 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 (started()) { switch (test_type_) { case OSR_TEST_POPUP_SIZE: EXPECT_EQ(kExpandedSelectRect.x, rect.x); EXPECT_EQ(kExpandedSelectRect.y, rect.y); EXPECT_EQ(kExpandedSelectRect.width, rect.width); EXPECT_EQ(kExpandedSelectRect.height, rect.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); EXPECT_EQ(expanded_select_rect.width, width); EXPECT_EQ(expanded_select_rect.height, 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(*(reinterpret_cast(buffer)), 0xffff8080); 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(*(reinterpret_cast(buffer)), 0x7f7f0000U); DestroySucceededTestSoon(); } break; case OSR_TEST_FOCUS: if (StartTest()) { // body.onfocus will make LI00 red browser->GetHost()->SendFocusEvent(true); } break; case OSR_TEST_CURSOR: if (StartTest()) { // make mouse leave first CefMouseEvent mouse_event; mouse_event.x = 0; mouse_event.y = 0; mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, true); // enter mouse in the LI2 element having hand cursor const CefRect& expected_rect = GetExpectedRect(2); 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 = GetExpectedRect(3); 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 = GetExpectedRect(4); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_RIGHT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_RIGHT, true, 1); } break; case OSR_TEST_CLICK_LEFT: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetExpectedRect(0); mouse_event.x = MiddleX(expected_rect); mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); } break; case OSR_TEST_RESIZE: if (StartTest()) { browser->GetHost()->WasResized(); // There may be some partial repaints before the full repaint. } else if (IsFullRepaint(dirtyRects[0], width, height)) { EXPECT_EQ(GetScaledInt(kOsrWidth) * 2, width); EXPECT_EQ(GetScaledInt(kOsrHeight) * 2, height); EXPECT_EQ(dirtyRects.size(), 1U); DestroySucceededTestSoon(); } break; case OSR_TEST_INVALIDATE: { if (StartTest()) { browser->GetHost()->Invalidate(PET_VIEW); } else { EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_EQ(dirtyRects[0], GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight))); DestroySucceededTestSoon(); } break; } case OSR_TEST_KEY_EVENTS: if (StartTest()) { // click inside edit box CefMouseEvent mouse_event; mouse_event.x = MiddleX(kEditBoxRect); mouse_event.y = MiddleY(kEditBoxRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); // 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_MACOSX) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]); #else #error "Unsupported platform" #endif } // click button to navigate mouse_event.x = MiddleX(kNavigateButtonRect); mouse_event.y = MiddleY(kNavigateButtonRect); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); } break; case OSR_TEST_TOOLTIP: if (StartTest()) { CefMouseEvent mouse_event; const CefRect& expected_rect = GetExpectedRect(10); 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_SCROLLING: { static const int deltaY = 10; if (StartTest()) { // scroll down once CefMouseEvent mouse_event; const CefRect& expected_rect = GetExpectedRect(0); 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: if (StartTest()) { ExpandDropDown(); // Wait for the first popup paint to occur } else if (type == PET_POPUP) { CefMouseEvent mouse_event; mouse_event.x = 1; mouse_event.y = 1; mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); } break; case OSR_TEST_POPUP_HIDE_ON_SCROLL: if (StartTest()) { ExpandDropDown(); // Wait for the first popup paint to occur } else 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: if (StartTest()) { ExpandDropDown(); // Wait for the first popup paint to occur } else if (type == PET_POPUP) { browser->GetHost()->SendFocusEvent(false); } break; case OSR_TEST_POPUP_HIDE_ON_ESC: if (StartTest()) { ExpandDropDown(); // Wait for the first popup paint to occur } else if (type == PET_POPUP) { #if defined(OS_WIN) SendKeyEvent(browser, VK_ESCAPE); #elif defined(OS_MACOSX) || defined(OS_LINUX) SendKeyEvent(browser, kNativeKeyEscape, VKEY_ESCAPE); #else #error "Unsupported platform" #endif } break; case OSR_TEST_POPUP_SHOW: case OSR_TEST_POPUP_SIZE: if (StartTest()) { ExpandDropDown(); } break; case OSR_TEST_POPUP_PAINT: if (StartTest()) { ExpandDropDown(); } else if (type == PET_POPUP) { EXPECT_EQ(dirtyRects.size(), 1U); const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, expanded_select_rect.width, expanded_select_rect.height)); // first pixel of border #if defined(OS_MACOSX) EXPECT_EQ(0xff5d99d6, *(reinterpret_cast(buffer))); #elif defined(OS_LINUX) || defined(OS_WIN) if (scale_factor_ == 1.0) { EXPECT_EQ(0xff6497ea, *(reinterpret_cast(buffer))); } else { EXPECT_EQ(0xff4d90fe, *(reinterpret_cast(buffer))); } #else #error "Unsupported platform" #endif EXPECT_EQ(expanded_select_rect.width, width); EXPECT_EQ(expanded_select_rect.height, height); DestroySucceededTestSoon(); } break; case OSR_TEST_POPUP_SCROLL_INSIDE: { static enum {NotStarted, Started, Scrolled} scroll_inside_state = NotStarted; if (StartTest()) { ExpandDropDown(); scroll_inside_state = Started; } else 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); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, expanded_select_rect.width, expanded_select_rect.height)); DestroySucceededTestSoon(); } } } 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; mouse_event.x = MiddleX(kDragDivRect); mouse_event.y = MiddleY(kDragDivRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); // click on the element to drag mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1); // move the mouse to start dragging mouse_event.x -= 5; mouse_event.y -= 5; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } } break; case OSR_TEST_IME_COMMIT_TEXT: { // trigger the IME Set Composition event if (StartTest()) { // click inside edit box so that text could be entered CefMouseEvent mouse_event; mouse_event.x = MiddleX(kEditBoxRect); mouse_event.y = MiddleY(kEditBoxRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); 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_MACOSX) || 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); // click button to navigate mouse_event.x = MiddleX(kNavigateButtonRect); mouse_event.y = MiddleY(kNavigateButtonRect); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); } } break; case OSR_TEST_IME_FINISH_COMPOSITION: { // trigger the IME Set Composition event if (StartTest()) { // click inside edit box so that text could be entered CefMouseEvent mouse_event; mouse_event.x = MiddleX(kEditBoxRect); mouse_event.y = MiddleY(kEditBoxRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1); 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_MACOSX) || 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); // click button to navigate mouse_event.x = MiddleX(kNavigateButtonRect); mouse_event.y = MiddleY(kNavigateButtonRect); browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1); } } break; case OSR_TEST_IME_CANCEL_COMPOSITION: { // trigger the IME Set Composition event if (StartTest()) { // click inside edit box so that text could be entered CefMouseEvent mouse_event; mouse_event.x = MiddleX(kEditBoxRect); mouse_event.y = MiddleY(kEditBoxRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); // Add some input keys to edit box CefString markedText("か"); 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(); // click button to navigate and verify mouse_event.x = MiddleX(kNavigateButtonRect); mouse_event.y = MiddleY(kNavigateButtonRect); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); } } break; case OSR_TEST_IME_SET_COMPOSITION: { // trigger the IME Set Composition event if (StartTest()) { // click inside edit box so that text could be entered CefMouseEvent mouse_event; mouse_event.x = MiddleX(kEditBoxRect); mouse_event.y = MiddleY(kEditBoxRect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); // Now set some intermediate text composition CefString markedText("か"); 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); } } break; default: break; } } bool OnSetFocus(CefRefPtr browser, FocusSource source) override { 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 OnCursorChange(CefRefPtr browser, CefCursorHandle cursor, CursorType type, const CefCursorInfo& custom_cursor_info) override { if (test_type_ == OSR_TEST_CURSOR && started()) { EXPECT_EQ(CT_HAND, type); EXPECT_EQ(NULL, custom_cursor_info.buffer); DestroySucceededTestSoon(); } } 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, 0); EXPECT_EQ(range.to, 1); 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()) { 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; ev.x = MiddleX(kDragDivRect) - 5; ev.y = MiddleY(kDragDivRect) - 5; ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; browser->GetHost()->DragTargetDragEnter(data, ev, allowed_ops); ev.x = MiddleX(kDropDivRect); ev.y = MiddleY(kDropDivRect); 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 (test_type_ == OSR_TEST_DRAG_DROP_UPDATE_CURSOR && started()) { if (operation != DRAG_OPERATION_NONE) { browser->GetHost()->DragSourceEndedAt(MiddleX(kDropDivRect), MiddleY(kDropDivRect), 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; ev.x = MiddleX(kDropDivRect); ev.y = MiddleY(kDropDivRect); 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(); } } 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 = GetExpectedRect(4); 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(); } } // OSRTestHandler functions void CreateOSRBrowser(const CefString& url) { CefWindowInfo windowInfo; CefBrowserSettings settings; const bool transparent = (test_type_ == OSR_TEST_TRANSPARENCY); #if defined(OS_WIN) windowInfo.SetAsWindowless(GetDesktopWindow(), transparent); #elif defined(OS_MACOSX) // 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(), transparent); else windowInfo.SetAsWindowless(kNullWindowHandle, transparent); #elif defined(OS_LINUX) windowInfo.SetAsWindowless(kNullWindowHandle, transparent); #else #error "Unsupported platform" #endif CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, NULL); } CefRect GetScaledRect(const CefRect& rect) const { return client::LogicalToDevice(rect, scale_factor_); } int GetScaledInt(int value) const { return client::LogicalToDevice(value, scale_factor_); } CefRect GetExpectedRect(int index) { CefRect rect = kExpectedRectLI[index]; #if defined(OS_WIN) || defined(OS_LINUX) // Adjust the rect to include system vertical scrollbar width. rect.width += kDefaultVerticalScrollbarWidth - kVerticalScrollbarWidth; #elif !defined(OS_MACOSX) #error "Unsupported platform" #endif return rect; } static bool IsFullRepaint(const CefRect& rc, int width, int height) { return rc.width == width && rc.height == height; } static bool IsBackgroundInBuffer(const uint32* buffer, size_t size, uint32 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; } void DestroySucceededTestSoon() { if (succeeded()) return; if (++event_count_ == event_total_) CefPostTask(TID_UI, base::Bind(&OSRTestHandler::DestroyTest, this)); } void DestroyTest() override { // Always get the OnSetFocus call for the initial navigation. EXPECT_TRUE(got_navigation_focus_event_); if (test_type_ == OSR_TEST_FOCUS || (test_type_ >= OSR_TEST_POPUP_FIRST && test_type_ <= OSR_TEST_POPUP_LAST)) { // SetFocus is called by the system when we explicitly set the focus and // when popups are dismissed. EXPECT_TRUE(got_system_focus_event_); } else { EXPECT_FALSE(got_system_focus_event_); } RoutingTestHandler::DestroyTest(); } void ExpandDropDown() { GetBrowser()->GetHost()->SendFocusEvent(true); CefMouseEvent mouse_event; mouse_event.x = MiddleX(kSelectRect); mouse_event.y = MiddleY(kSelectRect); mouse_event.modifiers = 0; GetBrowser()->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); } void SendKeyEvent(CefRefPtr browser, #if defined(OS_LINUX) || defined(OS_MACOSX) 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_MACOSX) 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() { return started_; } // true if the exit point was reached, even the result is not // the expected one bool succeeded() { return (event_count_ == event_total_); } // 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: OSRTestType test_type_; float scale_factor_; int event_count_; int event_total_; bool started_; TrackCallback got_update_cursor_; TrackCallback got_navigation_focus_event_; TrackCallback got_system_focus_event_; 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->succeeded());\ 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(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(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);