--- /dev/null
+// 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 <Carbon/Carbon.h> // For character codes.
+#include "tests/ceftests/os_rendering_unittest_mac.h"
+#elif defined(OS_LINUX)
+#include <X11/keysym.h>
+#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, 334);
+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, 79, 334);
+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<CefBrowser> browser) override {
+ if (test_type_ == OSR_TEST_IS_WINDOWLESS) {
+ EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled());
+ DestroySucceededTestSoon();
+ }
+ RoutingTestHandler::OnAfterCreated(browser);
+ }
+
+ void OnLoadEnd(CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> 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<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ int64 query_id,
+ const CefString& request,
+ bool persistent,
+ CefRefPtr<Callback> 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<CefFocusHandler> GetFocusHandler() override {
+ return this;
+ }
+
+ CefRefPtr<CefRenderHandler> GetRenderHandler() override {
+ return this;
+ }
+
+ CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override {
+ return this;
+ }
+
+ CefRefPtr<CefRequestHandler> GetRequestHandler() override {
+ return this;
+ }
+
+ CefRefPtr<CefResourceHandler> GetResourceHandler(
+ CefRefPtr<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ CefRefPtr<CefRequest> request) override {
+ std::string url = request->GetURL();
+
+ if (url.find(kTestUrl) == 0) {
+ // Show the osr test contents
+ CefRefPtr<CefStreamReader> stream =
+ client::GetBinaryResourceReader("osr_test.html");
+ return new CefStreamResourceHandler("text/html", stream);
+ }
+
+ return NULL;
+ }
+
+ // CefRenderHandler methods
+ bool GetViewRect(CefRefPtr<CefBrowser> 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<CefBrowser> 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<CefBrowser> 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<CefBrowser> 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<CefBrowser> 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);
+ if (ExpectComputedPopupSize()) {
+ EXPECT_EQ(kExpandedSelectRect.width, rect.width);
+ EXPECT_EQ(kExpandedSelectRect.height, rect.height);
+ } else {
+ EXPECT_GT(rect.width, kExpandedSelectRect.width);
+ EXPECT_GT(rect.height, kExpandedSelectRect.height);
+ }
+ DestroySucceededTestSoon();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void OnPaint(CefRefPtr<CefBrowser> 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<const uint32*>(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<const uint32*>(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<const uint32*>(buffer)));
+ 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(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);
+ }
+
+ // first pixel of border
+#if defined(OS_MACOSX)
+ EXPECT_EQ(0xff5d99d6, *(reinterpret_cast<const uint32*>(buffer)));
+#elif defined(OS_LINUX) || defined(OS_WIN)
+ EXPECT_EQ(0xff6497ea, *(reinterpret_cast<const uint32*>(buffer)));
+#else
+#error "Unsupported platform"
+#endif
+ 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 {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(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);
+ }
+ 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<int>(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<CefCompositionUnderline> underlines;
+
+ // Use a thin black underline by default.
+ CefRange range(0, static_cast<int>(markedText.length()));
+ cef_composition_underline_t line = {
+ range, 0xFF000000, 0, false
+ };
+ underlines.push_back(line);
+
+ CefRange replacement_range(0,
+ static_cast<int>(markedText.length()));
+ CefRange selection_range(0, static_cast<int>(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<CefCompositionUnderline> underlines;
+
+ // Use a thin black underline by default.
+ CefRange range(0, static_cast<int>(markedText.length()));
+ cef_composition_underline_t line = {
+ range, 0xFF000000, 0, false
+ };
+ underlines.push_back(line);
+
+ CefRange replacement_range(0,
+ static_cast<int>(markedText.length()));
+ CefRange selection_range(0, static_cast<int>(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<CefBrowser> 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<CefBrowser> 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<CefBrowser> 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<CefBrowser> browser,
+ CefRefPtr<CefDragData> 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<CefDragData> 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<CefBrowser> 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<CefBrowser> 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<CefBrowser> browser,
+ CefRefPtr<CefFrame> frame,
+ CefRefPtr<CefContextMenuParams> params,
+ CefRefPtr<CefMenuModel> 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;
+ }
+
+ bool ExpectComputedPopupSize() const {
+#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
+ // On Windows 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.
+ // On both Windows and Linux 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;
+#else
+ return true;
+#endif
+ }
+
+ 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<CefBrowser> 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<OSRTestHandler> 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);