diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-20 13:27:38 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-20 13:27:38 +0000 |
commit | 3de922f39b244a05cf5ed58699256158048a1b89 (patch) | |
tree | 80f5fd65f420f54afa2daef7d217670812614bb6 | |
parent | a3e41cd4f1ed6879b25a3dcc8730db868df69fd4 (diff) | |
download | chromium_src-3de922f39b244a05cf5ed58699256158048a1b89.zip chromium_src-3de922f39b244a05cf5ed58699256158048a1b89.tar.gz chromium_src-3de922f39b244a05cf5ed58699256158048a1b89.tar.bz2 |
Import TestRunner library into chromium.
The reasons for the move are:
- it's actually a blink embedder, so it can't use blink/wtf types
- it can't use base either
- we want to replace CppBoundClass with gin::Wrappable, not possible in blink
In the first step, this is mostly a 1:1 copy (except for include paths).
Follow-up CLs will move the test plugin and layout tests helpers and
clean up the coding style
BUG=324658
R=abarth@chromium.org, maruel@chromium.org, torne@chromium.org, jam@chromium.org
TBR=torne@chromium.org
Review URL: https://codereview.chromium.org/110533009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242095 0039d316-1c4b-4281-b951-d872f2087c98
91 files changed, 16744 insertions, 24 deletions
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index e33fa80..9bb1471 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -28,6 +28,13 @@ _EXCLUDED_PATHS = ( r"^gpu[\\\/]config[\\\/].*_list_json\.cc$", ) +# TestRunner library is temporarily excluded from pan-project checks until +# it's transitioned to chromium coding style. +_TESTRUNNER_PATHS = ( + r"^content[\\\/]shell[\\\/]renderer[\\\/]test_runner[\\\/].*", + r"^content[\\\/]shell[\\\/]common[\\\/]test_runner[\\\/].*", +) + # Fragment of a regular expression that matches C++ and Objective-C++ # implementation files. _IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$' @@ -999,7 +1006,8 @@ def _CommonChecks(input_api, output_api): """Checks common to both upload and commit.""" results = [] results.extend(input_api.canned_checks.PanProjectChecks( - input_api, output_api, excluded_paths=_EXCLUDED_PATHS)) + input_api, output_api, + excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS)) results.extend(_CheckAuthorizedAuthor(input_api, output_api)) results.extend( _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api)) diff --git a/android_webview/tools/webview_licenses.py b/android_webview/tools/webview_licenses.py index 9702465..3a1911b 100755 --- a/android_webview/tools/webview_licenses.py +++ b/android_webview/tools/webview_licenses.py @@ -103,6 +103,8 @@ def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): excluded_dirs_list.append('chrome/app/resources') # This is a test output directory excluded_dirs_list.append('chrome/tools/test/reference_build') + # blink style copy right headers. + excluded_dirs_list.append('content/shell/renderer/test_runner') # This is tests directory, doesn't exist in the snapshot excluded_dirs_list.append('content/test/data') # This is a tests directory that doesn't exist in the shipped product. diff --git a/content/content_shell.gypi b/content/content_shell.gypi index 0031964..9d5ded0 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi @@ -46,7 +46,9 @@ '../net/net.gyp:net', '../net/net.gyp:net_resources', '../skia/skia.gyp:skia', - '../third_party/WebKit/public/blink_test_runner.gyp:blink_test_runner', + '../third_party/WebKit/public/blink.gyp:blink', + '../third_party/WebKit/public/blink_test_runner.gyp:blink_test_runner_resources', + '../third_party/WebKit/public/blink_test_runner.gyp:blink_test_support', '../ui/events/events.gyp:events_base', '../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx_geometry', @@ -63,6 +65,7 @@ '..', ], 'sources': [ + 'public/test/layouttest_support.h', 'shell/android/shell_jni_registrar.cc', 'shell/android/shell_jni_registrar.h', 'shell/android/shell_manager.cc', @@ -151,6 +154,8 @@ 'shell/common/shell_switches.h', 'shell/common/shell_test_configuration.cc', 'shell/common/shell_test_configuration.h', + 'shell/common/test_runner/WebPreferences.cpp', + 'shell/common/test_runner/WebPreferences.h', 'shell/common/webkit_test_helpers.cc', 'shell/common/webkit_test_helpers.h', 'shell/geolocation/shell_access_token_store.cc', @@ -165,8 +170,83 @@ 'shell/renderer/shell_render_process_observer.h', 'shell/renderer/shell_render_view_observer.cc', 'shell/renderer/shell_render_view_observer.h', + 'shell/renderer/test_runner/AccessibilityController.cpp', + 'shell/renderer/test_runner/AccessibilityController.h', + 'shell/renderer/test_runner/CppBoundClass.cpp', + 'shell/renderer/test_runner/CppBoundClass.h', + 'shell/renderer/test_runner/CppVariant.cpp', + 'shell/renderer/test_runner/CppVariant.h', + 'shell/renderer/test_runner/EventSender.cpp', + 'shell/renderer/test_runner/EventSender.h', + 'shell/renderer/test_runner/GamepadController.cpp', + 'shell/renderer/test_runner/GamepadController.h', + 'shell/renderer/test_runner/KeyCodeMapping.cpp', + 'shell/renderer/test_runner/KeyCodeMapping.h', + 'shell/renderer/test_runner/MockColorChooser.cpp', + 'shell/renderer/test_runner/MockColorChooser.h', + 'shell/renderer/test_runner/MockConstraints.cpp', + 'shell/renderer/test_runner/MockConstraints.h', + 'shell/renderer/test_runner/MockGrammarCheck.cpp', + 'shell/renderer/test_runner/MockGrammarCheck.h', + 'shell/renderer/test_runner/MockSpellCheck.cpp', + 'shell/renderer/test_runner/MockSpellCheck.h', + 'shell/renderer/test_runner/MockWebAudioDevice.cpp', + 'shell/renderer/test_runner/MockWebAudioDevice.h', + 'shell/renderer/test_runner/MockWebMediaStreamCenter.cpp', + 'shell/renderer/test_runner/MockWebMediaStreamCenter.h', + 'shell/renderer/test_runner/MockWebMIDIAccessor.cpp', + 'shell/renderer/test_runner/MockWebMIDIAccessor.h', + 'shell/renderer/test_runner/MockWebRTCDataChannelHandler.cpp', + 'shell/renderer/test_runner/MockWebRTCDataChannelHandler.h', + 'shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.cpp', + 'shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h', + 'shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.cpp', + 'shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h', + 'shell/renderer/test_runner/MockWebSpeechInputController.cpp', + 'shell/renderer/test_runner/MockWebSpeechInputController.h', + 'shell/renderer/test_runner/MockWebSpeechRecognizer.cpp', + 'shell/renderer/test_runner/MockWebSpeechRecognizer.h', + 'shell/renderer/test_runner/NotificationPresenter.cpp', + 'shell/renderer/test_runner/NotificationPresenter.h', + 'shell/renderer/test_runner/SpellCheckClient.cpp', + 'shell/renderer/test_runner/SpellCheckClient.h', + 'shell/renderer/test_runner/TestCommon.cpp', + 'shell/renderer/test_runner/TestCommon.h', + 'shell/renderer/test_runner/TestInterfaces.cpp', + 'shell/renderer/test_runner/TestInterfaces.h', + 'shell/renderer/test_runner/TestPlugin.cpp', + 'shell/renderer/test_runner/TestPlugin.h', + 'shell/renderer/test_runner/TestRunner.cpp', + 'shell/renderer/test_runner/TestRunner.h', + 'shell/renderer/test_runner/TextInputController.cpp', + 'shell/renderer/test_runner/TextInputController.h', + 'shell/renderer/test_runner/WebAXObjectProxy.cpp', + 'shell/renderer/test_runner/WebAXObjectProxy.h', + 'shell/renderer/test_runner/WebFrameTestProxy.h', + 'shell/renderer/test_runner/WebPermissions.cpp', + 'shell/renderer/test_runner/WebPermissions.h', + 'shell/renderer/test_runner/WebScopedPtr.h', + 'shell/renderer/test_runner/WebTask.cpp', + 'shell/renderer/test_runner/WebTask.h', + 'shell/renderer/test_runner/WebTestDelegate.h', + 'shell/renderer/test_runner/WebTestInterfaces.cpp', + 'shell/renderer/test_runner/WebTestInterfaces.h', + 'shell/renderer/test_runner/WebTestProxy.cpp', + 'shell/renderer/test_runner/WebTestProxy.h', + 'shell/renderer/test_runner/WebTestRunner.h', + 'shell/renderer/test_runner/WebTestThemeControlWin.cpp', + 'shell/renderer/test_runner/WebTestThemeControlWin.h', + 'shell/renderer/test_runner/WebTestThemeEngineMac.h', + 'shell/renderer/test_runner/WebTestThemeEngineMac.mm', + 'shell/renderer/test_runner/WebTestThemeEngineMock.cpp', + 'shell/renderer/test_runner/WebTestThemeEngineMock.h', + 'shell/renderer/test_runner/WebTestThemeEngineWin.cpp', + 'shell/renderer/test_runner/WebTestThemeEngineWin.h', + 'shell/renderer/test_runner/WebUserMediaClientMock.cpp', + 'shell/renderer/test_runner/WebUserMediaClientMock.h', 'shell/renderer/webkit_test_runner.cc', 'shell/renderer/webkit_test_runner.h', + 'test/layouttest_support.cc', ], 'msvs_settings': { 'VCLinkerTool': { @@ -197,6 +277,10 @@ }, # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], + }, { # OS!="win" + 'sources/': [ + ['exclude', 'Win\\.cpp$'], + ], }], # OS=="win" ['OS=="linux"', { 'dependencies': [ diff --git a/content/content_tests.gypi b/content/content_tests.gypi index ffd2ec1..ec1edb39 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -40,7 +40,6 @@ 'public/test/download_test_observer.h', 'public/test/fake_speech_recognition_manager.cc', 'public/test/fake_speech_recognition_manager.h', - 'public/test/layouttest_support.h', 'public/test/mock_download_item.cc', 'public/test/mock_download_item.h', 'public/test/mock_download_manager.cc', @@ -113,7 +112,6 @@ 'gpu/gpu_idirect3d9_mock_win.h', 'test/content_test_suite.cc', 'test/content_test_suite.h', - 'test/layouttest_support.cc', 'test/mock_google_streaming_server.cc', 'test/mock_google_streaming_server.h', 'test/mock_keyboard.cc', @@ -226,7 +224,6 @@ '../ppapi/ppapi_internal.gyp:ppapi_shared', '../ppapi/ppapi_internal.gyp:ppapi_unittest_shared', '../third_party/WebKit/public/blink.gyp:blink', - '../third_party/WebKit/public/blink_test_runner.gyp:blink_test_runner', '../ui/surface/surface.gyp:surface', '../webkit/child/webkit_child.gyp:webkit_child', '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu', @@ -1214,6 +1211,8 @@ 'type': 'static_library', 'dependencies': [ 'test_support_content', + # TODO(jochen): remove this. + '../third_party/WebKit/public/blink_test_runner.gyp:blink_test_support', ], 'include_dirs': [ '..', diff --git a/content/shell/DEPS b/content/shell/DEPS index 3cc264e..1d7524c 100644 --- a/content/shell/DEPS +++ b/content/shell/DEPS @@ -20,9 +20,6 @@ include_rules = [ "+ui/aura", "+ui/views", - # For WebTestRunner library - "+third_party/WebKit/public/testing", - "+components/breakpad", ] diff --git a/content/shell/common/test_runner/OWNERS b/content/shell/common/test_runner/OWNERS new file mode 100644 index 0000000..cf00f71 --- /dev/null +++ b/content/shell/common/test_runner/OWNERS @@ -0,0 +1 @@ +abarth@chromium.org diff --git a/content/shell/common/test_runner/WebPreferences.cpp b/content/shell/common/test_runner/WebPreferences.cpp new file mode 100644 index 0000000..a8952e3 --- /dev/null +++ b/content/shell/common/test_runner/WebPreferences.cpp @@ -0,0 +1,51 @@ +// Copyright 2013 The Chromium 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 "content/shell/common/test_runner/WebPreferences.h" + +using namespace blink; + +namespace WebTestRunner { + +void WebPreferences::reset() +{ + defaultFontSize = 16; + minimumFontSize = 0; + DOMPasteAllowed = true; + XSSAuditorEnabled = false; + allowDisplayOfInsecureContent = true; + allowFileAccessFromFileURLs = true; + allowRunningOfInsecureContent = true; + defaultTextEncodingName = WebString::fromUTF8("ISO-8859-1"); + experimentalWebGLEnabled = false; + experimentalCSSRegionsEnabled = true; + experimentalCSSGridLayoutEnabled = true; + javaEnabled = false; + javaScriptCanAccessClipboard = true; + javaScriptCanOpenWindowsAutomatically = true; + supportsMultipleWindows = true; + javaScriptEnabled = true; + loadsImagesAutomatically = true; + offlineWebApplicationCacheEnabled = true; + pluginsEnabled = true; + caretBrowsingEnabled = false; + + // Allow those layout tests running as local files, i.e. under + // LayoutTests/http/tests/local, to access http server. + allowUniversalAccessFromFileURLs = true; + +#ifdef __APPLE__ + editingBehavior = WebSettings::EditingBehaviorMac; +#else + editingBehavior = WebSettings::EditingBehaviorWin; +#endif + + tabsToLinks = false; + hyperlinkAuditingEnabled = false; + cssCustomFilterEnabled = false; + shouldRespectImageOrientation = false; + asynchronousSpellCheckingEnabled = false; +} + +} diff --git a/content/shell/common/test_runner/WebPreferences.h b/content/shell/common/test_runner/WebPreferences.h new file mode 100644 index 0000000..dbcc923 --- /dev/null +++ b/content/shell/common/test_runner/WebPreferences.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebPreferences_h +#define WebPreferences_h + +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebSettings.h" + +namespace blink { +class WebView; +} + +namespace WebTestRunner { + +struct WebPreferences { + int defaultFontSize; + int minimumFontSize; + bool DOMPasteAllowed; + bool XSSAuditorEnabled; + bool allowDisplayOfInsecureContent; + bool allowFileAccessFromFileURLs; + bool allowRunningOfInsecureContent; + bool authorAndUserStylesEnabled; + blink::WebString defaultTextEncodingName; + bool experimentalWebGLEnabled; + bool experimentalCSSRegionsEnabled; + bool experimentalCSSGridLayoutEnabled; + bool javaEnabled; + bool javaScriptCanAccessClipboard; + bool javaScriptCanOpenWindowsAutomatically; + bool supportsMultipleWindows; + bool javaScriptEnabled; + bool loadsImagesAutomatically; + bool offlineWebApplicationCacheEnabled; + bool pluginsEnabled; + bool allowUniversalAccessFromFileURLs; + blink::WebSettings::EditingBehavior editingBehavior; + bool tabsToLinks; + bool hyperlinkAuditingEnabled; + bool caretBrowsingEnabled; + bool cssCustomFilterEnabled; + bool shouldRespectImageOrientation; + bool asynchronousSpellCheckingEnabled; + + WebPreferences() { reset(); } + void reset(); +}; + +} + +#endif // WebPreferences_h diff --git a/content/shell/common/webkit_test_helpers.cc b/content/shell/common/webkit_test_helpers.cc index 3d3212e..131b453 100644 --- a/content/shell/common/webkit_test_helpers.cc +++ b/content/shell/common/webkit_test_helpers.cc @@ -10,7 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "content/public/common/content_switches.h" #include "content/shell/common/shell_switches.h" -#include "third_party/WebKit/public/testing/WebPreferences.h" +#include "content/shell/common/test_runner/WebPreferences.h" #include "webkit/common/webpreferences.h" namespace content { diff --git a/content/shell/renderer/shell_content_renderer_client.cc b/content/shell/renderer/shell_content_renderer_client.cc index 58f0222..e7046ba 100644 --- a/content/shell/renderer/shell_content_renderer_client.cc +++ b/content/shell/renderer/shell_content_renderer_client.cc @@ -15,12 +15,12 @@ #include "content/shell/renderer/shell_render_frame_observer.h" #include "content/shell/renderer/shell_render_process_observer.h" #include "content/shell/renderer/shell_render_view_observer.h" +#include "content/shell/renderer/test_runner/WebTestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" #include "content/shell/renderer/webkit_test_runner.h" #include "content/test/mock_webclipboard_impl.h" #include "third_party/WebKit/public/platform/WebMediaStreamCenter.h" -#include "third_party/WebKit/public/testing/WebTestInterfaces.h" -#include "third_party/WebKit/public/testing/WebTestProxy.h" -#include "third_party/WebKit/public/testing/WebTestRunner.h" #include "third_party/WebKit/public/web/WebPluginParams.h" #include "third_party/WebKit/public/web/WebView.h" #include "v8/include/v8.h" diff --git a/content/shell/renderer/shell_render_frame_observer.cc b/content/shell/renderer/shell_render_frame_observer.cc index a7c367d..9d6c251 100644 --- a/content/shell/renderer/shell_render_frame_observer.cc +++ b/content/shell/renderer/shell_render_frame_observer.cc @@ -7,8 +7,8 @@ #include "base/command_line.h" #include "content/shell/common/shell_switches.h" #include "content/shell/renderer/shell_render_process_observer.h" -#include "third_party/WebKit/public/testing/WebTestInterfaces.h" -#include "third_party/WebKit/public/testing/WebTestRunner.h" +#include "content/shell/renderer/test_runner/WebTestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" #include "third_party/WebKit/public/web/WebFrame.h" namespace content { diff --git a/content/shell/renderer/shell_render_process_observer.cc b/content/shell/renderer/shell_render_process_observer.cc index c4fc640..457ccc4 100644 --- a/content/shell/renderer/shell_render_process_observer.cc +++ b/content/shell/renderer/shell_render_process_observer.cc @@ -13,8 +13,8 @@ #include "content/shell/common/shell_switches.h" #include "content/shell/renderer/gc_extension.h" #include "content/shell/renderer/shell_content_renderer_client.h" +#include "content/shell/renderer/test_runner/WebTestInterfaces.h" #include "content/shell/renderer/webkit_test_runner.h" -#include "third_party/WebKit/public/testing/WebTestInterfaces.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebView.h" #include "webkit/glue/webkit_glue.h" diff --git a/content/shell/renderer/test_runner/AccessibilityController.cpp b/content/shell/renderer/test_runner/AccessibilityController.cpp new file mode 100644 index 0000000..9225ede --- /dev/null +++ b/content/shell/renderer/test_runner/AccessibilityController.cpp @@ -0,0 +1,180 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/AccessibilityController.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/web/WebAXObject.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebNode.h" +#include "third_party/WebKit/public/web/WebView.h" + +using namespace blink; + +namespace WebTestRunner { + +AccessibilityController::AccessibilityController() + : m_logAccessibilityEvents(false) +{ + + bindMethod("logAccessibilityEvents", &AccessibilityController::logAccessibilityEventsCallback); + bindMethod("addNotificationListener", &AccessibilityController::addNotificationListenerCallback); + bindMethod("removeNotificationListener", &AccessibilityController::removeNotificationListenerCallback); + + bindProperty("focusedElement", &AccessibilityController::focusedElementGetterCallback); + bindProperty("rootElement", &AccessibilityController::rootElementGetterCallback); + + bindMethod("accessibleElementById", &AccessibilityController::accessibleElementByIdGetterCallback); + + bindFallbackMethod(&AccessibilityController::fallbackCallback); +} + +void AccessibilityController::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + WebAXObject::enableAccessibility(); + WebAXObject::enableInlineTextBoxAccessibility(); + CppBoundClass::bindToJavascript(frame, classname); +} + +void AccessibilityController::reset() +{ + m_rootElement = WebAXObject(); + m_focusedElement = WebAXObject(); + m_elements.clear(); + m_notificationCallbacks.clear(); + + m_logAccessibilityEvents = false; +} + +void AccessibilityController::setFocusedElement(const WebAXObject& focusedElement) +{ + m_focusedElement = focusedElement; +} + +WebAXObjectProxy* AccessibilityController::getFocusedElement() +{ + if (m_focusedElement.isNull()) + m_focusedElement = m_webView->accessibilityObject(); + return m_elements.getOrCreate(m_focusedElement); +} + +WebAXObjectProxy* AccessibilityController::getRootElement() +{ + if (m_rootElement.isNull()) + m_rootElement = m_webView->accessibilityObject(); + return m_elements.createRoot(m_rootElement); +} + +WebAXObjectProxy* AccessibilityController::findAccessibleElementByIdRecursive(const WebAXObject& obj, const WebString& id) +{ + if (obj.isNull() || obj.isDetached()) + return 0; + + WebNode node = obj.node(); + if (!node.isNull() && node.isElementNode()) { + WebElement element = node.to<WebElement>(); + element.getAttribute("id"); + if (element.getAttribute("id") == id) + return m_elements.getOrCreate(obj); + } + + unsigned childCount = obj.childCount(); + for (unsigned i = 0; i < childCount; i++) { + if (WebAXObjectProxy* result = findAccessibleElementByIdRecursive(obj.childAt(i), id)) + return result; + } + + return 0; +} + +WebAXObjectProxy* AccessibilityController::getAccessibleElementById(const std::string& id) +{ + if (m_rootElement.isNull()) + m_rootElement = m_webView->accessibilityObject(); + + if (!m_rootElement.updateBackingStoreAndCheckValidity()) + return 0; + + return findAccessibleElementByIdRecursive(m_rootElement, WebString::fromUTF8(id.c_str())); +} + +bool AccessibilityController::shouldLogAccessibilityEvents() +{ + return m_logAccessibilityEvents; +} + +void AccessibilityController::notificationReceived(const blink::WebAXObject& target, const char* notificationName) +{ + // Call notification listeners on the element. + WebAXObjectProxy* element = m_elements.getOrCreate(target); + element->notificationReceived(notificationName); + + // Call global notification listeners. + size_t callbackCount = m_notificationCallbacks.size(); + for (size_t i = 0; i < callbackCount; i++) { + CppVariant arguments[2]; + arguments[0].set(*element->getAsCppVariant()); + arguments[1].set(notificationName); + CppVariant invokeResult; + m_notificationCallbacks[i].invokeDefault(arguments, 2, invokeResult); + } +} + +void AccessibilityController::logAccessibilityEventsCallback(const CppArgumentList&, CppVariant* result) +{ + m_logAccessibilityEvents = true; + result->setNull(); +} + +void AccessibilityController::addNotificationListenerCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + m_notificationCallbacks.push_back(arguments[0]); + result->setNull(); +} + +void AccessibilityController::removeNotificationListenerCallback(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void AccessibilityController::focusedElementGetterCallback(CppVariant* result) +{ + result->set(*(getFocusedElement()->getAsCppVariant())); +} + +void AccessibilityController::rootElementGetterCallback(CppVariant* result) +{ + result->set(*(getRootElement()->getAsCppVariant())); +} + +void AccessibilityController::accessibleElementByIdGetterCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + std::string id = arguments[0].toString(); + WebAXObjectProxy* foundElement = getAccessibleElementById(id); + if (!foundElement) + return; + + result->set(*(foundElement->getAsCppVariant())); +} + +void AccessibilityController::fallbackCallback(const CppArgumentList&, CppVariant* result) +{ + m_delegate->printMessage("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on AccessibilityController\n"); + result->setNull(); +} + +} diff --git a/content/shell/renderer/test_runner/AccessibilityController.h b/content/shell/renderer/test_runner/AccessibilityController.h new file mode 100644 index 0000000..42f7403 --- /dev/null +++ b/content/shell/renderer/test_runner/AccessibilityController.h @@ -0,0 +1,70 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AccessibilityController_h +#define AccessibilityController_h + +#include "content/shell/renderer/test_runner/CppBoundClass.h" +#include "content/shell/renderer/test_runner/WebAXObjectProxy.h" + +namespace blink { +class WebAXObject; +class WebFrame; +class WebView; +} + +namespace WebTestRunner { + +class WebTestDelegate; + +class AccessibilityController : public CppBoundClass { +public: + AccessibilityController(); + + // Shadow to include accessibility initialization. + void bindToJavascript(blink::WebFrame*, const blink::WebString& classname); + void reset(); + + void setFocusedElement(const blink::WebAXObject&); + WebAXObjectProxy* getFocusedElement(); + WebAXObjectProxy* getRootElement(); + WebAXObjectProxy* getAccessibleElementById(const std::string& id); + + bool shouldLogAccessibilityEvents(); + + void notificationReceived(const blink::WebAXObject& target, const char* notificationName); + + void setDelegate(WebTestDelegate* delegate) { m_delegate = delegate; } + void setWebView(blink::WebView* webView) { m_webView = webView; } + +private: + // If true, will log all accessibility notifications. + bool m_logAccessibilityEvents; + + // Bound methods and properties + void logAccessibilityEventsCallback(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + void addNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void removeNotificationListenerCallback(const CppArgumentList&, CppVariant*); + + void focusedElementGetterCallback(CppVariant*); + void rootElementGetterCallback(CppVariant*); + void accessibleElementByIdGetterCallback(const CppArgumentList&, CppVariant*); + + WebAXObjectProxy* findAccessibleElementByIdRecursive(const blink::WebAXObject&, const blink::WebString& id); + + blink::WebAXObject m_focusedElement; + blink::WebAXObject m_rootElement; + + WebAXObjectProxyList m_elements; + + std::vector<CppVariant> m_notificationCallbacks; + + WebTestDelegate* m_delegate; + blink::WebView* m_webView; +}; + +} + +#endif // AccessibilityController_h diff --git a/content/shell/renderer/test_runner/CppBoundClass.cpp b/content/shell/renderer/test_runner/CppBoundClass.cpp new file mode 100644 index 0000000..e8d603f --- /dev/null +++ b/content/shell/renderer/test_runner/CppBoundClass.cpp @@ -0,0 +1,362 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file contains definitions for CppBoundClass + +// Here's the control flow of a JS method getting forwarded to a class. +// - Something calls our NPObject with a function like "Invoke". +// - CppNPObject's static invoke() function forwards it to its attached +// CppBoundClass's invoke() method. +// - CppBoundClass has then overridden invoke() to look up the function +// name in its internal map of methods, and then calls the appropriate +// method. + +#include "content/shell/renderer/test_runner/CppBoundClass.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebFrame.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { +public: + CppVariantPropertyCallback(CppVariant* value) : m_value(value) { } + + virtual bool getValue(CppVariant* value) + { + value->set(*m_value); + return true; + } + + virtual bool setValue(const CppVariant& value) + { + m_value->set(value); + return true; + } + +private: + CppVariant* m_value; +}; + +class GetterPropertyCallback : public CppBoundClass::PropertyCallback { +public: + GetterPropertyCallback(WebScopedPtr<CppBoundClass::GetterCallback> callback) + : m_callback(callback) + { + } + + virtual bool getValue(CppVariant* value) + { + m_callback->run(value); + return true; + } + + virtual bool setValue(const CppVariant& value) { return false; } + +private: + WebScopedPtr<CppBoundClass::GetterCallback> m_callback; +}; + +} + +// Our special NPObject type. We extend an NPObject with a pointer to a +// CppBoundClass, which is just a C++ interface that we forward all NPObject +// callbacks to. +struct CppNPObject { + NPObject parent; // This must be the first field in the struct. + CppBoundClass* boundClass; + + // + // All following objects and functions are static, and just used to interface + // with NPObject/NPClass. + // + + // An NPClass associates static functions of CppNPObject with the + // function pointers used by the JS runtime. + static NPClass npClass; + + // Allocate a new NPObject with the specified class. + static NPObject* allocate(NPP, NPClass*); + + // Free an object. + static void deallocate(NPObject*); + + // Returns true if the C++ class associated with this NPObject exposes the + // given property. Called by the JS runtime. + static bool hasProperty(NPObject*, NPIdentifier); + + // Returns true if the C++ class associated with this NPObject exposes the + // given method. Called by the JS runtime. + static bool hasMethod(NPObject*, NPIdentifier); + + // If the given method is exposed by the C++ class associated with this + // NPObject, invokes it with the given arguments and returns a result. Otherwise, + // returns "undefined" (in the JavaScript sense). Called by the JS runtime. + static bool invoke(NPObject*, NPIdentifier, + const NPVariant* arguments, uint32_t argumentCount, + NPVariant* result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, returns its value. Otherwise, returns "undefined" (in the + // JavaScript sense). Called by the JS runtime. + static bool getProperty(NPObject*, NPIdentifier, NPVariant* result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, sets its value. Otherwise, does nothing. Called by the JS + // runtime. + static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value); +}; + +// Build CppNPObject's static function pointers into an NPClass, for use +// in constructing NPObjects for the C++ classes. +NPClass CppNPObject::npClass = { + NP_CLASS_STRUCT_VERSION, + CppNPObject::allocate, + CppNPObject::deallocate, + /* NPInvalidateFunctionPtr */ 0, + CppNPObject::hasMethod, + CppNPObject::invoke, + /* NPInvokeDefaultFunctionPtr */ 0, + CppNPObject::hasProperty, + CppNPObject::getProperty, + CppNPObject::setProperty, + /* NPRemovePropertyFunctionPtr */ 0 +}; + +NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) +{ + CppNPObject* obj = new CppNPObject; + // obj->parent will be initialized by the NPObject code calling this. + obj->boundClass = 0; + return &obj->parent; +} + +void CppNPObject::deallocate(NPObject* npObj) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + delete obj; +} + +bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->hasMethod(ident); +} + +bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->hasProperty(ident); +} + +bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident, + const NPVariant* arguments, uint32_t argumentCount, + NPVariant* result) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->invoke(ident, arguments, argumentCount, result); +} + +bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->getProperty(ident, result); +} + +bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->setProperty(ident, value); +} + +CppBoundClass::~CppBoundClass() +{ + for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i) + delete i->second; + + for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i) + delete i->second; + + // Unregister ourselves if we were bound to a frame. + if (m_boundToFrame) + WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant)); +} + +bool CppBoundClass::hasMethod(NPIdentifier ident) const +{ + return m_methods.find(ident) != m_methods.end(); +} + +bool CppBoundClass::hasProperty(NPIdentifier ident) const +{ + return m_properties.find(ident) != m_properties.end(); +} + +bool CppBoundClass::invoke(NPIdentifier ident, + const NPVariant* arguments, + size_t argumentCount, + NPVariant* result) { + MethodList::const_iterator end = m_methods.end(); + MethodList::const_iterator method = m_methods.find(ident); + Callback* callback; + if (method == end) { + if (!m_fallbackCallback.get()) { + VOID_TO_NPVARIANT(*result); + return false; + } + callback = m_fallbackCallback.get(); + } else + callback = (*method).second; + + // Build a CppArgumentList argument vector from the NPVariants coming in. + CppArgumentList cppArguments(argumentCount); + for (size_t i = 0; i < argumentCount; i++) + cppArguments[i].set(arguments[i]); + + CppVariant cppResult; + callback->run(cppArguments, &cppResult); + + cppResult.copyToNPVariant(result); + return true; +} + +bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const +{ + PropertyList::const_iterator callback = m_properties.find(ident); + if (callback == m_properties.end()) { + VOID_TO_NPVARIANT(*result); + return false; + } + + CppVariant cppValue; + if (!callback->second->getValue(&cppValue)) + return false; + cppValue.copyToNPVariant(result); + return true; +} + +bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value) +{ + PropertyList::iterator callback = m_properties.find(ident); + if (callback == m_properties.end()) + return false; + + CppVariant cppValue; + cppValue.set(*value); + return (*callback).second->setValue(cppValue); +} + +void CppBoundClass::bindCallback(const string& name, Callback* callback) +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + MethodList::iterator oldCallback = m_methods.find(ident); + if (oldCallback != m_methods.end()) { + delete oldCallback->second; + if (!callback) { + m_methods.erase(oldCallback); + return; + } + } + + m_methods[ident] = callback; +} + +void CppBoundClass::bindGetterCallback(const string& name, WebScopedPtr<GetterCallback> callback) +{ + PropertyCallback* propertyCallback = callback.get() ? new GetterPropertyCallback(callback) : 0; + bindProperty(name, propertyCallback); +} + +void CppBoundClass::bindProperty(const string& name, CppVariant* prop) +{ + PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0; + bindProperty(name, propertyCallback); +} + +void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback) +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + PropertyList::iterator oldCallback = m_properties.find(ident); + if (oldCallback != m_properties.end()) { + delete oldCallback->second; + if (!callback) { + m_properties.erase(oldCallback); + return; + } + } + + m_properties[ident] = callback; +} + +bool CppBoundClass::isMethodRegistered(const string& name) const +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + MethodList::const_iterator callback = m_methods.find(ident); + return callback != m_methods.end(); +} + +CppVariant* CppBoundClass::getAsCppVariant() +{ + if (!m_selfVariant.isObject()) { + // Create an NPObject using our static NPClass. The first argument (a + // plugin's instance handle) is passed through to the allocate function + // directly, and we don't use it, so it's ok to be 0. + NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass); + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + obj->boundClass = this; + m_selfVariant.set(npObj); + WebBindings::releaseObject(npObj); // CppVariant takes the reference. + } + BLINK_ASSERT(m_selfVariant.isObject()); + return &m_selfVariant; +} + +void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + // BindToWindowObject will take its own reference to the NPObject, and clean + // up after itself. It will also (indirectly) register the object with V8, + // so we must remember this so we can unregister it when we're destroyed. + frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()), 0); + m_boundToFrame = true; +} + +} diff --git a/content/shell/renderer/test_runner/CppBoundClass.h b/content/shell/renderer/test_runner/CppBoundClass.h new file mode 100644 index 0000000..f3870fd --- /dev/null +++ b/content/shell/renderer/test_runner/CppBoundClass.h @@ -0,0 +1,251 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + CppBoundClass class: + This base class serves as a parent for C++ classes designed to be bound to + JavaScript objects. + + Subclasses should define the constructor to build the property and method + lists needed to bind this class to a JS object. They should also declare + and define member variables and methods to be exposed to JS through + that object. +*/ + +#ifndef CppBoundClass_h +#define CppBoundClass_h + +#include <map> +#include <vector> + +#include "content/shell/renderer/test_runner/CppVariant.h" +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +namespace blink { +class WebFrame; +class WebString; +} + +namespace WebTestRunner { + +typedef std::vector<CppVariant> CppArgumentList; + +// CppBoundClass lets you map Javascript method calls and property accesses +// directly to C++ method calls and CppVariant* variable access. +class CppBoundClass : public blink::WebNonCopyable { +public: + class PropertyCallback { + public: + virtual ~PropertyCallback() { } + + // Sets |value| to the value of the property. Returns false in case of + // failure. |value| is always non-0. + virtual bool getValue(CppVariant* result) = 0; + + // sets the property value to |value|. Returns false in case of failure. + virtual bool setValue(const CppVariant&) = 0; + }; + + // Callback class for "void function(CppVariant*)" + class GetterCallback { + public: + virtual ~GetterCallback() { } + virtual void run(CppVariant*) = 0; + }; + + // The constructor should call BindMethod, BindProperty, and + // SetFallbackMethod as needed to set up the methods, properties, and + // fallback method. + CppBoundClass() : m_boundToFrame(false) { } + virtual ~CppBoundClass(); + + // Return a CppVariant representing this class, for use with BindProperty(). + // The variant type is guaranteed to be NPVariantType_Object. + CppVariant* getAsCppVariant(); + + // Given a WebFrame, BindToJavascript builds the NPObject that will represent + // the class and binds it to the frame's window under the given name. This + // should generally be called from the WebView delegate's + // WindowObjectCleared(). A class so bound will be accessible to JavaScript + // as window.<classname>. The owner of the CppBoundObject is responsible for + // keeping the object around while the frame is alive, and for destroying it + // afterwards. + void bindToJavascript(blink::WebFrame*, const blink::WebString& classname); + + // Used by a test. Returns true if a method with the specified name exists, + // regardless of whether a fallback is registered. + bool isMethodRegistered(const std::string&) const; + +protected: + // Callback for "void function(const CppArguemntList&, CppVariant*)" + class Callback { + public: + virtual ~Callback() { } + virtual void run(const CppArgumentList&, CppVariant*) = 0; + }; + + // Callback for "void T::method(const CppArguemntList&, CppVariant*)" + template <class T> class MemberCallback : public Callback { + public: + typedef void (T::*MethodType)(const CppArgumentList&, CppVariant*); + MemberCallback(T* object, MethodType method) + : m_object(object) + , m_method(method) { } + virtual ~MemberCallback() { } + + virtual void run(const CppArgumentList& arguments, CppVariant* result) + { + (m_object->*m_method)(arguments, result); + } + + private: + T* m_object; + MethodType m_method; + }; + + // Callback class for "void T::method(CppVariant*)" + template <class T> class MemberGetterCallback : public GetterCallback { + public: + typedef void (T::*MethodType)(CppVariant*); + MemberGetterCallback(T* object, MethodType method) + : m_object(object) + , m_method(method) { } + virtual ~MemberGetterCallback() { } + + virtual void run(CppVariant* result) { (m_object->*m_method)(result); } + + private: + T* m_object; + MethodType m_method; + }; + + // Bind the Javascript method called the string parameter to the C++ method. + void bindCallback(const std::string&, Callback*); + + // A wrapper for bindCallback, to simplify the common case of binding a + // method on the current object. Though not verified here, the method parameter + // must be a method of this CppBoundClass subclass. + template<class T> + void bindMethod(const std::string& name, void (T::*method)(const CppArgumentList&, CppVariant*)) + { + Callback* callback = new MemberCallback<T>(static_cast<T*>(this), method); + bindCallback(name, callback); + } + + // Bind Javascript property |name| to the C++ getter callback |callback|. + // This can be used to create read-only properties. + void bindGetterCallback(const std::string&, WebScopedPtr<GetterCallback>); + + // A wrapper for BindGetterCallback, to simplify the common case of binding a + // property on the current object. Though not verified here, the method parameter + // must be a method of this CppBoundClass subclass. + template<class T> + void bindProperty(const std::string& name, void (T::*method)(CppVariant*)) + { + WebScopedPtr<GetterCallback> callback(new MemberGetterCallback<T>(static_cast<T*>(this), method)); + bindGetterCallback(name, callback); + } + + // Bind the Javascript property called |name| to a CppVariant. + void bindProperty(const std::string&, CppVariant*); + + // Bind Javascript property called |name| to a PropertyCallback. + // CppBoundClass assumes control over the life time of the callback. + void bindProperty(const std::string&, PropertyCallback*); + + // Set the fallback callback, which is called when when a callback is + // invoked that isn't bound. + // If it is 0 (its default value), a JavaScript exception is thrown in + // that case (as normally expected). If non 0, the fallback method is + // invoked and the script continues its execution. + // Passing 0 clears out any existing binding. + // It is used for tests and should probably only be used in such cases + // as it may cause unexpected behaviors (a JavaScript object with a + // fallback always returns true when checked for a method's + // existence). + void bindFallbackCallback(WebScopedPtr<Callback> fallbackCallback) + { + m_fallbackCallback = fallbackCallback; + } + + // A wrapper for BindFallbackCallback, to simplify the common case of + // binding a method on the current object. Though not verified here, + // |method| must be a method of this CppBoundClass subclass. + // Passing 0 for |method| clears out any existing binding. + template<class T> + void bindFallbackMethod(void (T::*method)(const CppArgumentList&, CppVariant*)) + { + if (method) + bindFallbackCallback(WebScopedPtr<Callback>(new MemberCallback<T>(static_cast<T*>(this), method))); + else + bindFallbackCallback(WebScopedPtr<Callback>()); + } + + // Some fields are protected because some tests depend on accessing them, + // but otherwise they should be considered private. + + typedef std::map<NPIdentifier, PropertyCallback*> PropertyList; + typedef std::map<NPIdentifier, Callback*> MethodList; + // These maps associate names with property and method pointers to be + // exposed to JavaScript. + PropertyList m_properties; + MethodList m_methods; + + // The callback gets invoked when a call is made to an nonexistent method. + WebScopedPtr<Callback> m_fallbackCallback; + +private: + // NPObject callbacks. + friend struct CppNPObject; + bool hasMethod(NPIdentifier) const; + bool invoke(NPIdentifier, const NPVariant* args, size_t argCount, + NPVariant* result); + bool hasProperty(NPIdentifier) const; + bool getProperty(NPIdentifier, NPVariant* result) const; + bool setProperty(NPIdentifier, const NPVariant*); + + // A lazily-initialized CppVariant representing this class. We retain 1 + // reference to this object, and it is released on deletion. + CppVariant m_selfVariant; + + // True if our np_object has been bound to a WebFrame, in which case it must + // be unregistered with V8 when we delete it. + bool m_boundToFrame; +}; + +} + +#endif // CppBoundClass_h diff --git a/content/shell/renderer/test_runner/CppVariant.cpp b/content/shell/renderer/test_runner/CppVariant.cpp new file mode 100644 index 0000000..47936df --- /dev/null +++ b/content/shell/renderer/test_runner/CppVariant.cpp @@ -0,0 +1,296 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/CppVariant.h" + +#include <limits> +#include "content/shell/renderer/test_runner/TestCommon.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +CppVariant::CppVariant() +{ + type = NPVariantType_Null; +} + +// Note that Set() performs a deep copy, which is necessary to safely +// call FreeData() on the value in the destructor. +CppVariant::CppVariant(const CppVariant& original) +{ + type = NPVariantType_Null; + set(original); +} + +// See comment for copy constructor, above. +CppVariant& CppVariant::operator=(const CppVariant& original) +{ + if (&original != this) + set(original); + return *this; +} + +CppVariant::~CppVariant() +{ + freeData(); +} + +void CppVariant::freeData() +{ + WebBindings::releaseVariantValue(this); +} + +bool CppVariant::isEqual(const CppVariant& other) const +{ + if (type != other.type) + return false; + + switch (type) { + case NPVariantType_Bool: + return (value.boolValue == other.value.boolValue); + case NPVariantType_Int32: + return (value.intValue == other.value.intValue); + case NPVariantType_Double: + return (value.doubleValue == other.value.doubleValue); + case NPVariantType_String: { + const NPString *this_value = &value.stringValue; + const NPString *other_value = &other.value.stringValue; + uint32_t len = this_value->UTF8Length; + return len == other_value->UTF8Length + && !strncmp(this_value->UTF8Characters, + other_value->UTF8Characters, len); + } + case NPVariantType_Null: + case NPVariantType_Void: + return true; + case NPVariantType_Object: { + NPObject* thisValue = value.objectValue; + NPObject* otherValue = other.value.objectValue; + return thisValue->_class == otherValue->_class + && thisValue->referenceCount == otherValue->referenceCount; + } + } + return false; +} + +void CppVariant::copyToNPVariant(NPVariant* result) const +{ + result->type = type; + switch (type) { + case NPVariantType_Bool: + result->value.boolValue = value.boolValue; + break; + case NPVariantType_Int32: + result->value.intValue = value.intValue; + break; + case NPVariantType_Double: + result->value.doubleValue = value.doubleValue; + break; + case NPVariantType_String: + WebBindings::initializeVariantWithStringCopy(result, &value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + // Nothing to set. + break; + case NPVariantType_Object: + result->type = NPVariantType_Object; + result->value.objectValue = WebBindings::retainObject(value.objectValue); + break; + } +} + +void CppVariant::set(const NPVariant& newValue) +{ + freeData(); + switch (newValue.type) { + case NPVariantType_Bool: + set(newValue.value.boolValue); + break; + case NPVariantType_Int32: + set(newValue.value.intValue); + break; + case NPVariantType_Double: + set(newValue.value.doubleValue); + break; + case NPVariantType_String: + set(newValue.value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + type = newValue.type; + break; + case NPVariantType_Object: + set(newValue.value.objectValue); + break; + } +} + +void CppVariant::setNull() +{ + freeData(); + type = NPVariantType_Null; +} + +void CppVariant::set(bool newValue) +{ + freeData(); + type = NPVariantType_Bool; + value.boolValue = newValue; +} + +void CppVariant::set(int32_t newValue) +{ + freeData(); + type = NPVariantType_Int32; + value.intValue = newValue; +} + +void CppVariant::set(double newValue) +{ + freeData(); + type = NPVariantType_Double; + value.doubleValue = newValue; +} + +// The newValue must be a null-terminated string. +void CppVariant::set(const char* newValue) +{ + freeData(); + type = NPVariantType_String; + NPString newString = {newValue, + static_cast<uint32_t>(strlen(newValue))}; + WebBindings::initializeVariantWithStringCopy(this, &newString); +} + +void CppVariant::set(const string& newValue) +{ + freeData(); + type = NPVariantType_String; + NPString newString = {newValue.data(), + static_cast<uint32_t>(newValue.size())}; + WebBindings::initializeVariantWithStringCopy(this, &newString); +} + +void CppVariant::set(const NPString& newValue) +{ + freeData(); + type = NPVariantType_String; + WebBindings::initializeVariantWithStringCopy(this, &newValue); +} + +void CppVariant::set(NPObject* newValue) +{ + freeData(); + type = NPVariantType_Object; + value.objectValue = WebBindings::retainObject(newValue); +} + +string CppVariant::toString() const +{ + BLINK_ASSERT(isString()); + return string(value.stringValue.UTF8Characters, + value.stringValue.UTF8Length); +} + +int32_t CppVariant::toInt32() const +{ + if (isInt32()) + return value.intValue; + if (isDouble()) + return static_cast<int32_t>(value.doubleValue); + BLINK_ASSERT_NOT_REACHED(); + return 0; +} + +double CppVariant::toDouble() const +{ + if (isInt32()) + return static_cast<double>(value.intValue); + if (isDouble()) + return value.doubleValue; + BLINK_ASSERT_NOT_REACHED(); + return 0; +} + +bool CppVariant::toBoolean() const +{ + BLINK_ASSERT(isBool()); + return value.boolValue; +} + +vector<string> CppVariant::toStringVector() const +{ + + BLINK_ASSERT(isObject()); + vector<string> stringVector; + NPObject* npValue = value.objectValue; + NPIdentifier lengthId = WebBindings::getStringIdentifier("length"); + + if (!WebBindings::hasProperty(0, npValue, lengthId)) + return stringVector; + + NPVariant lengthValue; + if (!WebBindings::getProperty(0, npValue, lengthId, &lengthValue)) + return stringVector; + + int length = 0; + // The length is a double in some cases. + if (NPVARIANT_IS_DOUBLE(lengthValue)) + length = static_cast<int>(NPVARIANT_TO_DOUBLE(lengthValue)); + else if (NPVARIANT_IS_INT32(lengthValue)) + length = NPVARIANT_TO_INT32(lengthValue); + WebBindings::releaseVariantValue(&lengthValue); + + // For sanity, only allow 100 items. + length = min(100, length); + for (int i = 0; i < length; ++i) { + // Get each of the items. + char indexInChar[20]; // Enough size to store 32-bit integer + snprintf(indexInChar, 20, "%d", i); + string index(indexInChar); + NPIdentifier indexId = WebBindings::getStringIdentifier(index.c_str()); + if (!WebBindings::hasProperty(0, npValue, indexId)) + continue; + NPVariant indexValue; + if (!WebBindings::getProperty(0, npValue, indexId, &indexValue)) + continue; + if (NPVARIANT_IS_STRING(indexValue)) { + string item(NPVARIANT_TO_STRING(indexValue).UTF8Characters, + NPVARIANT_TO_STRING(indexValue).UTF8Length); + stringVector.push_back(item); + } + WebBindings::releaseVariantValue(&indexValue); + } + return stringVector; +} + +bool CppVariant::invoke(const string& method, const CppVariant* arguments, + uint32_t argumentCount, CppVariant& result) const +{ + BLINK_ASSERT(isObject()); + NPIdentifier methodName = WebBindings::getStringIdentifier(method.c_str()); + NPObject* npObject = value.objectValue; + if (!WebBindings::hasMethod(0, npObject, methodName)) + return false; + NPVariant r; + bool status = WebBindings::invoke(0, npObject, methodName, arguments, argumentCount, &r); + result.set(r); + return status; +} + +bool CppVariant::invokeDefault(const CppVariant* arguments, uint32_t argumentCount, + CppVariant& result) const +{ + BLINK_ASSERT(isObject()); + NPObject* npObject = value.objectValue; + NPVariant r; + bool status = WebBindings::invokeDefault(0, npObject, arguments, argumentCount, &r); + result.set(r); + return status; +} + +} diff --git a/content/shell/renderer/test_runner/CppVariant.h b/content/shell/renderer/test_runner/CppVariant.h new file mode 100644 index 0000000..1aa6c6d --- /dev/null +++ b/content/shell/renderer/test_runner/CppVariant.h @@ -0,0 +1,119 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + This file contains the declaration for CppVariant, a type used by C++ classes + that are to be bound to JavaScript objects. + + CppVariant exists primarily as an interface between C++ callers and the + corresponding NPVariant type. CppVariant also provides a number of + convenience constructors and accessors, so that the NPVariantType values + don't need to be exposed, and a destructor to free any memory allocated for + string values. +*/ + +#ifndef CppVariant_h +#define CppVariant_h + +#include <string> +#include <vector> + +#include "third_party/WebKit/public/web/WebBindings.h" + +namespace WebTestRunner { + +class CppVariant : public NPVariant { +public: + CppVariant(); + ~CppVariant(); + void setNull(); + void set(bool); + void set(int32_t); + void set(double); + + // Note that setting a CppVariant to a string value involves copying the + // string data, which must be freed with a call to freeData() when the + // CppVariant is set to a different value or is no longer needed. Normally + // this is handled by the other set() methods and by the destructor. + void set(const char*); // Must be a null-terminated string. + void set(const std::string&); + void set(const NPString&); + void set(const NPVariant&); + + // Note that setting a CppVariant to an NPObject involves ref-counting + // the actual object. freeData() should only be called if the CppVariant + // is no longer needed. The other set() methods handle this internally. + // Also, the object's NPClass is expected to be a static object: neither + // the NP runtime nor CppVariant will ever free it. + void set(NPObject*_value); + + // These three methods all perform deep copies of any string data. This + // allows local CppVariants to be released by the destructor without + // corrupting their sources. In performance-critical code, or when strings + // are very long, avoid creating new CppVariants. + // In case of NPObject as the data, the copying involves ref-counting + // as opposed to deep-copying. The ref-counting ensures that sources don't + // get corrupted when the copies get destroyed. + void copyToNPVariant(NPVariant* result) const; + CppVariant& operator=(const CppVariant& original); + CppVariant(const CppVariant& original); + + // Calls NPN_ReleaseVariantValue, which frees any string data + // held by the object and sets its type to null. + // In case of NPObject, the NPN_ReleaseVariantValue decrements + // the ref-count (releases when ref-count becomes 0) + void freeData(); + + // Compares this CppVariant's type and value to another's. They must be + // identical in both type and value to be considered equal. For string and + // object types, a deep comparison is performed; that is, the contents of the + // strings, or the classes and refcounts of the objects, must be the same, + // but they need not be the same pointers. + bool isEqual(const CppVariant&) const; + + // The value of a CppVariant may be read directly from its NPVariant (but + // should only be set using one of the set() methods above). Although the + // type of a CppVariant is likewise public, it can be accessed through these + // functions rather than directly if a caller wishes to avoid dependence on + // the NPVariantType values. + bool isBool() const { return (type == NPVariantType_Bool); } + bool isInt32() const { return (type == NPVariantType_Int32); } + bool isDouble() const { return (type == NPVariantType_Double); } + bool isNumber() const { return (isInt32() || isDouble()); } + bool isString() const { return (type == NPVariantType_String); } + bool isVoid() const { return (type == NPVariantType_Void); } + bool isNull() const { return (type == NPVariantType_Null); } + bool isEmpty() const { return (isVoid() || isNull()); } + bool isObject() const { return (type == NPVariantType_Object); } + + // Converters. The CppVariant must be of a type convertible to these values. + // For example, toInt32() works only if isNumber() is true. + std::string toString() const; + int32_t toInt32() const; + double toDouble() const; + bool toBoolean() const; + // Returns a vector of strings for the specified argument. This is useful + // for converting a JavaScript array of strings into a vector of strings. + std::vector<std::string> toStringVector() const; + + // Invoke method of the given name on an object with the supplied arguments. + // The first argument should be the object on which the method is to be + // invoked. Returns whether the method was successfully invoked. If the + // method was invoked successfully, any return value is stored in the + // CppVariant specified by result. + bool invoke(const std::string&, const CppVariant* arguments, + uint32_t argumentCount, CppVariant& result) const; + + // Invoke an object's default method with the supplied arguments. + // The first argument should be the object on which the method is to be + // invoked. Returns whether the method was successfully invoked. If the + // method was invoked successfully, any return value is stored in the + // CppVariant specified by result. + bool invokeDefault(const CppVariant* arguments, + uint32_t argumentCount, CppVariant& result) const; +}; + +} + +#endif // CppVariant_h diff --git a/content/shell/renderer/test_runner/EventSender.cpp b/content/shell/renderer/test_runner/EventSender.cpp new file mode 100644 index 0000000..5d7eec8 --- /dev/null +++ b/content/shell/renderer/test_runner/EventSender.cpp @@ -0,0 +1,1439 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains the definition for EventSender. +// +// Some notes about drag and drop handling: +// Windows drag and drop goes through a system call to doDragDrop. At that +// point, program control is given to Windows which then periodically makes +// callbacks into the webview. This won't work for layout tests, so instead, +// we queue up all the mouse move and mouse up events. When the test tries to +// start a drag (by calling EvenSendingController::doDragDrop), we take the +// events in the queue and replay them. +// The behavior of queuing events and replaying them can be disabled by a +// layout test by setting eventSender.dragMode to false. + +#include "content/shell/renderer/test_runner/EventSender.h" + +#include <deque> + +#include "content/shell/renderer/test_runner/KeyCodeMapping.h" +#include "content/shell/renderer/test_runner/MockSpellCheck.h" +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "third_party/WebKit/public/platform/WebDragData.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" +#include "third_party/WebKit/public/web/WebTouchPoint.h" +#include "third_party/WebKit/public/web/WebView.h" + +#ifdef WIN32 +#include "third_party/WebKit/public/web/win/WebInputEventFactory.h" +#elif __APPLE__ +#include "third_party/WebKit/public/web/mac/WebInputEventFactory.h" +#elif defined(ANDROID) +#include "third_party/WebKit/public/web/android/WebInputEventFactory.h" +#elif defined(TOOLKIT_GTK) +#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" +#endif + +// FIXME: layout before each event? + +using namespace std; +using namespace blink; + +namespace WebTestRunner { + +WebPoint EventSender::lastMousePos; +WebMouseEvent::Button EventSender::pressedButton = WebMouseEvent::ButtonNone; +WebMouseEvent::Button EventSender::lastButtonType = WebMouseEvent::ButtonNone; + +namespace { + +struct SavedEvent { + enum SavedEventType { + Unspecified, + MouseUp, + MouseMove, + LeapForward + }; + + SavedEventType type; + WebMouseEvent::Button buttonType; // For MouseUp. + WebPoint pos; // For MouseMove. + int milliseconds; // For LeapForward. + + SavedEvent() + : type(Unspecified) + , buttonType(WebMouseEvent::ButtonNone) + , milliseconds(0) { } +}; + +WebDragData currentDragData; +WebDragOperation currentDragEffect; +WebDragOperationsMask currentDragEffectsAllowed; +bool replayingSavedEvents = false; +deque<SavedEvent> mouseEventQueue; +int touchModifiers; +vector<WebTouchPoint> touchPoints; + +// Time and place of the last mouse up event. +double lastClickTimeSec = 0; +WebPoint lastClickPos; +int clickCount = 0; + +// maximum distance (in space and time) for a mouse click +// to register as a double or triple click +const double multipleClickTimeSec = 1; +const int multipleClickRadiusPixels = 5; + +// How much we should scroll per event - the value here is chosen to +// match the WebKit impl and layout test results. +const float scrollbarPixelsPerTick = 40.0f; + +inline bool outsideMultiClickRadius(const WebPoint& a, const WebPoint& b) +{ + return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) > + multipleClickRadiusPixels * multipleClickRadiusPixels; +} + +// Used to offset the time the event hander things an event happened. This is +// done so tests can run without a delay, but bypass checks that are time +// dependent (e.g., dragging has a timeout vs selection). +uint32 timeOffsetMs = 0; + +double getCurrentEventTimeSec(WebTestDelegate* delegate) +{ + return (delegate->getCurrentTimeInMillisecond() + timeOffsetMs) / 1000.0; +} + +void advanceEventTime(int32_t deltaMs) +{ + timeOffsetMs += deltaMs; +} + +void initMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b, const WebPoint& pos, WebMouseEvent* e, double ts) +{ + e->type = t; + e->button = b; + e->modifiers = 0; + e->x = pos.x; + e->y = pos.y; + e->globalX = pos.x; + e->globalY = pos.y; + e->timeStampSeconds = ts; + e->clickCount = clickCount; +} + +void applyKeyModifier(const string& modifierName, WebInputEvent* event) +{ + const char* characters = modifierName.c_str(); + if (!strcmp(characters, "ctrlKey") +#ifndef __APPLE__ + || !strcmp(characters, "addSelectionKey") +#endif + ) { + event->modifiers |= WebInputEvent::ControlKey; + } else if (!strcmp(characters, "shiftKey") || !strcmp(characters, "rangeSelectionKey")) + event->modifiers |= WebInputEvent::ShiftKey; + else if (!strcmp(characters, "altKey")) { + event->modifiers |= WebInputEvent::AltKey; +#ifdef __APPLE__ + } else if (!strcmp(characters, "metaKey") || !strcmp(characters, "addSelectionKey")) { + event->modifiers |= WebInputEvent::MetaKey; +#else + } else if (!strcmp(characters, "metaKey")) { + event->modifiers |= WebInputEvent::MetaKey; +#endif + } else if (!strcmp(characters, "autoRepeat")) { + event->modifiers |= WebInputEvent::IsAutoRepeat; + } +} + +void applyKeyModifiers(const CppVariant* argument, WebInputEvent* event) +{ + if (argument->isObject()) { + vector<string> modifiers = argument->toStringVector(); + for (vector<string>::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) + applyKeyModifier(*i, event); + } else if (argument->isString()) { + applyKeyModifier(argument->toString(), event); + } +} + +// Get the edit command corresponding to a keyboard event. +// Returns true if the specified event corresponds to an edit command, the name +// of the edit command will be stored in |*name|. +bool getEditCommand(const WebKeyboardEvent& event, string* name) +{ +#ifdef __APPLE__ + // We only cares about Left,Right,Up,Down keys with Command or Command+Shift + // modifiers. These key events correspond to some special movement and + // selection editor commands, and was supposed to be handled in + // WebKit/chromium/src/EditorClientImpl.cpp. But these keys will be marked + // as system key, which prevents them from being handled. Thus they must be + // handled specially. + if ((event.modifiers & ~WebKeyboardEvent::ShiftKey) != WebKeyboardEvent::MetaKey) + return false; + + switch (event.windowsKeyCode) { + case VKEY_LEFT: + *name = "MoveToBeginningOfLine"; + break; + case VKEY_RIGHT: + *name = "MoveToEndOfLine"; + break; + case VKEY_UP: + *name = "MoveToBeginningOfDocument"; + break; + case VKEY_DOWN: + *name = "MoveToEndOfDocument"; + break; + default: + return false; + } + + if (event.modifiers & WebKeyboardEvent::ShiftKey) + name->append("AndModifySelection"); + + return true; +#else + return false; +#endif +} + +// Key event location code introduced in DOM Level 3. +// See also: http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents +enum KeyLocationCode { + DOMKeyLocationStandard = 0x00, + DOMKeyLocationLeft = 0x01, + DOMKeyLocationRight = 0x02, + DOMKeyLocationNumpad = 0x03 +}; + +} + +EventSender::EventSender(TestInterfaces* interfaces) + : m_testInterfaces(interfaces) + , m_delegate(0) +{ + // Initialize the map that associates methods of this class with the names + // they will use when called by JavaScript. The actual binding of those + // names to their methods will be done by calling bindToJavaScript() (defined + // by CppBoundClass, the parent to EventSender). + bindMethod("addTouchPoint", &EventSender::addTouchPoint); + bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles); + bindMethod("cancelTouchPoint", &EventSender::cancelTouchPoint); + bindMethod("clearKillRing", &EventSender::clearKillRing); + bindMethod("clearTouchPoints", &EventSender::clearTouchPoints); + bindMethod("contextClick", &EventSender::contextClick); + bindMethod("continuousMouseScrollBy", &EventSender::continuousMouseScrollBy); + bindMethod("dispatchMessage", &EventSender::dispatchMessage); + bindMethod("dumpFilenameBeingDragged", &EventSender::dumpFilenameBeingDragged); + bindMethod("enableDOMUIEventLogging", &EventSender::enableDOMUIEventLogging); + bindMethod("fireKeyboardEventsToElement", &EventSender::fireKeyboardEventsToElement); + bindMethod("keyDown", &EventSender::keyDown); + bindMethod("leapForward", &EventSender::leapForward); + bindMethod("mouseDown", &EventSender::mouseDown); + bindMethod("mouseMoveTo", &EventSender::mouseMoveTo); + bindMethod("mouseScrollBy", &EventSender::mouseScrollBy); + bindMethod("mouseUp", &EventSender::mouseUp); + bindMethod("mouseDragBegin", &EventSender::mouseDragBegin); + bindMethod("mouseDragEnd", &EventSender::mouseDragEnd); + bindMethod("mouseMomentumBegin", &EventSender::mouseMomentumBegin); + bindMethod("mouseMomentumScrollBy", &EventSender::mouseMomentumScrollBy); + bindMethod("mouseMomentumEnd", &EventSender::mouseMomentumEnd); + bindMethod("releaseTouchPoint", &EventSender::releaseTouchPoint); + bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick); + bindMethod("scheduleAsynchronousKeyDown", &EventSender::scheduleAsynchronousKeyDown); + bindMethod("setTouchModifier", &EventSender::setTouchModifier); + bindMethod("textZoomIn", &EventSender::textZoomIn); + bindMethod("textZoomOut", &EventSender::textZoomOut); + bindMethod("touchCancel", &EventSender::touchCancel); + bindMethod("touchEnd", &EventSender::touchEnd); + bindMethod("touchMove", &EventSender::touchMove); + bindMethod("touchStart", &EventSender::touchStart); + bindMethod("updateTouchPoint", &EventSender::updateTouchPoint); + bindMethod("gestureFlingCancel", &EventSender::gestureFlingCancel); + bindMethod("gestureFlingStart", &EventSender::gestureFlingStart); + bindMethod("gestureScrollBegin", &EventSender::gestureScrollBegin); + bindMethod("gestureScrollEnd", &EventSender::gestureScrollEnd); + bindMethod("gestureScrollFirstPoint", &EventSender::gestureScrollFirstPoint); + bindMethod("gestureScrollUpdate", &EventSender::gestureScrollUpdate); + bindMethod("gestureScrollUpdateWithoutPropagation", &EventSender::gestureScrollUpdateWithoutPropagation); + bindMethod("gestureTap", &EventSender::gestureTap); + bindMethod("gestureTapDown", &EventSender::gestureTapDown); + bindMethod("gestureShowPress", &EventSender::gestureShowPress); + bindMethod("gestureTapCancel", &EventSender::gestureTapCancel); + bindMethod("gestureLongPress", &EventSender::gestureLongPress); + bindMethod("gestureLongTap", &EventSender::gestureLongTap); + bindMethod("gestureTwoFingerTap", &EventSender::gestureTwoFingerTap); + bindMethod("zoomPageIn", &EventSender::zoomPageIn); + bindMethod("zoomPageOut", &EventSender::zoomPageOut); + bindMethod("setPageScaleFactor", &EventSender::setPageScaleFactor); + + bindProperty("forceLayoutOnEvents", &forceLayoutOnEvents); + + // When set to true (the default value), we batch mouse move and mouse up + // events so we can simulate drag & drop. + bindProperty("dragMode", &dragMode); +#ifdef WIN32 + bindProperty("WM_KEYDOWN", &wmKeyDown); + bindProperty("WM_KEYUP", &wmKeyUp); + bindProperty("WM_CHAR", &wmChar); + bindProperty("WM_DEADCHAR", &wmDeadChar); + bindProperty("WM_SYSKEYDOWN", &wmSysKeyDown); + bindProperty("WM_SYSKEYUP", &wmSysKeyUp); + bindProperty("WM_SYSCHAR", &wmSysChar); + bindProperty("WM_SYSDEADCHAR", &wmSysDeadChar); +#endif +} + +EventSender::~EventSender() +{ +} + +void EventSender::setContextMenuData(const WebContextMenuData& contextMenuData) +{ + m_lastContextMenuData = WebScopedPtr<WebContextMenuData>(new WebContextMenuData(contextMenuData)); +} + +void EventSender::reset() +{ + // The test should have finished a drag and the mouse button state. + BLINK_ASSERT(currentDragData.isNull()); + currentDragData.reset(); + currentDragEffect = blink::WebDragOperationNone; + currentDragEffectsAllowed = blink::WebDragOperationNone; + if (webview() && pressedButton != WebMouseEvent::ButtonNone) + webview()->mouseCaptureLost(); + pressedButton = WebMouseEvent::ButtonNone; + dragMode.set(true); + forceLayoutOnEvents.set(true); +#ifdef WIN32 + wmKeyDown.set(WM_KEYDOWN); + wmKeyUp.set(WM_KEYUP); + wmChar.set(WM_CHAR); + wmDeadChar.set(WM_DEADCHAR); + wmSysKeyDown.set(WM_SYSKEYDOWN); + wmSysKeyUp.set(WM_SYSKEYUP); + wmSysChar.set(WM_SYSCHAR); + wmSysDeadChar.set(WM_SYSDEADCHAR); +#endif + lastMousePos = WebPoint(0, 0); + lastClickTimeSec = 0; + lastClickPos = WebPoint(0, 0); + clickCount = 0; + lastButtonType = WebMouseEvent::ButtonNone; + timeOffsetMs = 0; + touchModifiers = 0; + touchPoints.clear(); + m_taskList.revokeAll(); + m_currentGestureLocation = WebPoint(0, 0); + mouseEventQueue.clear(); +} + +void EventSender::doDragDrop(const WebDragData& dragData, WebDragOperationsMask mask) +{ + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + WebPoint clientPoint(event.x, event.y); + WebPoint screenPoint(event.globalX, event.globalY); + currentDragData = dragData; + currentDragEffectsAllowed = mask; + currentDragEffect = webview()->dragTargetDragEnter(dragData, clientPoint, screenPoint, currentDragEffectsAllowed, 0); + + // Finish processing events. + replaySavedEvents(); +} + +void EventSender::dumpFilenameBeingDragged(const CppArgumentList&, CppVariant*) +{ + WebString filename; + WebVector<WebDragData::Item> items = currentDragData.items(); + for (size_t i = 0; i < items.size(); ++i) { + if (items[i].storageType == WebDragData::Item::StorageTypeBinaryData) { + filename = items[i].title; + break; + } + } + m_delegate->printMessage(std::string("Filename being dragged: ") + filename.utf8().data() + "\n"); +} + +WebMouseEvent::Button EventSender::getButtonTypeFromButtonNumber(int buttonCode) +{ + if (!buttonCode) + return WebMouseEvent::ButtonLeft; + if (buttonCode == 2) + return WebMouseEvent::ButtonRight; + return WebMouseEvent::ButtonMiddle; +} + +int EventSender::getButtonNumberFromSingleArg(const CppArgumentList& arguments) +{ + int buttonCode = 0; + if (arguments.size() > 0 && arguments[0].isNumber()) + buttonCode = arguments[0].toInt32(); + return buttonCode; +} + +void EventSender::updateClickCountForButton(WebMouseEvent::Button buttonType) +{ + if ((getCurrentEventTimeSec(m_delegate) - lastClickTimeSec < multipleClickTimeSec) + && (!outsideMultiClickRadius(lastMousePos, lastClickPos)) + && (buttonType == lastButtonType)) + ++clickCount; + else { + clickCount = 1; + lastButtonType = buttonType; + } +} + +// +// Implemented javascript methods. +// + +void EventSender::mouseDown(const CppArgumentList& arguments, CppVariant* result) +{ + if (result) // Could be 0 if invoked asynchronously. + result->setNull(); + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + int buttonNumber = getButtonNumberFromSingleArg(arguments); + BLINK_ASSERT(buttonNumber != -1); + + WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); + + updateClickCountForButton(buttonType); + + WebMouseEvent event; + pressedButton = buttonType; + initMouseEvent(WebInputEvent::MouseDown, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + applyKeyModifiers(&(arguments[1]), &event); + webview()->handleInputEvent(event); +} + +void EventSender::mouseUp(const CppArgumentList& arguments, CppVariant* result) +{ + if (result) // Could be 0 if invoked asynchronously. + result->setNull(); + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + int buttonNumber = getButtonNumberFromSingleArg(arguments); + BLINK_ASSERT(buttonNumber != -1); + + WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); + + if (isDragMode() && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseUp; + savedEvent.buttonType = buttonType; + mouseEventQueue.push_back(savedEvent); + replaySavedEvents(); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseUp, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + applyKeyModifiers(&(arguments[1]), &event); + doMouseUp(event); + } +} + +void EventSender::doMouseUp(const WebMouseEvent& e) +{ + webview()->handleInputEvent(e); + + pressedButton = WebMouseEvent::ButtonNone; + lastClickTimeSec = e.timeStampSeconds; + lastClickPos = lastMousePos; + + // If we're in a drag operation, complete it. + if (currentDragData.isNull()) + return; + + WebPoint clientPoint(e.x, e.y); + WebPoint screenPoint(e.globalX, e.globalY); + finishDragAndDrop(e, webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed, 0)); +} + +void EventSender::finishDragAndDrop(const WebMouseEvent& e, blink::WebDragOperation dragEffect) +{ + WebPoint clientPoint(e.x, e.y); + WebPoint screenPoint(e.globalX, e.globalY); + currentDragEffect = dragEffect; + if (currentDragEffect) + webview()->dragTargetDrop(clientPoint, screenPoint, 0); + else + webview()->dragTargetDragLeave(); + webview()->dragSourceEndedAt(clientPoint, screenPoint, currentDragEffect); + webview()->dragSourceSystemDragEnded(); + + currentDragData.reset(); +} + +void EventSender::mouseMoveTo(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + WebPoint mousePos(arguments[0].toInt32(), arguments[1].toInt32()); + + if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseMove; + savedEvent.pos = mousePos; + mouseEventQueue.push_back(savedEvent); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseMove, pressedButton, mousePos, &event, getCurrentEventTimeSec(m_delegate)); + if (arguments.size() >= 3 && (arguments[2].isObject() || arguments[2].isString())) + applyKeyModifiers(&(arguments[2]), &event); + doMouseMove(event); + } +} + +void EventSender::doMouseMove(const WebMouseEvent& e) +{ + lastMousePos = WebPoint(e.x, e.y); + + webview()->handleInputEvent(e); + + if (pressedButton == WebMouseEvent::ButtonNone || currentDragData.isNull()) + return; + WebPoint clientPoint(e.x, e.y); + WebPoint screenPoint(e.globalX, e.globalY); + currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed, 0); +} + +void EventSender::keyDown(const CppArgumentList& arguments, CppVariant* result) +{ + if (result) + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isString()) + return; + bool generateChar = false; + + // FIXME: I'm not exactly sure how we should convert the string to a key + // event. This seems to work in the cases I tested. + // FIXME: Should we also generate a KEY_UP? + string codeStr = arguments[0].toString(); + + // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when + // Windows uses \r for "Enter". + int code = 0; + int text = 0; + bool needsShiftKeyModifier = false; + if ("\n" == codeStr) { + generateChar = true; + text = code = VKEY_RETURN; + } else if ("rightArrow" == codeStr) + code = VKEY_RIGHT; + else if ("downArrow" == codeStr) + code = VKEY_DOWN; + else if ("leftArrow" == codeStr) + code = VKEY_LEFT; + else if ("upArrow" == codeStr) + code = VKEY_UP; + else if ("insert" == codeStr) + code = VKEY_INSERT; + else if ("delete" == codeStr) + code = VKEY_DELETE; + else if ("pageUp" == codeStr) + code = VKEY_PRIOR; + else if ("pageDown" == codeStr) + code = VKEY_NEXT; + else if ("home" == codeStr) + code = VKEY_HOME; + else if ("end" == codeStr) + code = VKEY_END; + else if ("printScreen" == codeStr) + code = VKEY_SNAPSHOT; + else if ("menu" == codeStr) + code = VKEY_APPS; + else if ("leftControl" == codeStr) + code = VKEY_LCONTROL; + else if ("rightControl" == codeStr) + code = VKEY_RCONTROL; + else if ("leftShift" == codeStr) + code = VKEY_LSHIFT; + else if ("rightShift" == codeStr) + code = VKEY_RSHIFT; + else if ("leftAlt" == codeStr) + code = VKEY_LMENU; + else if ("rightAlt" == codeStr) + code = VKEY_RMENU; + else if ("numLock" == codeStr) + code = VKEY_NUMLOCK; + else { + // Compare the input string with the function-key names defined by the + // DOM spec (i.e. "F1",...,"F24"). If the input string is a function-key + // name, set its key code. + for (int i = 1; i <= 24; ++i) { + char functionChars[10]; + snprintf(functionChars, 10, "F%d", i); + string functionKeyName(functionChars); + if (functionKeyName == codeStr) { + code = VKEY_F1 + (i - 1); + break; + } + } + if (!code) { + WebString webCodeStr = WebString::fromUTF8(codeStr.data(), codeStr.size()); + BLINK_ASSERT(webCodeStr.length() == 1); + text = code = webCodeStr.at(0); + needsShiftKeyModifier = needsShiftModifier(code); + if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z') + code -= 'a' - 'A'; + generateChar = true; + } + + if ("(" == codeStr) { + code = '9'; + needsShiftKeyModifier = true; + } + } + + // For one generated keyboard event, we need to generate a keyDown/keyUp + // pair; refer to EventSender.cpp in Tools/DumpRenderTree/win. + // On Windows, we might also need to generate a char event to mimic the + // Windows event flow; on other platforms we create a merged event and test + // the event flow that that platform provides. + WebKeyboardEvent eventDown, eventChar, eventUp; + eventDown.type = WebInputEvent::RawKeyDown; + eventDown.modifiers = 0; + eventDown.windowsKeyCode = code; +#if defined(__linux__) && defined(TOOLKIT_GTK) + eventDown.nativeKeyCode = NativeKeyCodeForWindowsKeyCode(code); +#endif + + if (generateChar) { + eventDown.text[0] = text; + eventDown.unmodifiedText[0] = text; + } + eventDown.setKeyIdentifierFromWindowsKeyCode(); + + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) { + applyKeyModifiers(&(arguments[1]), &eventDown); +#if WIN32 || __APPLE__ || defined(ANDROID) || defined(TOOLKIT_GTK) + eventDown.isSystemKey = WebInputEventFactory::isSystemKeyEvent(eventDown); +#endif + } + + if (needsShiftKeyModifier) + eventDown.modifiers |= WebInputEvent::ShiftKey; + + // See if KeyLocation argument is given. + if (arguments.size() >= 3 && arguments[2].isNumber()) { + int location = arguments[2].toInt32(); + if (location == DOMKeyLocationNumpad) + eventDown.modifiers |= WebInputEvent::IsKeyPad; + } + + eventChar = eventUp = eventDown; + eventUp.type = WebInputEvent::KeyUp; + // EventSender.m forces a layout here, with at least one + // test (fast/forms/focus-control-to-page.html) relying on this. + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + // In the browser, if a keyboard event corresponds to an editor command, + // the command will be dispatched to the renderer just before dispatching + // the keyboard event, and then it will be executed in the + // RenderView::handleCurrentKeyboardEvent() method, which is called from + // third_party/WebKit/Source/WebKit/chromium/src/EditorClientImpl.cpp. + // We just simulate the same behavior here. + string editCommand; + if (getEditCommand(eventDown, &editCommand)) + m_delegate->setEditCommand(editCommand, ""); + + webview()->handleInputEvent(eventDown); + + if (code == VKEY_ESCAPE && !currentDragData.isNull()) { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + finishDragAndDrop(event, blink::WebDragOperationNone); + } + + m_delegate->clearEditCommand(); + + if (generateChar) { + eventChar.type = WebInputEvent::Char; + eventChar.keyIdentifier[0] = '\0'; + webview()->handleInputEvent(eventChar); + } + + webview()->handleInputEvent(eventUp); +} + +void EventSender::dispatchMessage(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + +#ifdef WIN32 + if (arguments.size() == 3) { + // Grab the message id to see if we need to dispatch it. + int msg = arguments[0].toInt32(); + + // WebKit's version of this function stuffs a MSG struct and uses + // TranslateMessage and DispatchMessage. We use a WebKeyboardEvent, which + // doesn't need to receive the DeadChar and SysDeadChar messages. + if (msg == WM_DEADCHAR || msg == WM_SYSDEADCHAR) + return; + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + unsigned long lparam = static_cast<unsigned long>(arguments[2].toDouble()); + webview()->handleInputEvent(WebInputEventFactory::keyboardEvent(0, msg, arguments[1].toInt32(), lparam)); + } else + BLINK_ASSERT_NOT_REACHED(); +#endif +} + +bool EventSender::needsShiftModifier(int keyCode) +{ + // If code is an uppercase letter, assign a SHIFT key to + // eventDown.modifier, this logic comes from + // Tools/DumpRenderTree/win/EventSender.cpp + return (keyCode & 0xFF) >= 'A' && (keyCode & 0xFF) <= 'Z'; +} + +void EventSender::leapForward(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isNumber()) + return; + + int milliseconds = arguments[0].toInt32(); + if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::LeapForward; + savedEvent.milliseconds = milliseconds; + mouseEventQueue.push_back(savedEvent); + } else + doLeapForward(milliseconds); +} + +void EventSender::doLeapForward(int milliseconds) +{ + advanceEventTime(milliseconds); +} + +// Apple's port of WebKit zooms by a factor of 1.2 (see +// WebKit/WebView/WebView.mm) +void EventSender::textZoomIn(const CppArgumentList&, CppVariant* result) +{ + webview()->setTextZoomFactor(webview()->textZoomFactor() * 1.2f); + result->setNull(); +} + +void EventSender::textZoomOut(const CppArgumentList&, CppVariant* result) +{ + webview()->setTextZoomFactor(webview()->textZoomFactor() / 1.2f); + result->setNull(); +} + +void EventSender::zoomPageIn(const CppArgumentList&, CppVariant* result) +{ + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + + for (size_t i = 0; i < windowList.size(); ++i) + windowList.at(i)->webView()->setZoomLevel(windowList.at(i)->webView()->zoomLevel() + 1); + result->setNull(); +} + +void EventSender::zoomPageOut(const CppArgumentList&, CppVariant* result) +{ + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + + for (size_t i = 0; i < windowList.size(); ++i) + windowList.at(i)->webView()->setZoomLevel(windowList.at(i)->webView()->zoomLevel() - 1); + result->setNull(); +} + +void EventSender::setPageScaleFactor(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; + + float scaleFactor = static_cast<float>(arguments[0].toDouble()); + int x = arguments[1].toInt32(); + int y = arguments[2].toInt32(); + webview()->setPageScaleFactorLimits(scaleFactor, scaleFactor); + webview()->setPageScaleFactor(scaleFactor, WebPoint(x, y)); + result->setNull(); +} + +void EventSender::mouseScrollBy(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseWheelEvent(arguments, result, false, &event); + webview()->handleInputEvent(event); +} + +void EventSender::continuousMouseScrollBy(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseWheelEvent(arguments, result, true, &event); + webview()->handleInputEvent(event); +} + +void EventSender::replaySavedEvents() +{ + replayingSavedEvents = true; + while (!mouseEventQueue.empty()) { + SavedEvent e = mouseEventQueue.front(); + mouseEventQueue.pop_front(); + + switch (e.type) { + case SavedEvent::MouseMove: { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseMove, pressedButton, e.pos, &event, getCurrentEventTimeSec(m_delegate)); + doMouseMove(event); + break; + } + case SavedEvent::LeapForward: + doLeapForward(e.milliseconds); + break; + case SavedEvent::MouseUp: { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseUp, e.buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + doMouseUp(event); + break; + } + default: + BLINK_ASSERT_NOT_REACHED(); + } + } + + replayingSavedEvents = false; +} + +// Because actual context menu is implemented by the browser side, +// this function does only what LayoutTests are expecting: +// - Many test checks the count of items. So returning non-zero value makes sense. +// - Some test compares the count before and after some action. So changing the count based on flags +// also makes sense. This function is doing such for some flags. +// - Some test even checks actual string content. So providing it would be also helpful. +// +static vector<WebString> makeMenuItemStringsFor(WebContextMenuData* contextMenu, WebTestDelegate* delegate) +{ + // These constants are based on Safari's context menu because tests are made for it. + static const char* nonEditableMenuStrings[] = { "Back", "Reload Page", "Open in Dashbaord", "<separator>", "View Source", "Save Page As", "Print Page", "Inspect Element", 0 }; + static const char* editableMenuStrings[] = { "Cut", "Copy", "<separator>", "Paste", "Spelling and Grammar", "Substitutions, Transformations", "Font", "Speech", "Paragraph Direction", "<separator>", 0 }; + + // This is possible because mouse events are cancelleable. + if (!contextMenu) + return vector<WebString>(); + + vector<WebString> strings; + + if (contextMenu->isEditable) { + for (const char** item = editableMenuStrings; *item; ++item) + strings.push_back(WebString::fromUTF8(*item)); + WebVector<WebString> suggestions; + MockSpellCheck::fillSuggestionList(contextMenu->misspelledWord, &suggestions); + for (size_t i = 0; i < suggestions.size(); ++i) + strings.push_back(suggestions[i]); + } else { + for (const char** item = nonEditableMenuStrings; *item; ++item) + strings.push_back(WebString::fromUTF8(*item)); + } + + return strings; +} + +void EventSender::contextClick(const CppArgumentList& arguments, CppVariant* result) +{ + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + updateClickCountForButton(WebMouseEvent::ButtonRight); + + // Clears last context menu data because we need to know if the context menu be requested + // after following mouse events. + m_lastContextMenuData.reset(); + + // Generate right mouse down and up. + WebMouseEvent event; + // This is a hack to work around only allowing a single pressed button since we want to + // test the case where both the left and right mouse buttons are pressed. + if (pressedButton == WebMouseEvent::ButtonNone) + pressedButton = WebMouseEvent::ButtonRight; + initMouseEvent(WebInputEvent::MouseDown, WebMouseEvent::ButtonRight, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + webview()->handleInputEvent(event); + +#ifdef WIN32 + initMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + webview()->handleInputEvent(event); + + pressedButton = WebMouseEvent::ButtonNone; +#endif + + NPObject* resultArray = WebBindings::makeStringArray(makeMenuItemStringsFor(m_lastContextMenuData.get(), m_delegate)); + result->set(resultArray); + WebBindings::releaseObject(resultArray); + + m_lastContextMenuData.reset(); +} + +class MouseDownTask: public WebMethodTask<EventSender> { +public: + MouseDownTask(EventSender* obj, const CppArgumentList& arg) + : WebMethodTask<EventSender>(obj), m_arguments(arg) { } + virtual void runIfValid() { m_object->mouseDown(m_arguments, 0); } + +private: + CppArgumentList m_arguments; +}; + +class MouseUpTask: public WebMethodTask<EventSender> { +public: + MouseUpTask(EventSender* obj, const CppArgumentList& arg) + : WebMethodTask<EventSender>(obj), m_arguments(arg) { } + virtual void runIfValid() { m_object->mouseUp(m_arguments, 0); } + +private: + CppArgumentList m_arguments; +}; + +void EventSender::scheduleAsynchronousClick(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_delegate->postTask(new MouseDownTask(this, arguments)); + m_delegate->postTask(new MouseUpTask(this, arguments)); +} + +class KeyDownTask : public WebMethodTask<EventSender> { +public: + KeyDownTask(EventSender* obj, const CppArgumentList& arg) + : WebMethodTask<EventSender>(obj), m_arguments(arg) { } + virtual void runIfValid() { m_object->keyDown(m_arguments, 0); } + +private: + CppArgumentList m_arguments; +}; + +void EventSender::scheduleAsynchronousKeyDown(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_delegate->postTask(new KeyDownTask(this, arguments)); +} + +void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVariant* result) +{ + currentDragData.initialize(); + vector<string> files = arguments[0].toStringVector(); + WebVector<WebString> absoluteFilenames(files.size()); + for (size_t i = 0; i < files.size(); ++i) { + WebDragData::Item item; + item.storageType = WebDragData::Item::StorageTypeFilename; + item.filenameData = m_delegate->getAbsoluteWebStringFromUTF8Path(files[i]); + currentDragData.addItem(item); + absoluteFilenames[i] = item.filenameData; + } + currentDragData.setFilesystemId(m_delegate->registerIsolatedFileSystem(absoluteFilenames)); + currentDragEffectsAllowed = blink::WebDragOperationCopy; + + // Provide a drag source. + webview()->dragTargetDragEnter(currentDragData, lastMousePos, lastMousePos, currentDragEffectsAllowed, 0); + + // dragMode saves events and then replays them later. We don't need/want that. + dragMode.set(false); + + // Make the rest of eventSender think a drag is in progress. + pressedButton = WebMouseEvent::ButtonLeft; + + result->setNull(); +} + +void EventSender::addTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebTouchPoint touchPoint; + touchPoint.state = WebTouchPoint::StatePressed; + touchPoint.position = WebPoint(arguments[0].toInt32(), arguments[1].toInt32()); + touchPoint.screenPosition = touchPoint.position; + + if (arguments.size() > 2) { + int radiusX = arguments[2].toInt32(); + int radiusY = radiusX; + if (arguments.size() > 3) + radiusY = arguments[3].toInt32(); + + touchPoint.radiusX = radiusX; + touchPoint.radiusY = radiusY; + } + + int lowestId = 0; + for (size_t i = 0; i < touchPoints.size(); i++) { + if (touchPoints[i].id == lowestId) + lowestId++; + } + touchPoint.id = lowestId; + touchPoints.push_back(touchPoint); +} + +void EventSender::clearTouchPoints(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + touchPoints.clear(); +} + +void EventSender::releaseTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + BLINK_ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateReleased; +} + +void EventSender::setTouchModifier(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + int mask = 0; + const string keyName = arguments[0].toString(); + if (keyName == "shift") + mask = WebInputEvent::ShiftKey; + else if (keyName == "alt") + mask = WebInputEvent::AltKey; + else if (keyName == "ctrl") + mask = WebInputEvent::ControlKey; + else if (keyName == "meta") + mask = WebInputEvent::MetaKey; + + if (arguments[1].toBoolean()) + touchModifiers |= mask; + else + touchModifiers &= ~mask; +} + +void EventSender::updateTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + BLINK_ASSERT(index < touchPoints.size()); + + WebPoint position(arguments[1].toInt32(), arguments[2].toInt32()); + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateMoved; + touchPoint->position = position; + touchPoint->screenPosition = position; +} + +void EventSender::cancelTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + BLINK_ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateCancelled; +} + +void EventSender::sendCurrentTouchEvent(const WebInputEvent::Type type) +{ + BLINK_ASSERT(static_cast<unsigned>(WebTouchEvent::touchesLengthCap) > touchPoints.size()); + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + WebTouchEvent touchEvent; + touchEvent.type = type; + touchEvent.modifiers = touchModifiers; + touchEvent.timeStampSeconds = getCurrentEventTimeSec(m_delegate); + touchEvent.touchesLength = touchPoints.size(); + for (unsigned i = 0; i < touchPoints.size(); ++i) + touchEvent.touches[i] = touchPoints[i]; + webview()->handleInputEvent(touchEvent); + + for (unsigned i = 0; i < touchPoints.size(); ++i) { + WebTouchPoint* touchPoint = &touchPoints[i]; + if (touchPoint->state == WebTouchPoint::StateReleased) { + touchPoints.erase(touchPoints.begin() + i); + --i; + } else + touchPoint->state = WebTouchPoint::StateStationary; + } +} + +void EventSender::mouseDragBegin(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + event.phase = WebMouseWheelEvent::PhaseBegan; + event.hasPreciseScrollingDeltas = true; + webview()->handleInputEvent(event); +} + +void EventSender::mouseDragEnd(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + event.phase = WebMouseWheelEvent::PhaseEnded; + event.hasPreciseScrollingDeltas = true; + webview()->handleInputEvent(event); +} + +void EventSender::mouseMomentumBegin(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + event.momentumPhase = WebMouseWheelEvent::PhaseBegan; + event.hasPreciseScrollingDeltas = true; + webview()->handleInputEvent(event); +} + +void EventSender::mouseMomentumScrollBy(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseWheelEvent(arguments, result, true, &event); + event.momentumPhase = WebMouseWheelEvent::PhaseChanged; + event.hasPreciseScrollingDeltas = true; + webview()->handleInputEvent(event); +} + +void EventSender::mouseMomentumEnd(const CppArgumentList& arguments, CppVariant* result) +{ + WebMouseWheelEvent event; + initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); + event.momentumPhase = WebMouseWheelEvent::PhaseEnded; + event.hasPreciseScrollingDeltas = true; + webview()->handleInputEvent(event); +} + +void EventSender::initMouseWheelEvent(const CppArgumentList& arguments, CppVariant* result, bool continuous, WebMouseWheelEvent* event) +{ + result->setNull(); + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + // Force a layout here just to make sure every position has been + // determined before we send events (as well as all the other methods + // that send an event do). + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + int horizontal = arguments[0].toInt32(); + int vertical = arguments[1].toInt32(); + int paged = false; + int hasPreciseScrollingDeltas = false; + + if (arguments.size() > 2 && arguments[2].isBool()) + paged = arguments[2].toBoolean(); + + if (arguments.size() > 3 && arguments[3].isBool()) + hasPreciseScrollingDeltas = arguments[3].toBoolean(); + + initMouseEvent(WebInputEvent::MouseWheel, pressedButton, lastMousePos, event, getCurrentEventTimeSec(m_delegate)); + event->wheelTicksX = static_cast<float>(horizontal); + event->wheelTicksY = static_cast<float>(vertical); + event->deltaX = event->wheelTicksX; + event->deltaY = event->wheelTicksY; + event->scrollByPage = paged; + event->hasPreciseScrollingDeltas = hasPreciseScrollingDeltas; + + if (continuous) { + event->wheelTicksX /= scrollbarPixelsPerTick; + event->wheelTicksY /= scrollbarPixelsPerTick; + } else { + event->deltaX *= scrollbarPixelsPerTick; + event->deltaY *= scrollbarPixelsPerTick; + } +} + +void EventSender::touchEnd(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchEnd); +} + +void EventSender::touchMove(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchMove); +} + +void EventSender::touchStart(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchStart); +} + +void EventSender::touchCancel(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchCancel); +} + +void EventSender::gestureScrollBegin(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollBegin, arguments); +} + +void EventSender::gestureScrollEnd(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollEnd, arguments); +} + +void EventSender::gestureScrollUpdate(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollUpdate, arguments); +} + +void EventSender::gestureScrollUpdateWithoutPropagation(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollUpdateWithoutPropagation, arguments); +} + +void EventSender::gestureTap(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureTap, arguments); +} + +void EventSender::gestureTapDown(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureTapDown, arguments); +} + +void EventSender::gestureShowPress(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureShowPress, arguments); +} + +void EventSender::gestureTapCancel(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureTapCancel, arguments); +} + +void EventSender::gestureLongPress(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureLongPress, arguments); +} + +void EventSender::gestureLongTap(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureLongTap, arguments); +} + +void EventSender::gestureTwoFingerTap(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureTwoFingerTap, arguments); +} + +void EventSender::gestureScrollFirstPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebPoint point(arguments[0].toInt32(), arguments[1].toInt32()); + m_currentGestureLocation = point; +} + +void EventSender::gestureEvent(WebInputEvent::Type type, const CppArgumentList& arguments) +{ + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebPoint point(arguments[0].toInt32(), arguments[1].toInt32()); + + WebGestureEvent event; + event.type = type; + + switch (type) { + case WebInputEvent::GestureScrollUpdate: + case WebInputEvent::GestureScrollUpdateWithoutPropagation: + event.data.scrollUpdate.deltaX = static_cast<float>(arguments[0].toDouble()); + event.data.scrollUpdate.deltaY = static_cast<float>(arguments[1].toDouble()); + event.x = m_currentGestureLocation.x; + event.y = m_currentGestureLocation.y; + m_currentGestureLocation.x = m_currentGestureLocation.x + event.data.scrollUpdate.deltaX; + m_currentGestureLocation.y = m_currentGestureLocation.y + event.data.scrollUpdate.deltaY; + break; + + case WebInputEvent::GestureScrollBegin: + m_currentGestureLocation = WebPoint(point.x, point.y); + event.x = m_currentGestureLocation.x; + event.y = m_currentGestureLocation.y; + break; + case WebInputEvent::GestureScrollEnd: + case WebInputEvent::GestureFlingStart: + event.x = m_currentGestureLocation.x; + event.y = m_currentGestureLocation.y; + break; + case WebInputEvent::GestureTap: + if (arguments.size() >= 3) + event.data.tap.tapCount = static_cast<float>(arguments[2].toDouble()); + else + event.data.tap.tapCount = 1; + event.x = point.x; + event.y = point.y; + break; + case WebInputEvent::GestureTapUnconfirmed: + if (arguments.size() >= 3) + event.data.tap.tapCount = static_cast<float>(arguments[2].toDouble()); + else + event.data.tap.tapCount = 1; + event.x = point.x; + event.y = point.y; + break; + case WebInputEvent::GestureTapDown: + event.x = point.x; + event.y = point.y; + if (arguments.size() >= 4) { + event.data.tapDown.width = static_cast<float>(arguments[2].toDouble()); + event.data.tapDown.height = static_cast<float>(arguments[3].toDouble()); + } + break; + case WebInputEvent::GestureShowPress: + event.x = point.x; + event.y = point.y; + if (arguments.size() >= 4) { + event.data.showPress.width = static_cast<float>(arguments[2].toDouble()); + event.data.showPress.height = static_cast<float>(arguments[3].toDouble()); + } + break; + case WebInputEvent::GestureTapCancel: + event.x = point.x; + event.y = point.y; + break; + case WebInputEvent::GestureLongPress: + event.x = point.x; + event.y = point.y; + if (arguments.size() >= 4) { + event.data.longPress.width = static_cast<float>(arguments[2].toDouble()); + event.data.longPress.height = static_cast<float>(arguments[3].toDouble()); + } + break; + case WebInputEvent::GestureLongTap: + event.x = point.x; + event.y = point.y; + if (arguments.size() >= 4) { + event.data.longPress.width = static_cast<float>(arguments[2].toDouble()); + event.data.longPress.height = static_cast<float>(arguments[3].toDouble()); + } + break; + case WebInputEvent::GestureTwoFingerTap: + event.x = point.x; + event.y = point.y; + if (arguments.size() >= 4) { + event.data.twoFingerTap.firstFingerWidth = static_cast<float>(arguments[2].toDouble()); + event.data.twoFingerTap.firstFingerHeight = static_cast<float>(arguments[3].toDouble()); + } + break; + default: + BLINK_ASSERT_NOT_REACHED(); + } + + event.globalX = event.x; + event.globalY = event.y; + event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + webview()->handleInputEvent(event); + + // Long press might start a drag drop session. Complete it if so. + if (type == WebInputEvent::GestureLongPress && !currentDragData.isNull()) { + WebMouseEvent mouseEvent; + initMouseEvent(WebInputEvent::MouseDown, pressedButton, point, &mouseEvent, getCurrentEventTimeSec(m_delegate)); + finishDragAndDrop(mouseEvent, blink::WebDragOperationNone); + } +} + +void EventSender::gestureFlingCancel(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebGestureEvent event; + event.type = WebInputEvent::GestureFlingCancel; + event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + webview()->handleInputEvent(event); +} + +void EventSender::gestureFlingStart(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 4) + return; + + for (int i = 0; i < 4; i++) + if (!arguments[i].isNumber()) + return; + + WebGestureEvent event; + event.type = WebInputEvent::GestureFlingStart; + + event.x = static_cast<float>(arguments[0].toDouble()); + event.y = static_cast<float>(arguments[1].toDouble()); + event.globalX = event.x; + event.globalY = event.y; + + event.data.flingStart.velocityX = static_cast<float>(arguments[2].toDouble()); + event.data.flingStart.velocityY = static_cast<float>(arguments[3].toDouble()); + event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); + + if (shouldForceLayoutOnEvents()) + webview()->layout(); + + webview()->handleInputEvent(event); +} + +// +// Unimplemented stubs +// + +void EventSender::enableDOMUIEventLogging(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void EventSender::fireKeyboardEventsToElement(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void EventSender::clearKillRing(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +} diff --git a/content/shell/renderer/test_runner/EventSender.h b/content/shell/renderer/test_runner/EventSender.h new file mode 100644 index 0000000..03edb81 --- /dev/null +++ b/content/shell/renderer/test_runner/EventSender.h @@ -0,0 +1,188 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + EventSender class: + Bound to a JavaScript window.eventSender object using + CppBoundClass::bindToJavascript(), this allows layout tests to fire DOM events. +*/ + +#ifndef EventSender_h +#define EventSender_h + +#include "content/shell/renderer/test_runner/CppBoundClass.h" +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebPoint.h" +#include "third_party/WebKit/public/web/WebDragOperation.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +namespace blink { +class WebDragData; +class WebView; +struct WebContextMenuData; +} + +namespace WebTestRunner { + +class TestInterfaces; +class WebTestDelegate; + +class EventSender : public CppBoundClass { +public: + explicit EventSender(TestInterfaces*); + ~EventSender(); + + void setDelegate(WebTestDelegate* delegate) { m_delegate = delegate; } + void setWebView(blink::WebView* webView) { m_webView = webView; } + + void setContextMenuData(const blink::WebContextMenuData&); + + // Resets some static variable state. + void reset(); + + // Simulate drag&drop system call. + void doDragDrop(const blink::WebDragData&, blink::WebDragOperationsMask); + + // Test helper for dragging out images. + void dumpFilenameBeingDragged(const CppArgumentList&, CppVariant*); + + // JS callback methods. + void contextClick(const CppArgumentList&, CppVariant*); + void mouseDown(const CppArgumentList&, CppVariant*); + void mouseUp(const CppArgumentList&, CppVariant*); + void mouseMoveTo(const CppArgumentList&, CppVariant*); + void leapForward(const CppArgumentList&, CppVariant*); + void keyDown(const CppArgumentList&, CppVariant*); + void dispatchMessage(const CppArgumentList&, CppVariant*); + // FIXME: These aren't really events. They should be moved to layout controller. + void textZoomIn(const CppArgumentList&, CppVariant*); + void textZoomOut(const CppArgumentList&, CppVariant*); + void zoomPageIn(const CppArgumentList&, CppVariant*); + void zoomPageOut(const CppArgumentList&, CppVariant*); + void setPageScaleFactor(const CppArgumentList&, CppVariant*); + + void mouseDragBegin(const CppArgumentList&, CppVariant*); + void mouseDragEnd(const CppArgumentList&, CppVariant*); + void mouseMomentumBegin(const CppArgumentList&, CppVariant*); + void mouseMomentumScrollBy(const CppArgumentList&, CppVariant*); + void mouseMomentumEnd(const CppArgumentList&, CppVariant*); + void mouseScrollBy(const CppArgumentList&, CppVariant*); + void continuousMouseScrollBy(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousClick(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousKeyDown(const CppArgumentList&, CppVariant*); + void beginDragWithFiles(const CppArgumentList&, CppVariant*); + CppVariant dragMode; + + void addTouchPoint(const CppArgumentList&, CppVariant*); + void cancelTouchPoint(const CppArgumentList&, CppVariant*); + void clearTouchPoints(const CppArgumentList&, CppVariant*); + void releaseTouchPoint(const CppArgumentList&, CppVariant*); + void setTouchModifier(const CppArgumentList&, CppVariant*); + void touchCancel(const CppArgumentList&, CppVariant*); + void touchEnd(const CppArgumentList&, CppVariant*); + void touchMove(const CppArgumentList&, CppVariant*); + void touchStart(const CppArgumentList&, CppVariant*); + void updateTouchPoint(const CppArgumentList&, CppVariant*); + + void gestureFlingCancel(const CppArgumentList&, CppVariant*); + void gestureFlingStart(const CppArgumentList&, CppVariant*); + void gestureScrollBegin(const CppArgumentList&, CppVariant*); + void gestureScrollEnd(const CppArgumentList&, CppVariant*); + void gestureScrollFirstPoint(const CppArgumentList&, CppVariant*); + void gestureScrollUpdate(const CppArgumentList&, CppVariant*); + void gestureScrollUpdateWithoutPropagation(const CppArgumentList&, CppVariant*); + void gestureTap(const CppArgumentList&, CppVariant*); + void gestureTapDown(const CppArgumentList&, CppVariant*); + void gestureShowPress(const CppArgumentList&, CppVariant*); + void gestureTapCancel(const CppArgumentList&, CppVariant*); + void gestureLongPress(const CppArgumentList&, CppVariant*); + void gestureLongTap(const CppArgumentList&, CppVariant*); + void gestureTwoFingerTap(const CppArgumentList&, CppVariant*); + void gestureEvent(blink::WebInputEvent::Type, const CppArgumentList&); + + // Setting this to false makes EventSender not force layout() calls. + // This makes it possible to test the standard WebCore event dispatch. + CppVariant forceLayoutOnEvents; + + // Unimplemented stubs + void enableDOMUIEventLogging(const CppArgumentList&, CppVariant*); + void fireKeyboardEventsToElement(const CppArgumentList&, CppVariant*); + void clearKillRing(const CppArgumentList&, CppVariant*); + + // Properties used in layout tests. +#if defined(OS_WIN) + CppVariant wmKeyDown; + CppVariant wmKeyUp; + CppVariant wmChar; + CppVariant wmDeadChar; + CppVariant wmSysKeyDown; + CppVariant wmSysKeyUp; + CppVariant wmSysChar; + CppVariant wmSysDeadChar; +#endif + + WebTaskList* taskList() { return &m_taskList; } + +private: + blink::WebView* webview() { return m_webView; } + + // Returns true if dragMode is true. + bool isDragMode() { return dragMode.isBool() && dragMode.toBoolean(); } + + bool shouldForceLayoutOnEvents() const { return forceLayoutOnEvents.isBool() && forceLayoutOnEvents.toBoolean(); } + + // Sometimes we queue up mouse move and mouse up events for drag drop + // handling purposes. These methods dispatch the event. + void doMouseMove(const blink::WebMouseEvent&); + void doMouseUp(const blink::WebMouseEvent&); + static void doLeapForward(int milliseconds); + void replaySavedEvents(); + + // Helper to return the button type given a button code + static blink::WebMouseEvent::Button getButtonTypeFromButtonNumber(int); + + // Helper to extract the button number from the optional argument in + // mouseDown and mouseUp + static int getButtonNumberFromSingleArg(const CppArgumentList&); + + // Returns true if the specified key code passed in needs a shift key + // modifier to be passed into the generated event. + bool needsShiftModifier(int); + + void finishDragAndDrop(const blink::WebMouseEvent&, blink::WebDragOperation); + void updateClickCountForButton(blink::WebMouseEvent::Button); + + // Compose a touch event from the current touch points and send it. + void sendCurrentTouchEvent(const blink::WebInputEvent::Type); + + // Init a mouse wheel event from the given args. + void initMouseWheelEvent(const CppArgumentList&, CppVariant*, bool continuous, blink::WebMouseWheelEvent*); + + WebTaskList m_taskList; + + TestInterfaces* m_testInterfaces; + WebTestDelegate* m_delegate; + blink::WebView* m_webView; + + WebScopedPtr<blink::WebContextMenuData> m_lastContextMenuData; + + // Location of the touch point that initiated a gesture. + blink::WebPoint m_currentGestureLocation; + + // Location of last mouseMoveTo event. + static blink::WebPoint lastMousePos; + + // Currently pressed mouse button (Left/Right/Middle or None) + static blink::WebMouseEvent::Button pressedButton; + + // The last button number passed to mouseDown and mouseUp. + // Used to determine whether the click count continues to + // increment or not. + static blink::WebMouseEvent::Button lastButtonType; +}; + +} + +#endif // EventSender_h diff --git a/content/shell/renderer/test_runner/GamepadController.cpp b/content/shell/renderer/test_runner/GamepadController.cpp new file mode 100644 index 0000000..559585b --- /dev/null +++ b/content/shell/renderer/test_runner/GamepadController.cpp @@ -0,0 +1,173 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/GamepadController.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" + +using namespace blink; + +namespace WebTestRunner { + +GamepadController::GamepadController() +{ + bindMethod("connect", &GamepadController::connect); + bindMethod("disconnect", &GamepadController::disconnect); + bindMethod("setId", &GamepadController::setId); + bindMethod("setButtonCount", &GamepadController::setButtonCount); + bindMethod("setButtonData", &GamepadController::setButtonData); + bindMethod("setAxisCount", &GamepadController::setAxisCount); + bindMethod("setAxisData", &GamepadController::setAxisData); + + bindFallbackMethod(&GamepadController::fallbackCallback); + + reset(); +} + +void GamepadController::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + CppBoundClass::bindToJavascript(frame, classname); +} + +void GamepadController::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; +} + +void GamepadController::reset() +{ + memset(&m_gamepads, 0, sizeof(m_gamepads)); +} + +void GamepadController::connect(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 1) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + m_gamepads.items[index].connected = true; + m_gamepads.length = 0; + for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) + if (m_gamepads.items[i].connected) + m_gamepads.length = i + 1; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::disconnect(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 1) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + m_gamepads.items[index].connected = false; + m_gamepads.length = 0; + for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) + if (m_gamepads.items[i].connected) + m_gamepads.length = i + 1; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::setId(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + std::string src = args[1].toString(); + const char* p = src.c_str(); + memset(m_gamepads.items[index].id, 0, sizeof(m_gamepads.items[index].id)); + for (unsigned i = 0; *p && i < blink::WebGamepad::idLengthCap - 1; ++i) + m_gamepads.items[index].id[i] = *p++; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::setButtonCount(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + int buttons = args[1].toInt32(); + if (buttons < 0 || buttons >= static_cast<int>(blink::WebGamepad::buttonsLengthCap)) + return; + m_gamepads.items[index].buttonsLength = buttons; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::setButtonData(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 3) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + int button = args[1].toInt32(); + if (button < 0 || button >= static_cast<int>(blink::WebGamepad::buttonsLengthCap)) + return; + double data = args[2].toDouble(); + m_gamepads.items[index].buttons[button] = data; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::setAxisCount(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + int axes = args[1].toInt32(); + if (axes < 0 || axes >= static_cast<int>(blink::WebGamepad::axesLengthCap)) + return; + m_gamepads.items[index].axesLength = axes; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::setAxisData(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 3) { + m_delegate->printMessage("Invalid args"); + return; + } + int index = args[0].toInt32(); + if (index < 0 || index >= static_cast<int>(blink::WebGamepads::itemsLengthCap)) + return; + int axis = args[1].toInt32(); + if (axis < 0 || axis >= static_cast<int>(blink::WebGamepad::axesLengthCap)) + return; + double data = args[2].toDouble(); + m_gamepads.items[index].axes[axis] = data; + m_delegate->setGamepadData(m_gamepads); + result->setNull(); +} + +void GamepadController::fallbackCallback(const CppArgumentList&, CppVariant* result) +{ + m_delegate->printMessage("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on GamepadController\n"); + result->setNull(); +} + +} diff --git a/content/shell/renderer/test_runner/GamepadController.h b/content/shell/renderer/test_runner/GamepadController.h new file mode 100644 index 0000000..68ec793 --- /dev/null +++ b/content/shell/renderer/test_runner/GamepadController.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GamepadController_h +#define GamepadController_h + +#include "content/shell/renderer/test_runner/CppBoundClass.h" +#include "third_party/WebKit/public/platform/WebGamepads.h" + +namespace blink { +class WebGamepads; +class WebFrame; +} + +namespace WebTestRunner { + +class WebTestDelegate; + +class GamepadController : public CppBoundClass { +public: + GamepadController(); + + void bindToJavascript(blink::WebFrame*, const blink::WebString& classname); + void setDelegate(WebTestDelegate*); + void reset(); + +private: + // Bound methods and properties + void connect(const CppArgumentList&, CppVariant*); + void disconnect(const CppArgumentList&, CppVariant*); + void setId(const CppArgumentList&, CppVariant*); + void setButtonCount(const CppArgumentList&, CppVariant*); + void setButtonData(const CppArgumentList&, CppVariant*); + void setAxisCount(const CppArgumentList&, CppVariant*); + void setAxisData(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + + blink::WebGamepads m_gamepads; + + WebTestDelegate* m_delegate; +}; + +} + +#endif // GamepadController_h diff --git a/content/shell/renderer/test_runner/KeyCodeMapping.cpp b/content/shell/renderer/test_runner/KeyCodeMapping.cpp new file mode 100644 index 0000000..e5ec961 --- /dev/null +++ b/content/shell/renderer/test_runner/KeyCodeMapping.cpp @@ -0,0 +1,222 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/KeyCodeMapping.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" + +namespace WebTestRunner { + +int NativeKeyCodeForWindowsKeyCode(int keysym) +{ +#if defined(__linux__) && defined(TOOLKIT_GTK) + // See /usr/share/X11/xkb/keycodes/* + static const int asciiToKeyCode[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 22, + 23, + 0, + 0, + 0, + 36, + 0, + 0, + 50, + 37, + 64, + 127, + 66, + 0, + 0, + 0, + 0, + 131, + 0, + 9, + 100, + 102, + 0, + 0, + 65, // ' ' + 112, // '!' + 117, // '"' + 115, // '#' + 110, // '$' + 113, // '%' + 111, // '&' + 114, // ''' + 116, // '(' + 0, // ')' + 107, // '*' + 0, // '+' + 0, // ',' + 118, // '-' + 119, // '.' + 146, // '/' + 19, // '0' + 10, // '1' + 11, // '2' + 12, // '3' + 13, // '4' + 14, // '5' + 15, // '6' + 16, // '7' + 17, // '8' + 18, // '9' + 0, // ':' + 0, // ';' + 0, // '<' + 0, // '=' + 0, // '>' + 0, // '?' + 0, // '@' + 38, // 'A' + 56, // 'B' + 54, // 'C' + 40, // 'D' + 26, // 'E' + 41, // 'F' + 42, // 'G' + 43, // 'H' + 31, // 'I' + 44, // 'J' + 45, // 'K' + 46, // 'L' + 58, // 'M' + 57, // 'N' + 32, // 'O' + 33, // 'P' + 24, // 'Q' + 27, // 'R' + 39, // 'S' + 28, // 'T' + 30, // 'U' + 55, // 'V' + 25, // 'W' + 53, // 'X' + 29, // 'Y' + 52, // 'Z' + 133, // '[' + 134, // '\' + 135, // ']' + 0, // '^' + 0, // '_' + 90, // '`' + 38, // 'a' + 56, // 'b' + 54, // 'c' + 40, // 'd' + 26, // 'e' + 41, // 'f' + 42, // 'g' + 43, // 'h' + 31, // 'i' + 44, // 'j' + 45, // 'k' + 46, // 'l' + 58, // 'm' + 57, // 'n' + 32, // 'o' + 33, // 'p' + 24, // 'q' + 27, // 'r' + 39, // 's' + 28, // 't' + 30, // 'u' + 55, // 'v' + 25, // 'w' + 53, // 'x' + 29, // 'y' + 52, // 'z' + 96, // '{' + 0, // '|' + 0, // '}' + 0, // '~' + 0, // DEL + }; + + if (keysym <= 127) + return asciiToKeyCode[keysym]; + + switch (keysym) { + case VKEY_PRIOR: + return 112; + case VKEY_NEXT: + return 117; + case VKEY_END: + return 115; + case VKEY_HOME: + return 110; + case VKEY_LEFT: + return 113; + case VKEY_UP: + return 111; + case VKEY_RIGHT: + return 114; + case VKEY_DOWN: + return 116; + case VKEY_SNAPSHOT: + return 107; + case VKEY_INSERT: + return 118; + case VKEY_DELETE: + return 119; + case VKEY_APPS: + return 135; + case VKEY_F1: + case VKEY_F1 + 1: + case VKEY_F1 + 2: + case VKEY_F1 + 3: + case VKEY_F1 + 4: + case VKEY_F1 + 5: + case VKEY_F1 + 6: + case VKEY_F1 + 7: + case VKEY_F1 + 8: + case VKEY_F1 + 9: + case VKEY_F1 + 10: + case VKEY_F1 + 11: + case VKEY_F1 + 12: + case VKEY_F1 + 13: + case VKEY_F1 + 14: + case VKEY_F1 + 15: + case VKEY_F1 + 16: + case VKEY_F1 + 17: + case VKEY_F1 + 18: + case VKEY_F1 + 19: + case VKEY_F1 + 20: + case VKEY_F1 + 21: + case VKEY_F1 + 22: + case VKEY_F1 + 23: + return 67 + (keysym - VKEY_F1); + case VKEY_LSHIFT: + return 50; + case VKEY_RSHIFT: + return 62; + case VKEY_LCONTROL: + return 37; + case VKEY_RCONTROL: + return 105; + case VKEY_LMENU: + return 64; + case VKEY_RMENU: + return 108; + case VKEY_NUMLOCK: + return 77; + + default: + return 0; + } +#else + return keysym - keysym; +#endif +} + +} diff --git a/content/shell/renderer/test_runner/KeyCodeMapping.h b/content/shell/renderer/test_runner/KeyCodeMapping.h new file mode 100644 index 0000000..75325e1 --- /dev/null +++ b/content/shell/renderer/test_runner/KeyCodeMapping.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef KeyCodeMapping_h +#define KeyCodeMapping_h + +namespace WebTestRunner { + +// The keycodes match the values of the virtual keycodes found here http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx +enum { + VKEY_RETURN = 0x0D, + VKEY_ESCAPE = 0x1B, + VKEY_PRIOR = 0x21, + VKEY_NEXT = 0x22, + VKEY_END = 0x23, + VKEY_HOME = 0x24, + VKEY_LEFT = 0x25, + VKEY_UP = 0x26, + VKEY_RIGHT = 0x27, + VKEY_DOWN = 0x28, + VKEY_SNAPSHOT = 0x2C, + VKEY_INSERT = 0x2D, + VKEY_DELETE = 0x2E, + VKEY_APPS = 0x5D, + VKEY_F1 = 0x70, + VKEY_NUMLOCK = 0x90, + VKEY_LSHIFT = 0xA0, + VKEY_RSHIFT = 0xA1, + VKEY_LCONTROL = 0xA2, + VKEY_RCONTROL = 0xA3, + VKEY_LMENU = 0xA4, + VKEY_RMENU = 0xA5, +}; + +// Map a windows keycode to a native keycode on defined(__linux__) && defined(TOOLKIT_GTK). +int NativeKeyCodeForWindowsKeyCode(int keysym); + +} + +#endif // KeyCodeMapping_h diff --git a/content/shell/renderer/test_runner/MockColorChooser.cpp b/content/shell/renderer/test_runner/MockColorChooser.cpp new file mode 100644 index 0000000..7b88a5a --- /dev/null +++ b/content/shell/renderer/test_runner/MockColorChooser.cpp @@ -0,0 +1,58 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockColorChooser.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { +class HostMethodTask : public WebMethodTask<MockColorChooser> { +public: + typedef void (MockColorChooser::*CallbackMethodType)(); + HostMethodTask(MockColorChooser* object, CallbackMethodType callback) + : WebMethodTask<MockColorChooser>(object) + , m_callback(callback) + { } + + virtual void runIfValid() { (m_object->*m_callback)(); } + +private: + CallbackMethodType m_callback; +}; +} + +MockColorChooser::MockColorChooser(blink::WebColorChooserClient* client, WebTestDelegate* delegate, WebTestProxyBase* proxy) + : m_client(client) + , m_delegate(delegate) + , m_proxy(proxy) +{ + m_proxy->didOpenChooser(); +} + +MockColorChooser::~MockColorChooser() +{ + m_proxy->didCloseChooser(); +} + +void MockColorChooser::setSelectedColor(const blink::WebColor) +{ +} + +void MockColorChooser::endChooser() +{ + m_delegate->postDelayedTask(new HostMethodTask(this, &MockColorChooser::invokeDidEndChooser), 0); +} + +void MockColorChooser::invokeDidEndChooser() +{ + m_client->didEndChooser(); +} + +} diff --git a/content/shell/renderer/test_runner/MockColorChooser.h b/content/shell/renderer/test_runner/MockColorChooser.h new file mode 100644 index 0000000..5641114 --- /dev/null +++ b/content/shell/renderer/test_runner/MockColorChooser.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockColorChooser_h +#define MockColorChooser_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebColorChooser.h" +#include "third_party/WebKit/public/web/WebColorChooserClient.h" + +namespace WebTestRunner { + +class WebTestDelegate; +class WebTestProxyBase; +class MockColorChooser : public blink::WebColorChooser, public blink::WebNonCopyable { +public: + MockColorChooser(blink::WebColorChooserClient*, WebTestDelegate*, WebTestProxyBase*); + virtual ~MockColorChooser(); + + // blink::WebColorChooser implementation. + virtual void setSelectedColor(const blink::WebColor) OVERRIDE; + virtual void endChooser() OVERRIDE; + + void invokeDidEndChooser(); + WebTaskList* taskList() { return &m_taskList; } +private: + blink::WebColorChooserClient* m_client; + WebTestDelegate* m_delegate; + WebTestProxyBase* m_proxy; + WebTaskList m_taskList; +}; + +} + +#endif // MockColorChooser_h diff --git a/content/shell/renderer/test_runner/MockConstraints.cpp b/content/shell/renderer/test_runner/MockConstraints.cpp new file mode 100644 index 0000000..2fdb9fe --- /dev/null +++ b/content/shell/renderer/test_runner/MockConstraints.cpp @@ -0,0 +1,59 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockConstraints.h" + +#include "third_party/WebKit/public/platform/WebMediaConstraints.h" +#include "third_party/WebKit/public/platform/WebString.h" + +using namespace blink; + +namespace WebTestRunner { + +namespace { + +bool isSupported(const WebString& constraint) +{ + return constraint == "valid_and_supported_1" || constraint == "valid_and_supported_2"; +} + +bool isValid(const WebString& constraint) +{ + return isSupported(constraint) || constraint == "valid_but_unsupported_1" || constraint == "valid_but_unsupported_2"; +} + +} + +bool MockConstraints::verifyConstraints(const WebMediaConstraints& constraints, WebString* failedConstraint) +{ + WebVector<WebMediaConstraint> mandatoryConstraints; + constraints.getMandatoryConstraints(mandatoryConstraints); + if (mandatoryConstraints.size()) { + for (size_t i = 0; i < mandatoryConstraints.size(); ++i) { + const WebMediaConstraint& curr = mandatoryConstraints[i]; + if (!isSupported(curr.m_name) || curr.m_value != "1") { + if (failedConstraint) + *failedConstraint = curr.m_name; + return false; + } + } + } + + WebVector<WebMediaConstraint> optionalConstraints; + constraints.getOptionalConstraints(optionalConstraints); + if (optionalConstraints.size()) { + for (size_t i = 0; i < optionalConstraints.size(); ++i) { + const WebMediaConstraint& curr = optionalConstraints[i]; + if (!isValid(curr.m_name) || curr.m_value != "0") { + if (failedConstraint) + *failedConstraint = curr.m_name; + return false; + } + } + } + + return true; +} + +} diff --git a/content/shell/renderer/test_runner/MockConstraints.h b/content/shell/renderer/test_runner/MockConstraints.h new file mode 100644 index 0000000..1ed1c73 --- /dev/null +++ b/content/shell/renderer/test_runner/MockConstraints.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockConstraints_h +#define MockConstraints_h + +namespace blink { +class WebMediaConstraints; +class WebString; +} + +namespace WebTestRunner { + +class MockConstraints { +public: + static bool verifyConstraints(const blink::WebMediaConstraints&, blink::WebString* failedConstraint = 0); +}; + +} + +#endif // MockConstraints_h diff --git a/content/shell/renderer/test_runner/MockGrammarCheck.cpp b/content/shell/renderer/test_runner/MockGrammarCheck.cpp new file mode 100644 index 0000000..9a27c5e --- /dev/null +++ b/content/shell/renderer/test_runner/MockGrammarCheck.cpp @@ -0,0 +1,58 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockGrammarCheck.h" + +#include <algorithm> + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebTextCheckingResult.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +bool MockGrammarCheck::checkGrammarOfString(const WebString& text, vector<WebTextCheckingResult>* results) +{ + BLINK_ASSERT(results); + string16 stringText = text; + if (find_if(stringText.begin(), stringText.end(), isASCIIAlpha) == stringText.end()) + return true; + + // Find matching grammatical errors from known ones. This function has to + // check all errors because the given text may consist of two or more + // sentences that have grammatical errors. + static const struct { + const char* text; + int location; + int length; + } grammarErrors[] = { + {"I have a issue.", 7, 1}, + {"I have an grape.", 7, 2}, + {"I have an kiwi.", 7, 2}, + {"I have an muscat.", 7, 2}, + {"You has the right.", 4, 3}, + {"apple orange zz.", 0, 16}, + {"apple zz orange.", 0, 16}, + {"apple,zz,orange.", 0, 16}, + {"orange,zz,apple.", 0, 16}, + {"the the adlj adaasj sdklj. there there", 4, 3}, + {"the the adlj adaasj sdklj. there there", 33, 5}, + {"zz apple orange.", 0, 16}, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(grammarErrors); ++i) { + size_t offset = 0; + string16 error(grammarErrors[i].text, grammarErrors[i].text + strlen(grammarErrors[i].text)); + while ((offset = stringText.find(error, offset)) != string16::npos) { + results->push_back(WebTextCheckingResult(WebTextDecorationTypeGrammar, offset + grammarErrors[i].location, grammarErrors[i].length)); + offset += grammarErrors[i].length; + } + } + return false; +} + +} diff --git a/content/shell/renderer/test_runner/MockGrammarCheck.h b/content/shell/renderer/test_runner/MockGrammarCheck.h new file mode 100644 index 0000000..348c97a --- /dev/null +++ b/content/shell/renderer/test_runner/MockGrammarCheck.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockGrammarCheck_h +#define MockGrammarCheck_h + +#include <vector> + +namespace blink { + +class WebString; +struct WebTextCheckingResult; + +} + +namespace WebTestRunner { + +// A mock implementation of a grammar-checker used for WebKit tests. This class +// only implements the minimal functionarities required by WebKit tests, i.e. +// this class just compares the given string with known grammar mistakes in +// webkit tests and adds grammar markers on them. Even though this is sufficent +// for webkit tests, this class is not suitable for any other usages. +class MockGrammarCheck { +public: + static bool checkGrammarOfString(const blink::WebString&, std::vector<blink::WebTextCheckingResult>*); +}; + +} + +#endif // MockSpellCheck_h diff --git a/content/shell/renderer/test_runner/MockSpellCheck.cpp b/content/shell/renderer/test_runner/MockSpellCheck.cpp new file mode 100644 index 0000000..2268ebe --- /dev/null +++ b/content/shell/renderer/test_runner/MockSpellCheck.cpp @@ -0,0 +1,184 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockSpellCheck.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebCString.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +void append(WebVector<WebString>* data, const WebString& item) +{ + WebVector<WebString> result(data->size() + 1); + for (size_t i = 0; i < data->size(); ++i) + result[i] = (*data)[i]; + result[data->size()] = item; + data->swap(result); +} + +} + +MockSpellCheck::MockSpellCheck() + : m_initialized(false) { } + +MockSpellCheck::~MockSpellCheck() { } + +bool MockSpellCheck::spellCheckWord(const WebString& text, int* misspelledOffset, int* misspelledLength) +{ + BLINK_ASSERT(misspelledOffset); + BLINK_ASSERT(misspelledLength); + + // Initialize this spellchecker. + initializeIfNeeded(); + + // Reset the result values as our spellchecker does. + *misspelledOffset = 0; + *misspelledLength = 0; + + // Convert to a string16 because we store string16 instances in + // m_misspelledWords and WebString has no find(). + string16 stringText = text; + int skippedLength = 0; + + while (!stringText.empty()) { + // Extract the first possible English word from the given string. + // The given string may include non-ASCII characters or numbers. So, we + // should filter out such characters before start looking up our + // misspelled-word table. + // (This is a simple version of our SpellCheckWordIterator class.) + // If the given string doesn't include any ASCII characters, we can treat the + // string as valid one. + string16::iterator firstChar = find_if(stringText.begin(), stringText.end(), isASCIIAlpha); + if (firstChar == stringText.end()) + return true; + int wordOffset = distance(stringText.begin(), firstChar); + int maxWordLength = static_cast<int>(stringText.length()) - wordOffset; + int wordLength; + string16 word; + + // Look up our misspelled-word table to check if the extracted word is a + // known misspelled word, and return the offset and the length of the + // extracted word if this word is a known misspelled word. + // (See the comment in MockSpellCheck::initializeIfNeeded() why we use a + // misspelled-word table.) + for (size_t i = 0; i < m_misspelledWords.size(); ++i) { + wordLength = static_cast<int>(m_misspelledWords.at(i).length()) > maxWordLength ? maxWordLength : static_cast<int>(m_misspelledWords.at(i).length()); + word = stringText.substr(wordOffset, wordLength); + if (word == m_misspelledWords.at(i) && (static_cast<int>(stringText.length()) == wordOffset + wordLength || isNotASCIIAlpha(stringText[wordOffset + wordLength]))) { + *misspelledOffset = wordOffset + skippedLength; + *misspelledLength = wordLength; + break; + } + } + + if (*misspelledLength > 0) + break; + + string16::iterator lastChar = find_if(stringText.begin() + wordOffset, stringText.end(), isNotASCIIAlpha); + if (lastChar == stringText.end()) + wordLength = static_cast<int>(stringText.length()) - wordOffset; + else + wordLength = distance(firstChar, lastChar); + + BLINK_ASSERT(0 < wordOffset + wordLength); + stringText = stringText.substr(wordOffset + wordLength); + skippedLength += wordOffset + wordLength; + } + + return false; +} + +bool MockSpellCheck::hasInCache(const WebString& word) +{ + return word == WebString::fromUTF8("Spell wellcome. Is it broken?") || word == WebString::fromUTF8("Spell wellcome.\x007F"); +} + +bool MockSpellCheck::isMultiWordMisspelling(const WebString& text, vector<WebTextCheckingResult>* results) +{ + if (text == WebString::fromUTF8("Helllo wordl.")) { + results->push_back(WebTextCheckingResult(WebTextDecorationTypeSpelling, 0, 6, WebString("Hello"))); + results->push_back(WebTextCheckingResult(WebTextDecorationTypeSpelling, 7, 5, WebString("world"))); + return true; + } + return false; +} + +void MockSpellCheck::fillSuggestionList(const WebString& word, WebVector<WebString>* suggestions) +{ + if (word == WebString::fromUTF8("wellcome")) + append(suggestions, WebString::fromUTF8("welcome")); + else if (word == WebString::fromUTF8("upper case")) + append(suggestions, WebString::fromUTF8("uppercase")); + else if (word == WebString::fromUTF8("Helllo")) + append(suggestions, WebString::fromUTF8("Hello")); + else if (word == WebString::fromUTF8("wordl")) + append(suggestions, WebString::fromUTF8("world")); +} + +bool MockSpellCheck::initializeIfNeeded() +{ + // Exit if we have already initialized this object. + if (m_initialized) + return false; + + // Create a table that consists of misspelled words used in WebKit layout + // tests. + // Since WebKit layout tests don't have so many misspelled words as + // well-spelled words, it is easier to compare the given word with misspelled + // ones than to compare with well-spelled ones. + static const char* misspelledWords[] = { + // These words are known misspelled words in webkit tests. + // If there are other misspelled words in webkit tests, please add them in + // this array. + "foo", + "Foo", + "baz", + "fo", + "LibertyF", + "chello", + "xxxtestxxx", + "XXxxx", + "Textx", + "blockquoted", + "asd", + "Lorem", + "Nunc", + "Curabitur", + "eu", + "adlj", + "adaasj", + "sdklj", + "jlkds", + "jsaada", + "jlda", + "zz", + "contentEditable", + // The following words are used by unit tests. + "ifmmp", + "qwertyuiopasd", + "qwertyuiopasdf", + "upper case", + "wellcome" + }; + + m_misspelledWords.clear(); + for (size_t i = 0; i < arraysize(misspelledWords); ++i) + m_misspelledWords.push_back(string16(misspelledWords[i], misspelledWords[i] + strlen(misspelledWords[i]))); + + // Mark as initialized to prevent this object from being initialized twice + // or more. + m_initialized = true; + + // Since this MockSpellCheck class doesn't download dictionaries, this + // function always returns false. + return false; +} + +} diff --git a/content/shell/renderer/test_runner/MockSpellCheck.h b/content/shell/renderer/test_runner/MockSpellCheck.h new file mode 100644 index 0000000..5123c03 --- /dev/null +++ b/content/shell/renderer/test_runner/MockSpellCheck.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockSpellCheck_h +#define MockSpellCheck_h + +#include <vector> + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebTextCheckingResult.h" + +namespace WebTestRunner { + +// A mock implementation of a spell-checker used for WebKit tests. +// This class only implements the minimal functionarities required by WebKit +// tests, i.e. this class just compares the given string with known misspelled +// words in webkit tests and mark them as missspelled. +// Even though this is sufficent for webkit tests, this class is not suitable +// for any other usages. +class MockSpellCheck : public blink::WebNonCopyable { +public: + static void fillSuggestionList(const blink::WebString& word, blink::WebVector<blink::WebString>* suggestions); + + MockSpellCheck(); + ~MockSpellCheck(); + + // Checks the spellings of the specified text. + // This function returns true if the text consists of valid words, and + // returns false if it includes invalid words. + // When the given text includes invalid words, this function sets the + // position of the first invalid word to misspelledOffset, and the length of + // the first invalid word to misspelledLength, respectively. + // For example, when the given text is " zz zz", this function sets 3 to + // misspelledOffset and 2 to misspelledLength, respectively. + bool spellCheckWord(const blink::WebString& text, int* misspelledOffset, int* misspelledLength); + + // Checks whether the specified text can be spell checked immediately using + // the spell checker cache. + bool hasInCache(const blink::WebString& text); + + // Checks whether the specified text is a misspelling comprised of more + // than one word. If it is, append multiple results to the results vector. + bool isMultiWordMisspelling(const blink::WebString& text, std::vector<blink::WebTextCheckingResult>* results); + +private: + // Initialize the internal resources if we need to initialize it. + // Initializing this object may take long time. To prevent from hurting + // the performance of test_shell, we initialize this object when + // SpellCheckWord() is called for the first time. + // To be compliant with SpellCheck:InitializeIfNeeded(), this function + // returns true if this object is downloading a dictionary, otherwise + // it returns false. + bool initializeIfNeeded(); + + // A table that consists of misspelled words. + std::vector<string16> m_misspelledWords; + + // A flag representing whether or not this object is initialized. + bool m_initialized; +}; + +} + +#endif // MockSpellCheck_h diff --git a/content/shell/renderer/test_runner/MockWebAudioDevice.cpp b/content/shell/renderer/test_runner/MockWebAudioDevice.cpp new file mode 100644 index 0000000..b3bc688 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebAudioDevice.cpp @@ -0,0 +1,33 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebAudioDevice.h" + +using namespace blink; + +namespace WebTestRunner { + +MockWebAudioDevice::MockWebAudioDevice(double sampleRate) + : m_sampleRate(sampleRate) +{ +} + +MockWebAudioDevice::~MockWebAudioDevice() +{ +} + +void MockWebAudioDevice::start() +{ +} + +void MockWebAudioDevice::stop() +{ +} + +double MockWebAudioDevice::sampleRate() +{ + return m_sampleRate; +} + +} // namespace WebTestRunner diff --git a/content/shell/renderer/test_runner/MockWebAudioDevice.h b/content/shell/renderer/test_runner/MockWebAudioDevice.h new file mode 100644 index 0000000..3888f06 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebAudioDevice.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebAudioDevice_h +#define MockWebAudioDevice_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebAudioDevice.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +namespace WebTestRunner { + +class MockWebAudioDevice : public blink::WebAudioDevice, public blink::WebNonCopyable { +public: + explicit MockWebAudioDevice(double sampleRate); + virtual ~MockWebAudioDevice(); + + virtual void start(); + virtual void stop(); + virtual double sampleRate(); + +private: + double m_sampleRate; +}; + +} // namespace WebTestRunner + +#endif // MockWebAudioDevice_h diff --git a/content/shell/renderer/test_runner/MockWebMIDIAccessor.cpp b/content/shell/renderer/test_runner/MockWebMIDIAccessor.cpp new file mode 100644 index 0000000..21a4501 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebMIDIAccessor.cpp @@ -0,0 +1,58 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebMIDIAccessor.h" + +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/TestRunner.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessorClient.h" + +using namespace blink; + +namespace { + +class DidStartSessionTask : public WebTestRunner::WebMethodTask<WebTestRunner::MockWebMIDIAccessor> { +public: + DidStartSessionTask(WebTestRunner::MockWebMIDIAccessor* object, blink::WebMIDIAccessorClient* client, bool result) + : WebMethodTask<WebTestRunner::MockWebMIDIAccessor>(object) + , m_client(client) + , m_result(result) + { + } + + virtual void runIfValid() OVERRIDE + { + m_client->didStartSession(m_result); + } + +private: + blink::WebMIDIAccessorClient* m_client; + bool m_result; +}; + +} // namespace + +namespace WebTestRunner { + +MockWebMIDIAccessor::MockWebMIDIAccessor(blink::WebMIDIAccessorClient* client, TestInterfaces* interfaces) + : m_client(client) + , m_interfaces(interfaces) +{ +} + +MockWebMIDIAccessor::~MockWebMIDIAccessor() +{ +} + +void MockWebMIDIAccessor::startSession() +{ + // Add a mock input and output port. + m_client->didAddInputPort("MockInputID", "MockInputManufacturer", "MockInputName", "MockInputVersion"); + m_client->didAddOutputPort("MockOutputID", "MockOutputManufacturer", "MockOutputName", "MockOutputVersion"); + m_interfaces->delegate()->postTask(new DidStartSessionTask(this, m_client, m_interfaces->testRunner()->midiAccessorResult())); +} + +} // namespace WebTestRunner diff --git a/content/shell/renderer/test_runner/MockWebMIDIAccessor.h b/content/shell/renderer/test_runner/MockWebMIDIAccessor.h new file mode 100644 index 0000000..2953a5f --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebMIDIAccessor.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebMIDIAccessor_h +#define MockWebMIDIAccessor_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebMIDIAccessor.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +namespace blink { +class WebMIDIAccessorClient; +} + +namespace WebTestRunner { + +class TestInterfaces; + +class MockWebMIDIAccessor : public blink::WebMIDIAccessor, public blink::WebNonCopyable { +public: + explicit MockWebMIDIAccessor(blink::WebMIDIAccessorClient*, TestInterfaces*); + virtual ~MockWebMIDIAccessor(); + + // blink::WebMIDIAccessor implementation. + virtual void startSession() OVERRIDE; + virtual void sendMIDIData( + unsigned portIndex, + const unsigned char* data, + size_t length, + double timestamp) OVERRIDE { } + + // WebTask related methods + WebTaskList* taskList() { return &m_taskList; } + +private: + blink::WebMIDIAccessorClient* m_client; + WebTaskList m_taskList; + TestInterfaces* m_interfaces; +}; + +} // namespace WebTestRunner + +#endif // MockWebMIDIAccessor_h diff --git a/content/shell/renderer/test_runner/MockWebMediaStreamCenter.cpp b/content/shell/renderer/test_runner/MockWebMediaStreamCenter.cpp new file mode 100644 index 0000000..613e649 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebMediaStreamCenter.cpp @@ -0,0 +1,93 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebMediaStreamCenter.h" + +#include "third_party/WebKit/public/platform/WebAudioDestinationConsumer.h" +#include "third_party/WebKit/public/platform/WebMediaStream.h" +#include "third_party/WebKit/public/platform/WebMediaStreamCenterClient.h" +#include "third_party/WebKit/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrackSourcesRequest.h" +#include "third_party/WebKit/public/platform/WebSourceInfo.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +using namespace blink; + +namespace WebTestRunner { + +MockWebMediaStreamCenter::MockWebMediaStreamCenter(WebMediaStreamCenterClient* client) +{ +} + +bool MockWebMediaStreamCenter::getMediaStreamTrackSources(const WebMediaStreamTrackSourcesRequest& request) +{ + size_t size = 2; + WebVector<WebSourceInfo> results(size); + results[0].initialize("MockAudioDevice#1", WebSourceInfo::SourceKindAudio, "Mock audio device", WebSourceInfo::VideoFacingModeNone); + results[1].initialize("MockVideoDevice#1", WebSourceInfo::SourceKindVideo, "Mock video device", WebSourceInfo::VideoFacingModeEnvironment); + request.requestSucceeded(results); + return true; +} + +void MockWebMediaStreamCenter::didEnableMediaStreamTrack(const WebMediaStream&, const WebMediaStreamTrack& track) +{ + track.source().setReadyState(WebMediaStreamSource::ReadyStateLive); +} + +void MockWebMediaStreamCenter::didDisableMediaStreamTrack(const WebMediaStream&, const WebMediaStreamTrack& track) +{ + track.source().setReadyState(WebMediaStreamSource::ReadyStateMuted); +} + +bool MockWebMediaStreamCenter::didAddMediaStreamTrack(const WebMediaStream&, const WebMediaStreamTrack&) +{ + return true; +} + +bool MockWebMediaStreamCenter::didRemoveMediaStreamTrack(const WebMediaStream&, const WebMediaStreamTrack&) +{ + return true; +} + +void MockWebMediaStreamCenter::didStopLocalMediaStream(const WebMediaStream& stream) +{ + WebVector<WebMediaStreamTrack> tracks; + stream.audioTracks(tracks); + for (size_t i = 0; i < tracks.size(); ++i) + tracks[i].source().setReadyState(WebMediaStreamSource::ReadyStateEnded); + stream.videoTracks(tracks); + for (size_t i = 0; i < tracks.size(); ++i) + tracks[i].source().setReadyState(WebMediaStreamSource::ReadyStateEnded); +} + +bool MockWebMediaStreamCenter::didStopMediaStreamTrack(const blink::WebMediaStreamTrack& track) +{ + track.source().setReadyState(WebMediaStreamSource::ReadyStateEnded); + return true; +} + +class MockWebAudioDestinationConsumer : public WebAudioDestinationConsumer { +public: + virtual ~MockWebAudioDestinationConsumer() { } + virtual void setFormat(size_t numberOfChannels, float sampleRate) OVERRIDE { } + virtual void consumeAudio(const WebVector<const float*>&, size_t number_of_frames) OVERRIDE { } +}; + +void MockWebMediaStreamCenter::didCreateMediaStream(WebMediaStream& stream) +{ + WebVector<WebMediaStreamTrack> audioTracks; + stream.audioTracks(audioTracks); + for (size_t i = 0; i < audioTracks.size(); ++i) { + WebMediaStreamSource source = audioTracks[i].source(); + if (source.requiresAudioConsumer()) { + MockWebAudioDestinationConsumer* consumer = new MockWebAudioDestinationConsumer(); + source.addAudioConsumer(consumer); + source.removeAudioConsumer(consumer); + delete consumer; + } + } +} + +} diff --git a/content/shell/renderer/test_runner/MockWebMediaStreamCenter.h b/content/shell/renderer/test_runner/MockWebMediaStreamCenter.h new file mode 100644 index 0000000..178746d --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebMediaStreamCenter.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebMediaStreamCenter_h +#define MockWebMediaStreamCenter_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebMediaStreamCenter.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +namespace blink { +class WebMediaStreamCenterClient; +}; + +namespace WebTestRunner { + +class MockWebMediaStreamCenter : public blink::WebMediaStreamCenter, public blink::WebNonCopyable { +public: + explicit MockWebMediaStreamCenter(blink::WebMediaStreamCenterClient*); + + virtual bool getMediaStreamTrackSources(const blink::WebMediaStreamTrackSourcesRequest&) OVERRIDE; + virtual void didEnableMediaStreamTrack(const blink::WebMediaStream&, const blink::WebMediaStreamTrack&) OVERRIDE; + virtual void didDisableMediaStreamTrack(const blink::WebMediaStream&, const blink::WebMediaStreamTrack&) OVERRIDE; + virtual bool didAddMediaStreamTrack(const blink::WebMediaStream&, const blink::WebMediaStreamTrack&) OVERRIDE; + virtual bool didRemoveMediaStreamTrack(const blink::WebMediaStream&, const blink::WebMediaStreamTrack&) OVERRIDE; + virtual void didStopLocalMediaStream(const blink::WebMediaStream&) OVERRIDE; + virtual bool didStopMediaStreamTrack(const blink::WebMediaStreamTrack&) OVERRIDE; + virtual void didCreateMediaStream(blink::WebMediaStream&) OVERRIDE; + +private: + MockWebMediaStreamCenter() { } +}; + +} + +#endif // MockWebMediaStreamCenter_h diff --git a/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.cpp b/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.cpp new file mode 100644 index 0000000..223c16d --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.cpp @@ -0,0 +1,73 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h" + +#include <assert.h> + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/public/platform/WebRTCDTMFSenderHandlerClient.h" + +using namespace blink; + +namespace WebTestRunner { + +class DTMFSenderToneTask : public WebMethodTask<MockWebRTCDTMFSenderHandler> { +public: + DTMFSenderToneTask(MockWebRTCDTMFSenderHandler* object, WebRTCDTMFSenderHandlerClient* client) + : WebMethodTask<MockWebRTCDTMFSenderHandler>(object) + , m_client(client) + { + } + + virtual void runIfValid() OVERRIDE + { + WebString tones = m_object->currentToneBuffer(); + m_object->clearToneBuffer(); + m_client->didPlayTone(tones); + } + +private: + WebRTCDTMFSenderHandlerClient* m_client; +}; + +///////////////////// + +MockWebRTCDTMFSenderHandler::MockWebRTCDTMFSenderHandler(const WebMediaStreamTrack& track, WebTestDelegate* delegate) + : m_client(0) + , m_track(track) + , m_delegate(delegate) +{ +} + +void MockWebRTCDTMFSenderHandler::setClient(WebRTCDTMFSenderHandlerClient* client) +{ + m_client = client; +} + +WebString MockWebRTCDTMFSenderHandler::currentToneBuffer() +{ + return m_toneBuffer; +} + +bool MockWebRTCDTMFSenderHandler::canInsertDTMF() +{ + assert(m_client && !m_track.isNull()); + return m_track.source().type() == WebMediaStreamSource::TypeAudio && m_track.isEnabled() && m_track.source().readyState() == WebMediaStreamSource::ReadyStateLive; +} + +bool MockWebRTCDTMFSenderHandler::insertDTMF(const WebString& tones, long duration, long interToneGap) +{ + assert(m_client); + if (!canInsertDTMF()) + return false; + + m_toneBuffer = tones; + m_delegate->postTask(new DTMFSenderToneTask(this, m_client)); + m_delegate->postTask(new DTMFSenderToneTask(this, m_client)); + return true; +} + +} diff --git a/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h b/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h new file mode 100644 index 0000000..dd4a757 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebRTCDTMFSenderHandler_h +#define MockWebRTCDTMFSenderHandler_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebRTCDTMFSenderHandler.h" +#include "third_party/WebKit/public/platform/WebString.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +class MockWebRTCDTMFSenderHandler : public blink::WebRTCDTMFSenderHandler, public blink::WebNonCopyable { +public: + MockWebRTCDTMFSenderHandler(const blink::WebMediaStreamTrack&, WebTestDelegate*); + + virtual void setClient(blink::WebRTCDTMFSenderHandlerClient*) OVERRIDE; + + virtual blink::WebString currentToneBuffer() OVERRIDE; + + virtual bool canInsertDTMF() OVERRIDE; + virtual bool insertDTMF(const blink::WebString& tones, long duration, long interToneGap) OVERRIDE; + + // WebTask related methods + WebTaskList* taskList() { return &m_taskList; } + void clearToneBuffer() { m_toneBuffer.reset(); } + +private: + MockWebRTCDTMFSenderHandler(); + + blink::WebRTCDTMFSenderHandlerClient* m_client; + blink::WebMediaStreamTrack m_track; + blink::WebString m_toneBuffer; + WebTaskList m_taskList; + WebTestDelegate* m_delegate; +}; + +} + +#endif // MockWebRTCDTMFSenderHandler_h diff --git a/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.cpp b/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.cpp new file mode 100644 index 0000000..e57696b --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.cpp @@ -0,0 +1,108 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.h" + +#include <assert.h> + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebRTCDataChannelHandlerClient.h" + +using namespace blink; + +namespace WebTestRunner { + +class DataChannelReadyStateTask : public WebMethodTask<MockWebRTCDataChannelHandler> { +public: + DataChannelReadyStateTask(MockWebRTCDataChannelHandler* object, WebRTCDataChannelHandlerClient* dataChannelClient, WebRTCDataChannelHandlerClient::ReadyState state) + : WebMethodTask<MockWebRTCDataChannelHandler>(object) + , m_dataChannelClient(dataChannelClient) + , m_state(state) + { + } + + virtual void runIfValid() OVERRIDE + { + m_dataChannelClient->didChangeReadyState(m_state); + } + +private: + WebRTCDataChannelHandlerClient* m_dataChannelClient; + WebRTCDataChannelHandlerClient::ReadyState m_state; +}; + +///////////////////// + +MockWebRTCDataChannelHandler::MockWebRTCDataChannelHandler(WebString label, const WebRTCDataChannelInit& init, WebTestDelegate* delegate) + : m_client(0) + , m_label(label) + , m_init(init) + , m_delegate(delegate) +{ + m_reliable = (init.ordered && init.maxRetransmits == -1 && init.maxRetransmitTime == -1); +} + +void MockWebRTCDataChannelHandler::setClient(WebRTCDataChannelHandlerClient* client) +{ + m_client = client; + if (m_client) + m_delegate->postTask(new DataChannelReadyStateTask(this, m_client, WebRTCDataChannelHandlerClient::ReadyStateOpen)); +} + +bool MockWebRTCDataChannelHandler::ordered() const +{ + return m_init.ordered; +} + +unsigned short MockWebRTCDataChannelHandler::maxRetransmitTime() const +{ + return m_init.maxRetransmitTime; +} + +unsigned short MockWebRTCDataChannelHandler::maxRetransmits() const +{ + return m_init.maxRetransmits; +} + +WebString MockWebRTCDataChannelHandler::protocol() const +{ + return m_init.protocol; +} + +bool MockWebRTCDataChannelHandler::negotiated() const +{ + return m_init.negotiated; +} + +unsigned short MockWebRTCDataChannelHandler::id() const +{ + return m_init.id; +} + +unsigned long MockWebRTCDataChannelHandler::bufferedAmount() +{ + return 0; +} + +bool MockWebRTCDataChannelHandler::sendStringData(const WebString& data) +{ + assert(m_client); + m_client->didReceiveStringData(data); + return true; +} + +bool MockWebRTCDataChannelHandler::sendRawData(const char* data, size_t size) +{ + assert(m_client); + m_client->didReceiveRawData(data, size); + return true; +} + +void MockWebRTCDataChannelHandler::close() +{ + assert(m_client); + m_delegate->postTask(new DataChannelReadyStateTask(this, m_client, WebRTCDataChannelHandlerClient::ReadyStateClosed)); +} + +} diff --git a/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.h b/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.h new file mode 100644 index 0000000..ce99ff8 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.h @@ -0,0 +1,53 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebRTCDataChannelHandler_h +#define MockWebRTCDataChannelHandler_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebRTCDataChannelHandler.h" +#include "third_party/WebKit/public/platform/WebRTCDataChannelInit.h" +#include "third_party/WebKit/public/platform/WebString.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +class MockWebRTCDataChannelHandler : public blink::WebRTCDataChannelHandler, public blink::WebNonCopyable { +public: + MockWebRTCDataChannelHandler(blink::WebString label, const blink::WebRTCDataChannelInit&, WebTestDelegate*); + + virtual void setClient(blink::WebRTCDataChannelHandlerClient*) OVERRIDE; + virtual blink::WebString label() OVERRIDE { return m_label; } + virtual bool isReliable() OVERRIDE { return m_reliable; } + virtual bool ordered() const OVERRIDE; + virtual unsigned short maxRetransmitTime() const OVERRIDE; + virtual unsigned short maxRetransmits() const OVERRIDE; + virtual blink::WebString protocol() const OVERRIDE; + virtual bool negotiated() const OVERRIDE; + virtual unsigned short id() const OVERRIDE; + virtual unsigned long bufferedAmount() OVERRIDE; + virtual bool sendStringData(const blink::WebString&) OVERRIDE; + virtual bool sendRawData(const char*, size_t) OVERRIDE; + virtual void close() OVERRIDE; + + // WebTask related methods + WebTaskList* taskList() { return &m_taskList; } + +private: + MockWebRTCDataChannelHandler(); + + blink::WebRTCDataChannelHandlerClient* m_client; + blink::WebString m_label; + blink::WebRTCDataChannelInit m_init; + bool m_reliable; + WebTaskList m_taskList; + WebTestDelegate* m_delegate; +}; + +} + +#endif // MockWebRTCDataChannelHandler_h diff --git a/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.cpp b/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.cpp new file mode 100644 index 0000000..6b46ff7 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.cpp @@ -0,0 +1,281 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h" + +#include "content/shell/renderer/test_runner/MockConstraints.h" +#include "content/shell/renderer/test_runner/MockWebRTCDTMFSenderHandler.h" +#include "content/shell/renderer/test_runner/MockWebRTCDataChannelHandler.h" +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebMediaConstraints.h" +#include "third_party/WebKit/public/platform/WebMediaStream.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebRTCDataChannelInit.h" +#include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h" +#include "third_party/WebKit/public/platform/WebRTCStatsResponse.h" +#include "third_party/WebKit/public/platform/WebRTCVoidRequest.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +using namespace blink; + +namespace WebTestRunner { + +class RTCSessionDescriptionRequestSuccededTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RTCSessionDescriptionRequestSuccededTask(MockWebRTCPeerConnectionHandler* object, const WebRTCSessionDescriptionRequest& request, const WebRTCSessionDescription& result) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_request(request) + , m_result(result) + { + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestSucceeded(m_result); + } + +private: + WebRTCSessionDescriptionRequest m_request; + WebRTCSessionDescription m_result; +}; + +class RTCSessionDescriptionRequestFailedTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RTCSessionDescriptionRequestFailedTask(MockWebRTCPeerConnectionHandler* object, const WebRTCSessionDescriptionRequest& request) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_request(request) + { + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestFailed("TEST_ERROR"); + } + +private: + WebRTCSessionDescriptionRequest m_request; +}; + +class RTCStatsRequestSucceededTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RTCStatsRequestSucceededTask(MockWebRTCPeerConnectionHandler* object, const blink::WebRTCStatsRequest& request, const blink::WebRTCStatsResponse& response) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_request(request) + , m_response(response) + { + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestSucceeded(m_response); + } + +private: + blink::WebRTCStatsRequest m_request; + blink::WebRTCStatsResponse m_response; +}; + +class RTCVoidRequestTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RTCVoidRequestTask(MockWebRTCPeerConnectionHandler* object, const WebRTCVoidRequest& request, bool succeeded) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_request(request) + , m_succeeded(succeeded) + { + } + + virtual void runIfValid() OVERRIDE + { + if (m_succeeded) + m_request.requestSucceeded(); + else + m_request.requestFailed("TEST_ERROR"); + } + +private: + WebRTCVoidRequest m_request; + bool m_succeeded; +}; + +class RTCPeerConnectionStateTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RTCPeerConnectionStateTask(MockWebRTCPeerConnectionHandler* object, WebRTCPeerConnectionHandlerClient* client, WebRTCPeerConnectionHandlerClient::ICEConnectionState connectionState, WebRTCPeerConnectionHandlerClient::ICEGatheringState gatheringState) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_client(client) + , m_connectionState(connectionState) + , m_gatheringState(gatheringState) + { + } + + virtual void runIfValid() OVERRIDE + { + m_client->didChangeICEGatheringState(m_gatheringState); + m_client->didChangeICEConnectionState(m_connectionState); + } + +private: + WebRTCPeerConnectionHandlerClient* m_client; + WebRTCPeerConnectionHandlerClient::ICEConnectionState m_connectionState; + WebRTCPeerConnectionHandlerClient::ICEGatheringState m_gatheringState; +}; + +class RemoteDataChannelTask : public WebMethodTask<MockWebRTCPeerConnectionHandler> { +public: + RemoteDataChannelTask(MockWebRTCPeerConnectionHandler* object, WebRTCPeerConnectionHandlerClient* client, WebTestDelegate* delegate) + : WebMethodTask<MockWebRTCPeerConnectionHandler>(object) + , m_client(client) + , m_delegate(delegate) + { + } + + virtual void runIfValid() OVERRIDE + { + WebRTCDataChannelInit init; + WebRTCDataChannelHandler* remoteDataChannel = new MockWebRTCDataChannelHandler("MockRemoteDataChannel", init, m_delegate); + m_client->didAddRemoteDataChannel(remoteDataChannel); + } + +private: + WebRTCPeerConnectionHandlerClient* m_client; + WebTestDelegate* m_delegate; +}; + +///////////////////// + +MockWebRTCPeerConnectionHandler::MockWebRTCPeerConnectionHandler(WebRTCPeerConnectionHandlerClient* client, TestInterfaces* interfaces) + : m_client(client) + , m_stopped(false) + , m_streamCount(0) + , m_interfaces(interfaces) +{ +} + +bool MockWebRTCPeerConnectionHandler::initialize(const WebRTCConfiguration&, const WebMediaConstraints& constraints) +{ + if (MockConstraints::verifyConstraints(constraints)) { + m_interfaces->delegate()->postTask(new RTCPeerConnectionStateTask(this, m_client, WebRTCPeerConnectionHandlerClient::ICEConnectionStateCompleted, WebRTCPeerConnectionHandlerClient::ICEGatheringStateComplete)); + return true; + } + + return false; +} + +void MockWebRTCPeerConnectionHandler::createOffer(const WebRTCSessionDescriptionRequest& request, const WebMediaConstraints& constraints) +{ + WebString shouldSucceed; + if (constraints.getMandatoryConstraintValue("succeed", shouldSucceed) && shouldSucceed == "true") { + WebRTCSessionDescription sessionDescription; + sessionDescription.initialize("offer", "local"); + m_interfaces->delegate()->postTask(new RTCSessionDescriptionRequestSuccededTask(this, request, sessionDescription)); + } else + m_interfaces->delegate()->postTask(new RTCSessionDescriptionRequestFailedTask(this, request)); +} + +void MockWebRTCPeerConnectionHandler::createAnswer(const WebRTCSessionDescriptionRequest& request, const WebMediaConstraints&) +{ + if (!m_remoteDescription.isNull()) { + WebRTCSessionDescription sessionDescription; + sessionDescription.initialize("answer", "local"); + m_interfaces->delegate()->postTask(new RTCSessionDescriptionRequestSuccededTask(this, request, sessionDescription)); + } else + m_interfaces->delegate()->postTask(new RTCSessionDescriptionRequestFailedTask(this, request)); +} + +void MockWebRTCPeerConnectionHandler::setLocalDescription(const WebRTCVoidRequest& request, const WebRTCSessionDescription& localDescription) +{ + if (!localDescription.isNull() && localDescription.sdp() == "local") { + m_localDescription = localDescription; + m_interfaces->delegate()->postTask(new RTCVoidRequestTask(this, request, true)); + } else + m_interfaces->delegate()->postTask(new RTCVoidRequestTask(this, request, false)); +} + +void MockWebRTCPeerConnectionHandler::setRemoteDescription(const WebRTCVoidRequest& request, const WebRTCSessionDescription& remoteDescription) +{ + if (!remoteDescription.isNull() && remoteDescription.sdp() == "remote") { + m_remoteDescription = remoteDescription; + m_interfaces->delegate()->postTask(new RTCVoidRequestTask(this, request, true)); + } else + m_interfaces->delegate()->postTask(new RTCVoidRequestTask(this, request, false)); +} + +WebRTCSessionDescription MockWebRTCPeerConnectionHandler::localDescription() +{ + return m_localDescription; +} + +WebRTCSessionDescription MockWebRTCPeerConnectionHandler::remoteDescription() +{ + return m_remoteDescription; +} + +bool MockWebRTCPeerConnectionHandler::updateICE(const WebRTCConfiguration&, const WebMediaConstraints&) +{ + return true; +} + +bool MockWebRTCPeerConnectionHandler::addICECandidate(const WebRTCICECandidate& iceCandidate) +{ + m_client->didGenerateICECandidate(iceCandidate); + return true; +} + +bool MockWebRTCPeerConnectionHandler::addICECandidate(const WebRTCVoidRequest& request, const WebRTCICECandidate& iceCandidate) +{ + m_interfaces->delegate()->postTask(new RTCVoidRequestTask(this, request, true)); + return true; +} + +bool MockWebRTCPeerConnectionHandler::addStream(const WebMediaStream& stream, const WebMediaConstraints&) +{ + ++m_streamCount; + m_client->negotiationNeeded(); + return true; +} + +void MockWebRTCPeerConnectionHandler::removeStream(const WebMediaStream& stream) +{ + --m_streamCount; + m_client->negotiationNeeded(); +} + +void MockWebRTCPeerConnectionHandler::getStats(const WebRTCStatsRequest& request) +{ + WebRTCStatsResponse response = request.createResponse(); + double currentDate = m_interfaces->delegate()->getCurrentTimeInMillisecond(); + if (request.hasSelector()) { + // FIXME: There is no check that the fetched values are valid. + size_t reportIndex = response.addReport("Mock video", "ssrc", currentDate); + response.addStatistic(reportIndex, "type", "video"); + } else { + for (int i = 0; i < m_streamCount; ++i) { + size_t reportIndex = response.addReport("Mock audio", "ssrc", currentDate); + response.addStatistic(reportIndex, "type", "audio"); + reportIndex = response.addReport("Mock video", "ssrc", currentDate); + response.addStatistic(reportIndex, "type", "video"); + } + } + m_interfaces->delegate()->postTask(new RTCStatsRequestSucceededTask(this, request, response)); +} + +WebRTCDataChannelHandler* MockWebRTCPeerConnectionHandler::createDataChannel(const WebString& label, const blink::WebRTCDataChannelInit& init) +{ + m_interfaces->delegate()->postTask(new RemoteDataChannelTask(this, m_client, m_interfaces->delegate())); + + return new MockWebRTCDataChannelHandler(label, init, m_interfaces->delegate()); +} + +WebRTCDTMFSenderHandler* MockWebRTCPeerConnectionHandler::createDTMFSender(const WebMediaStreamTrack& track) +{ + return new MockWebRTCDTMFSenderHandler(track, m_interfaces->delegate()); +} + +void MockWebRTCPeerConnectionHandler::stop() +{ + m_stopped = true; +} + +} diff --git a/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h b/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h new file mode 100644 index 0000000..f44360e --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h @@ -0,0 +1,63 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebRTCPeerConnectionHandler_h +#define MockWebRTCPeerConnectionHandler_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandler.h" +#include "third_party/WebKit/public/platform/WebRTCSessionDescription.h" +#include "third_party/WebKit/public/platform/WebRTCSessionDescriptionRequest.h" +#include "third_party/WebKit/public/platform/WebRTCStatsRequest.h" + +namespace blink { +class WebRTCPeerConnectionHandlerClient; +}; + +namespace WebTestRunner { + +class TestInterfaces; + +class MockWebRTCPeerConnectionHandler : public blink::WebRTCPeerConnectionHandler, public blink::WebNonCopyable { +public: + MockWebRTCPeerConnectionHandler(blink::WebRTCPeerConnectionHandlerClient*, TestInterfaces*); + + virtual bool initialize(const blink::WebRTCConfiguration&, const blink::WebMediaConstraints&) OVERRIDE; + + virtual void createOffer(const blink::WebRTCSessionDescriptionRequest&, const blink::WebMediaConstraints&) OVERRIDE; + virtual void createAnswer(const blink::WebRTCSessionDescriptionRequest&, const blink::WebMediaConstraints&) OVERRIDE; + virtual void setLocalDescription(const blink::WebRTCVoidRequest&, const blink::WebRTCSessionDescription&) OVERRIDE; + virtual void setRemoteDescription(const blink::WebRTCVoidRequest&, const blink::WebRTCSessionDescription&) OVERRIDE; + virtual blink::WebRTCSessionDescription localDescription() OVERRIDE; + virtual blink::WebRTCSessionDescription remoteDescription() OVERRIDE; + virtual bool updateICE(const blink::WebRTCConfiguration&, const blink::WebMediaConstraints&) OVERRIDE; + virtual bool addICECandidate(const blink::WebRTCICECandidate&) OVERRIDE; + virtual bool addICECandidate(const blink::WebRTCVoidRequest&, const blink::WebRTCICECandidate&) OVERRIDE; + virtual bool addStream(const blink::WebMediaStream&, const blink::WebMediaConstraints&) OVERRIDE; + virtual void removeStream(const blink::WebMediaStream&) OVERRIDE; + virtual void getStats(const blink::WebRTCStatsRequest&) OVERRIDE; + virtual blink::WebRTCDataChannelHandler* createDataChannel(const blink::WebString& label, const blink::WebRTCDataChannelInit&) OVERRIDE; + virtual blink::WebRTCDTMFSenderHandler* createDTMFSender(const blink::WebMediaStreamTrack&) OVERRIDE; + virtual void stop() OVERRIDE; + + // WebTask related methods + WebTaskList* taskList() { return &m_taskList; } + +private: + MockWebRTCPeerConnectionHandler() { } + + blink::WebRTCPeerConnectionHandlerClient* m_client; + bool m_stopped; + WebTaskList m_taskList; + blink::WebRTCSessionDescription m_localDescription; + blink::WebRTCSessionDescription m_remoteDescription; + int m_streamCount; + TestInterfaces* m_interfaces; +}; + +} + +#endif // MockWebRTCPeerConnectionHandler_h diff --git a/content/shell/renderer/test_runner/MockWebSpeechInputController.cpp b/content/shell/renderer/test_runner/MockWebSpeechInputController.cpp new file mode 100644 index 0000000..f6263c7 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebSpeechInputController.cpp @@ -0,0 +1,186 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebSpeechInputController.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebSpeechInputListener.h" + +#if ENABLE_INPUT_SPEECH + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +WebSpeechInputResultArray makeRectResult(const WebRect& rect) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%d,%d,%d,%d", rect.x, rect.y, rect.width, rect.height); + + WebSpeechInputResult res; + res.assign(WebString::fromUTF8(static_cast<const char*>(buffer)), 1.0); + + WebSpeechInputResultArray results; + results.assign(&res, 1); + return results; +} + +} + +MockWebSpeechInputController::MockWebSpeechInputController(WebSpeechInputListener* listener) + : m_listener(listener) + , m_speechTask(0) + , m_recording(false) + , m_requestId(-1) + , m_dumpRect(false) + , m_delegate(0) +{ +} + +MockWebSpeechInputController::~MockWebSpeechInputController() +{ +} + +void MockWebSpeechInputController::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; +} + +void MockWebSpeechInputController::addMockRecognitionResult(const WebString& result, double confidence, const WebString& language) +{ + WebSpeechInputResult res; + res.assign(result, confidence); + + if (language.isEmpty()) + m_resultsForEmptyLanguage.push_back(res); + else { + string langString = language.utf8(); + if (m_recognitionResults.find(langString) == m_recognitionResults.end()) + m_recognitionResults[langString] = vector<WebSpeechInputResult>(); + m_recognitionResults[langString].push_back(res); + } +} + +void MockWebSpeechInputController::setDumpRect(bool dumpRect) +{ + m_dumpRect = dumpRect; +} + +void MockWebSpeechInputController::clearResults() +{ + m_resultsForEmptyLanguage.clear(); + m_recognitionResults.clear(); + m_dumpRect = false; +} + +bool MockWebSpeechInputController::startRecognition(int requestId, const WebRect& elementRect, const WebString& language, const WebString& grammar, const WebSecurityOrigin& origin) +{ + if (m_speechTask) + return false; + + m_requestId = requestId; + m_requestRect = elementRect; + m_recording = true; + m_language = language.utf8(); + + m_speechTask = new SpeechTask(this); + m_delegate->postTask(m_speechTask); + + return true; +} + +void MockWebSpeechInputController::cancelRecognition(int requestId) +{ + if (m_speechTask) { + BLINK_ASSERT(requestId == m_requestId); + + m_speechTask->stop(); + m_recording = false; + m_listener->didCompleteRecognition(m_requestId); + m_requestId = 0; + } +} + +void MockWebSpeechInputController::stopRecording(int requestId) +{ + BLINK_ASSERT(requestId == m_requestId); + if (m_speechTask && m_recording) { + m_speechTask->stop(); + speechTaskFired(); + } +} + +void MockWebSpeechInputController::speechTaskFired() +{ + if (m_recording) { + m_recording = false; + m_listener->didCompleteRecording(m_requestId); + + m_speechTask = new SpeechTask(this); + m_delegate->postTask(m_speechTask); + } else { + bool noResultsFound = false; + // We take a copy of the requestId here so that if scripts destroyed the input element + // inside one of the callbacks below, we'll still know what this session's requestId was. + int requestId = m_requestId; + m_requestId = 0; + + if (m_dumpRect) { + m_listener->setRecognitionResult(requestId, makeRectResult(m_requestRect)); + } else if (m_language.empty()) { + // Empty language case must be handled separately to avoid problems with HashMap and empty keys. + if (!m_resultsForEmptyLanguage.empty()) + m_listener->setRecognitionResult(requestId, m_resultsForEmptyLanguage); + else + noResultsFound = true; + } else { + if (m_recognitionResults.find(m_language) != m_recognitionResults.end()) + m_listener->setRecognitionResult(requestId, m_recognitionResults[m_language]); + else + noResultsFound = true; + } + + if (noResultsFound) { + // Can't avoid setting a result even if no result was set for the given language. + // This would avoid generating the events used to check the results and the test would timeout. + string error("error: no result found for language '"); + error.append(m_language); + error.append("'"); + + WebSpeechInputResult res; + res.assign(WebString::fromUTF8(error), 1.0); + + vector<WebSpeechInputResult> results; + results.push_back(res); + + m_listener->setRecognitionResult(requestId, results); + } + } +} + +MockWebSpeechInputController::SpeechTask::SpeechTask(MockWebSpeechInputController* mock) + : WebMethodTask<MockWebSpeechInputController>::WebMethodTask(mock) +{ +} + +void MockWebSpeechInputController::SpeechTask::stop() +{ + m_object->m_speechTask = 0; + cancel(); +} + +void MockWebSpeechInputController::SpeechTask::runIfValid() +{ + m_object->m_speechTask = 0; + m_object->speechTaskFired(); +} + +} + +#endif diff --git a/content/shell/renderer/test_runner/MockWebSpeechInputController.h b/content/shell/renderer/test_runner/MockWebSpeechInputController.h new file mode 100644 index 0000000..af493ce --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebSpeechInputController.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebSpeechInputController_h +#define MockWebSpeechInputController_h + +#include <map> +#include <string> +#include <vector> + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/web/WebSpeechInputController.h" +#include "third_party/WebKit/public/web/WebSpeechInputResult.h" + +namespace blink { +class WebSecurityOrigin; +class WebSpeechInputListener; +class WebString; +} + +namespace WebTestRunner { + +class WebTestDelegate; + +class MockWebSpeechInputController : public blink::WebSpeechInputController, public blink::WebNonCopyable { +public: + explicit MockWebSpeechInputController(blink::WebSpeechInputListener*); + ~MockWebSpeechInputController(); + + void addMockRecognitionResult(const blink::WebString& result, double confidence, const blink::WebString& language); + void setDumpRect(bool); + void clearResults(); + void setDelegate(WebTestDelegate*); + + // WebSpeechInputController implementation: + virtual bool startRecognition(int requestId, const blink::WebRect& elementRect, const blink::WebString& language, const blink::WebString& grammar, const blink::WebSecurityOrigin&) OVERRIDE; + virtual void cancelRecognition(int requestId) OVERRIDE; + virtual void stopRecording(int requestId) OVERRIDE; + + WebTaskList* taskList() { return &m_taskList; } + +private: + void speechTaskFired(); + + class SpeechTask : public WebMethodTask<MockWebSpeechInputController> { + public: + SpeechTask(MockWebSpeechInputController*); + void stop(); + + private: + virtual void runIfValid() OVERRIDE; + }; + + blink::WebSpeechInputListener* m_listener; + + WebTaskList m_taskList; + SpeechTask* m_speechTask; + + bool m_recording; + int m_requestId; + blink::WebRect m_requestRect; + std::string m_language; + + std::map<std::string, std::vector<blink::WebSpeechInputResult> > m_recognitionResults; + std::vector<blink::WebSpeechInputResult> m_resultsForEmptyLanguage; + bool m_dumpRect; + + WebTestDelegate* m_delegate; +}; + +} + +#endif // MockWebSpeechInputController_h diff --git a/content/shell/renderer/test_runner/MockWebSpeechRecognizer.cpp b/content/shell/renderer/test_runner/MockWebSpeechRecognizer.cpp new file mode 100644 index 0000000..2854a03 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebSpeechRecognizer.cpp @@ -0,0 +1,226 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/MockWebSpeechRecognizer.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/web/WebSpeechRecognitionResult.h" +#include "third_party/WebKit/public/web/WebSpeechRecognizerClient.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +// Task class for calling a client function that does not take any parameters. +typedef void (WebSpeechRecognizerClient::*ClientFunctionPointer)(const WebSpeechRecognitionHandle&); +class ClientCallTask : public MockWebSpeechRecognizer::Task { +public: + ClientCallTask(MockWebSpeechRecognizer* mock, ClientFunctionPointer function) + : MockWebSpeechRecognizer::Task(mock) + , m_function(function) + { + } + + virtual void run() OVERRIDE { (m_recognizer->client()->*m_function)(m_recognizer->handle()); } + +private: + ClientFunctionPointer m_function; +}; + +// Task for delivering a result event. +class ResultTask : public MockWebSpeechRecognizer::Task { +public: + ResultTask(MockWebSpeechRecognizer* mock, const WebString transcript, float confidence) + : MockWebSpeechRecognizer::Task(mock) + , m_transcript(transcript) + , m_confidence(confidence) + { + } + + virtual void run() OVERRIDE + { + WebVector<WebString> transcripts(static_cast<size_t>(1)); + WebVector<float> confidences(static_cast<size_t>(1)); + transcripts[0] = m_transcript; + confidences[0] = m_confidence; + WebVector<WebSpeechRecognitionResult> finalResults(static_cast<size_t>(1)); + WebVector<WebSpeechRecognitionResult> interimResults; + finalResults[0].assign(transcripts, confidences, true); + + m_recognizer->client()->didReceiveResults(m_recognizer->handle(), finalResults, interimResults); + } + +private: + WebString m_transcript; + float m_confidence; +}; + +// Task for delivering a nomatch event. +class NoMatchTask : public MockWebSpeechRecognizer::Task { +public: + NoMatchTask(MockWebSpeechRecognizer* mock) : MockWebSpeechRecognizer::Task(mock) { } + virtual void run() OVERRIDE { m_recognizer->client()->didReceiveNoMatch(m_recognizer->handle(), WebSpeechRecognitionResult()); } +}; + +// Task for delivering an error event. +class ErrorTask : public MockWebSpeechRecognizer::Task { +public: + ErrorTask(MockWebSpeechRecognizer* mock, WebSpeechRecognizerClient::ErrorCode code, const WebString& message) + : MockWebSpeechRecognizer::Task(mock) + , m_code(code) + , m_message(message) + { + } + + virtual void run() OVERRIDE { m_recognizer->client()->didReceiveError(m_recognizer->handle(), m_message, m_code); } + +private: + WebSpeechRecognizerClient::ErrorCode m_code; + WebString m_message; +}; + +} // namespace + +MockWebSpeechRecognizer::MockWebSpeechRecognizer() + : m_wasAborted(false) + , m_taskQueueRunning(false) + , m_delegate(0) +{ +} + +MockWebSpeechRecognizer::~MockWebSpeechRecognizer() +{ + clearTaskQueue(); +} + +void MockWebSpeechRecognizer::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; +} + +void MockWebSpeechRecognizer::start(const WebSpeechRecognitionHandle& handle, const WebSpeechRecognitionParams& params, WebSpeechRecognizerClient* client) +{ + m_wasAborted = false; + m_handle = handle; + m_client = client; + + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didStart)); + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didStartAudio)); + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didStartSound)); + + if (!m_mockTranscripts.empty()) { + BLINK_ASSERT(m_mockTranscripts.size() == m_mockConfidences.size()); + + for (size_t i = 0; i < m_mockTranscripts.size(); ++i) + m_taskQueue.push_back(new ResultTask(this, m_mockTranscripts[i], m_mockConfidences[i])); + + m_mockTranscripts.clear(); + m_mockConfidences.clear(); + } else + m_taskQueue.push_back(new NoMatchTask(this)); + + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didEndSound)); + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didEndAudio)); + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didEnd)); + + startTaskQueue(); +} + +void MockWebSpeechRecognizer::stop(const WebSpeechRecognitionHandle& handle, WebSpeechRecognizerClient* client) +{ + m_handle = handle; + m_client = client; + + // FIXME: Implement. + BLINK_ASSERT_NOT_REACHED(); +} + +void MockWebSpeechRecognizer::abort(const WebSpeechRecognitionHandle& handle, WebSpeechRecognizerClient* client) +{ + m_handle = handle; + m_client = client; + + clearTaskQueue(); + m_wasAborted = true; + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didEnd)); + startTaskQueue(); +} + +void MockWebSpeechRecognizer::addMockResult(const WebString& transcript, float confidence) +{ + m_mockTranscripts.push_back(transcript); + m_mockConfidences.push_back(confidence); +} + +void MockWebSpeechRecognizer::setError(const WebString& error, const WebString& message) +{ + WebSpeechRecognizerClient::ErrorCode code; + if (error == "OtherError") + code = WebSpeechRecognizerClient::OtherError; + else if (error == "NoSpeechError") + code = WebSpeechRecognizerClient::NoSpeechError; + else if (error == "AbortedError") + code = WebSpeechRecognizerClient::AbortedError; + else if (error == "AudioCaptureError") + code = WebSpeechRecognizerClient::AudioCaptureError; + else if (error == "NetworkError") + code = WebSpeechRecognizerClient::NetworkError; + else if (error == "NotAllowedError") + code = WebSpeechRecognizerClient::NotAllowedError; + else if (error == "ServiceNotAllowedError") + code = WebSpeechRecognizerClient::ServiceNotAllowedError; + else if (error == "BadGrammarError") + code = WebSpeechRecognizerClient::BadGrammarError; + else if (error == "LanguageNotSupportedError") + code = WebSpeechRecognizerClient::LanguageNotSupportedError; + else + return; + + clearTaskQueue(); + m_taskQueue.push_back(new ErrorTask(this, code, message)); + m_taskQueue.push_back(new ClientCallTask(this, &WebSpeechRecognizerClient::didEnd)); + startTaskQueue(); +} + +void MockWebSpeechRecognizer::startTaskQueue() +{ + if (m_taskQueueRunning) + return; + m_delegate->postTask(new StepTask(this)); + m_taskQueueRunning = true; +} + +void MockWebSpeechRecognizer::clearTaskQueue() +{ + while (!m_taskQueue.empty()) { + delete m_taskQueue.front(); + m_taskQueue.pop_front(); + } + m_taskQueueRunning = false; +} + +void MockWebSpeechRecognizer::StepTask::runIfValid() +{ + if (m_object->m_taskQueue.empty()) { + m_object->m_taskQueueRunning = false; + return; + } + + Task* task = m_object->m_taskQueue.front(); + m_object->m_taskQueue.pop_front(); + task->run(); + delete task; + + if (m_object->m_taskQueue.empty()) { + m_object->m_taskQueueRunning = false; + return; + } + + m_object->m_delegate->postTask(new StepTask(m_object)); +} + +} diff --git a/content/shell/renderer/test_runner/MockWebSpeechRecognizer.h b/content/shell/renderer/test_runner/MockWebSpeechRecognizer.h new file mode 100644 index 0000000..d249641 --- /dev/null +++ b/content/shell/renderer/test_runner/MockWebSpeechRecognizer.h @@ -0,0 +1,84 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebSpeechRecognizer_h +#define MockWebSpeechRecognizer_h + +#include <deque> +#include <vector> + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebSpeechRecognizer.h" + +namespace blink { +class WebSpeechRecognitionHandle; +class WebSpeechRecognitionParams; +class WebSpeechRecognizerClient; +} + +namespace WebTestRunner { + +class WebTestDelegate; + +class MockWebSpeechRecognizer : public blink::WebSpeechRecognizer, public blink::WebNonCopyable { +public: + MockWebSpeechRecognizer(); + ~MockWebSpeechRecognizer(); + + void setDelegate(WebTestDelegate*); + + // WebSpeechRecognizer implementation: + virtual void start(const blink::WebSpeechRecognitionHandle&, const blink::WebSpeechRecognitionParams&, blink::WebSpeechRecognizerClient*) OVERRIDE; + virtual void stop(const blink::WebSpeechRecognitionHandle&, blink::WebSpeechRecognizerClient*) OVERRIDE; + virtual void abort(const blink::WebSpeechRecognitionHandle&, blink::WebSpeechRecognizerClient*) OVERRIDE; + + // Methods accessed by layout tests: + void addMockResult(const blink::WebString& transcript, float confidence); + void setError(const blink::WebString& error, const blink::WebString& message); + bool wasAborted() const { return m_wasAborted; } + + // Methods accessed from Task objects: + blink::WebSpeechRecognizerClient* client() { return m_client; } + blink::WebSpeechRecognitionHandle& handle() { return m_handle; } + WebTaskList* taskList() { return &m_taskList; } + + class Task { + public: + Task(MockWebSpeechRecognizer* recognizer) : m_recognizer(recognizer) { } + virtual ~Task() { } + virtual void run() = 0; + protected: + MockWebSpeechRecognizer* m_recognizer; + }; + +private: + void startTaskQueue(); + void clearTaskQueue(); + + WebTaskList m_taskList; + blink::WebSpeechRecognitionHandle m_handle; + blink::WebSpeechRecognizerClient* m_client; + std::vector<blink::WebString> m_mockTranscripts; + std::vector<float> m_mockConfidences; + bool m_wasAborted; + + // Queue of tasks to be run. + std::deque<Task*> m_taskQueue; + bool m_taskQueueRunning; + + WebTestDelegate* m_delegate; + + // Task for stepping the queue. + class StepTask : public WebMethodTask<MockWebSpeechRecognizer> { + public: + StepTask(MockWebSpeechRecognizer* object) : WebMethodTask<MockWebSpeechRecognizer>(object) { } + virtual void runIfValid() OVERRIDE; + }; +}; + +} + +#endif // MockWebSpeechRecognizer_h diff --git a/content/shell/renderer/test_runner/NotificationPresenter.cpp b/content/shell/renderer/test_runner/NotificationPresenter.cpp new file mode 100644 index 0000000..b3c9963 --- /dev/null +++ b/content/shell/renderer/test_runner/NotificationPresenter.cpp @@ -0,0 +1,141 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/NotificationPresenter.h" + +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/Platform.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebNotificationPermissionCallback.h" +#include "third_party/WebKit/public/web/WebSecurityOrigin.h" +#include "url/gurl.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +WebString identifierForNotification(const WebNotification& notification) +{ + if (notification.isHTML()) + return notification.url().spec().utf16(); + return notification.title(); +} + +void deferredDisplayDispatch(void* context) +{ + WebNotification* notification = static_cast<WebNotification*>(context); + notification->dispatchDisplayEvent(); + delete notification; +} + +} + +NotificationPresenter::NotificationPresenter() + : m_delegate(0) +{ +} + +NotificationPresenter::~NotificationPresenter() +{ +} + +void NotificationPresenter::grantPermission(const WebString& origin) +{ + m_allowedOrigins.insert(origin.utf8()); +} + +bool NotificationPresenter::simulateClick(const WebString& title) +{ + string id(title.utf8()); + if (m_activeNotifications.find(id) == m_activeNotifications.end()) + return false; + + const WebNotification& notification = m_activeNotifications.find(id)->second; + WebNotification eventTarget(notification); + eventTarget.dispatchClickEvent(); + return true; +} + +void NotificationPresenter::cancelAllActiveNotifications() +{ + while (!m_activeNotifications.empty()) { + const WebNotification& notification = m_activeNotifications.begin()->second; + cancel(notification); + } +} + +// The output from all these methods matches what DumpRenderTree produces. +bool NotificationPresenter::show(const WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + if (!notification.replaceId().isEmpty()) { + string replaceId(notification.replaceId().utf8()); + if (m_replacements.find(replaceId) != m_replacements.end()) + m_delegate->printMessage(string("REPLACING NOTIFICATION ") + m_replacements.find(replaceId)->second + "\n"); + + m_replacements[replaceId] = identifier.utf8(); + } + + if (notification.isHTML()) + m_delegate->printMessage(string("DESKTOP NOTIFICATION: contents at ") + string(notification.url().spec()) + "\n"); + else { + m_delegate->printMessage("DESKTOP NOTIFICATION:"); + m_delegate->printMessage(notification.direction() == WebTextDirectionRightToLeft ? "(RTL)" : ""); + m_delegate->printMessage(" icon "); + m_delegate->printMessage(notification.iconURL().isEmpty() ? "" : notification.iconURL().spec().data()); + m_delegate->printMessage(", title "); + m_delegate->printMessage(notification.title().isEmpty() ? "" : notification.title().utf8().data()); + m_delegate->printMessage(", text "); + m_delegate->printMessage(notification.body().isEmpty() ? "" : notification.body().utf8().data()); + m_delegate->printMessage("\n"); + } + + string id(identifier.utf8()); + m_activeNotifications[id] = notification; + + Platform::current()->callOnMainThread(deferredDisplayDispatch, new WebNotification(notification)); + return true; +} + +void NotificationPresenter::cancel(const WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + m_delegate->printMessage(string("DESKTOP NOTIFICATION CLOSED: ") + string(identifier.utf8()) + "\n"); + WebNotification eventTarget(notification); + eventTarget.dispatchCloseEvent(false); + + string id(identifier.utf8()); + m_activeNotifications.erase(id); +} + +void NotificationPresenter::objectDestroyed(const blink::WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + string id(identifier.utf8()); + m_activeNotifications.erase(id); +} + +WebNotificationPresenter::Permission NotificationPresenter::checkPermission(const WebSecurityOrigin& origin) +{ + // Check with the layout test controller + WebString originString = origin.toString(); + bool allowed = m_allowedOrigins.find(string(originString.utf8())) != m_allowedOrigins.end(); + return allowed ? WebNotificationPresenter::PermissionAllowed + : WebNotificationPresenter::PermissionDenied; +} + +void NotificationPresenter::requestPermission( + const WebSecurityOrigin& origin, + WebNotificationPermissionCallback* callback) +{ + m_delegate->printMessage("DESKTOP NOTIFICATION PERMISSION REQUESTED: " + string(origin.toString().utf8()) + "\n"); + callback->permissionRequestComplete(); +} + +} diff --git a/content/shell/renderer/test_runner/NotificationPresenter.h b/content/shell/renderer/test_runner/NotificationPresenter.h new file mode 100644 index 0000000..a680a3a --- /dev/null +++ b/content/shell/renderer/test_runner/NotificationPresenter.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NotificationPresenter_h +#define NotificationPresenter_h + +#include <map> +#include <set> +#include <string> + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebNotification.h" +#include "third_party/WebKit/public/web/WebNotificationPresenter.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +// A class that implements WebNotificationPresenter for the TestRunner library. +class NotificationPresenter : public blink::WebNotificationPresenter, public blink::WebNonCopyable { +public: + NotificationPresenter(); + virtual ~NotificationPresenter(); + + void setDelegate(WebTestDelegate* delegate) { m_delegate = delegate; } + + // Called by the TestRunner to simulate a user granting permission. + void grantPermission(const blink::WebString& origin); + + // Called by the TestRunner to simulate a user clicking on a notification. + bool simulateClick(const blink::WebString& notificationIdentifier); + + // Called by the TestRunner to cancel all active notications. + void cancelAllActiveNotifications(); + + // blink::WebNotificationPresenter interface + virtual bool show(const blink::WebNotification&); + virtual void cancel(const blink::WebNotification&); + virtual void objectDestroyed(const blink::WebNotification&); + virtual Permission checkPermission(const blink::WebSecurityOrigin&); + virtual void requestPermission(const blink::WebSecurityOrigin&, blink::WebNotificationPermissionCallback*); + + void reset() { m_allowedOrigins.clear(); } + +private: + WebTestDelegate* m_delegate; + + // Set of allowed origins. + std::set<std::string> m_allowedOrigins; + + // Map of active notifications. + std::map<std::string, blink::WebNotification> m_activeNotifications; + + // Map of active replacement IDs to the titles of those notifications + std::map<std::string, std::string> m_replacements; +}; + +} + +#endif // NotificationPresenter_h diff --git a/content/shell/renderer/test_runner/OWNERS b/content/shell/renderer/test_runner/OWNERS new file mode 100644 index 0000000..cf00f71 --- /dev/null +++ b/content/shell/renderer/test_runner/OWNERS @@ -0,0 +1 @@ +abarth@chromium.org diff --git a/content/shell/renderer/test_runner/SpellCheckClient.cpp b/content/shell/renderer/test_runner/SpellCheckClient.cpp new file mode 100644 index 0000000..3ae2f32 --- /dev/null +++ b/content/shell/renderer/test_runner/SpellCheckClient.cpp @@ -0,0 +1,143 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/SpellCheckClient.h" + +#include "content/shell/renderer/test_runner/MockGrammarCheck.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "third_party/WebKit/public/web/WebTextCheckingCompletion.h" +#include "third_party/WebKit/public/web/WebTextCheckingResult.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +class HostMethodTask : public WebMethodTask<SpellCheckClient> { +public: + typedef void (SpellCheckClient::*CallbackMethodType)(); + HostMethodTask(SpellCheckClient* object, CallbackMethodType callback) + : WebMethodTask<SpellCheckClient>(object) + , m_callback(callback) + { } + + virtual void runIfValid() { (m_object->*m_callback)(); } + +private: + CallbackMethodType m_callback; +}; + +} + +SpellCheckClient::SpellCheckClient(WebTestProxyBase* webTestProxy) + : m_lastRequestedTextCheckingCompletion(0) + , m_webTestProxy(webTestProxy) +{ +} + +SpellCheckClient::~SpellCheckClient() +{ +} + +void SpellCheckClient::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; +} + +// blink::WebSpellCheckClient +void SpellCheckClient::spellCheck(const WebString& text, int& misspelledOffset, int& misspelledLength, WebVector<WebString>* optionalSuggestions) +{ + // Check the spelling of the given text. + m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength); +} + +void SpellCheckClient::checkTextOfParagraph(const WebString& text, WebTextCheckingTypeMask mask, WebVector<WebTextCheckingResult>* webResults) +{ + vector<WebTextCheckingResult> results; + if (mask & WebTextCheckingTypeSpelling) { + size_t offset = 0; + string16 data = text; + while (offset < data.length()) { + int misspelledPosition = 0; + int misspelledLength = 0; + m_spellcheck.spellCheckWord(data.substr(offset), &misspelledPosition, &misspelledLength); + if (!misspelledLength) + break; + WebTextCheckingResult result; + result.decoration = WebTextDecorationTypeSpelling; + result.location = offset + misspelledPosition; + result.length = misspelledLength; + results.push_back(result); + offset += misspelledPosition + misspelledLength; + } + } + if (mask & WebTextCheckingTypeGrammar) + MockGrammarCheck::checkGrammarOfString(text, &results); + webResults->assign(results); +} + +void SpellCheckClient::requestCheckingOfText( + const WebString& text, + const WebVector<uint32_t>& markers, + const WebVector<unsigned>& markerOffsets, + WebTextCheckingCompletion* completion) +{ + if (text.isEmpty()) { + if (completion) + completion->didCancelCheckingText(); + return; + } + + if (m_lastRequestedTextCheckingCompletion) + m_lastRequestedTextCheckingCompletion->didCancelCheckingText(); + + m_lastRequestedTextCheckingCompletion = completion; + m_lastRequestedTextCheckString = text; + if (m_spellcheck.hasInCache(text)) + finishLastTextCheck(); + else + m_delegate->postDelayedTask(new HostMethodTask(this, &SpellCheckClient::finishLastTextCheck), 0); +} + +void SpellCheckClient::finishLastTextCheck() +{ + if (!m_lastRequestedTextCheckingCompletion) + return; + vector<WebTextCheckingResult> results; + int offset = 0; + string16 text = m_lastRequestedTextCheckString; + if (!m_spellcheck.isMultiWordMisspelling(WebString(text), &results)) { + while (text.length()) { + int misspelledPosition = 0; + int misspelledLength = 0; + m_spellcheck.spellCheckWord(WebString(text), &misspelledPosition, &misspelledLength); + if (!misspelledLength) + break; + WebVector<WebString> suggestions; + m_spellcheck.fillSuggestionList(WebString(text.substr(misspelledPosition, misspelledLength)), &suggestions); + results.push_back(WebTextCheckingResult(WebTextDecorationTypeSpelling, offset + misspelledPosition, misspelledLength, suggestions.isEmpty() ? WebString() : suggestions[0])); + text = text.substr(misspelledPosition + misspelledLength); + offset += misspelledPosition + misspelledLength; + } + MockGrammarCheck::checkGrammarOfString(m_lastRequestedTextCheckString, &results); + } + m_lastRequestedTextCheckingCompletion->didFinishCheckingText(results); + m_lastRequestedTextCheckingCompletion = 0; + + m_webTestProxy->postSpellCheckEvent(WebString("finishLastTextCheck")); +} + +WebString SpellCheckClient::autoCorrectWord(const WebString&) +{ + // Returns an empty string as Mac WebKit ('WebKitSupport/WebEditorClient.mm') + // does. (If this function returns a non-empty string, WebKit replaces the + // given misspelled string with the result one. This process executes some + // editor commands and causes layout-test failures.) + return WebString(); +} + +} diff --git a/content/shell/renderer/test_runner/SpellCheckClient.h b/content/shell/renderer/test_runner/SpellCheckClient.h new file mode 100644 index 0000000..bc8ad9d --- /dev/null +++ b/content/shell/renderer/test_runner/SpellCheckClient.h @@ -0,0 +1,55 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SpellCheckClient_h +#define SpellCheckClient_h + +#include "content/shell/renderer/test_runner/MockSpellCheck.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebSpellCheckClient.h" + +namespace WebTestRunner { + +class WebTestDelegate; +class WebTestProxyBase; + +class SpellCheckClient : public blink::WebSpellCheckClient, public blink::WebNonCopyable { +public: + explicit SpellCheckClient(WebTestProxyBase*); + virtual ~SpellCheckClient(); + + void setDelegate(WebTestDelegate*); + + WebTaskList* taskList() { return &m_taskList; } + MockSpellCheck* mockSpellCheck() { return &m_spellcheck; } + + // blink::WebSpellCheckClient implementation. + virtual void spellCheck(const blink::WebString&, int& offset, int& length, blink::WebVector<blink::WebString>* optionalSuggestions); + virtual void checkTextOfParagraph(const blink::WebString&, blink::WebTextCheckingTypeMask, blink::WebVector<blink::WebTextCheckingResult>*); + virtual void requestCheckingOfText(const blink::WebString&, + const blink::WebVector<uint32_t>&, + const blink::WebVector<unsigned>&, + blink::WebTextCheckingCompletion*); + virtual blink::WebString autoCorrectWord(const blink::WebString&); + +private: + void finishLastTextCheck(); + + // The mock spellchecker used in spellCheck(). + MockSpellCheck m_spellcheck; + + blink::WebString m_lastRequestedTextCheckString; + blink::WebTextCheckingCompletion* m_lastRequestedTextCheckingCompletion; + + WebTaskList m_taskList; + + WebTestDelegate* m_delegate; + + WebTestProxyBase* m_webTestProxy; +}; + +} + +#endif // SpellCheckClient_h diff --git a/content/shell/renderer/test_runner/TestCommon.cpp b/content/shell/renderer/test_runner/TestCommon.cpp new file mode 100644 index 0000000..691ecdd --- /dev/null +++ b/content/shell/renderer/test_runner/TestCommon.cpp @@ -0,0 +1,37 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/TestCommon.h" + +using namespace std; + +namespace WebTestRunner { + +namespace { + +const char layoutTestsPattern[] = "/LayoutTests/"; +const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1; +const char fileUrlPattern[] = "file:/"; +const char fileTestPrefix[] = "(file test):"; +const char dataUrlPattern[] = "data:"; +const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1; + +} + +string normalizeLayoutTestURL(const string& url) +{ + string result = url; + size_t pos; + if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) { + // adjust file URLs to match upstream results. + result.replace(0, pos + layoutTestsPatternSize, fileTestPrefix); + } else if (!url.find(dataUrlPattern)) { + // URL-escape data URLs to match results upstream. + string path = url.substr(dataUrlPatternSize); + result.replace(dataUrlPatternSize, url.length(), path); + } + return result; +} + +} diff --git a/content/shell/renderer/test_runner/TestCommon.h b/content/shell/renderer/test_runner/TestCommon.h new file mode 100644 index 0000000..5e83c47 --- /dev/null +++ b/content/shell/renderer/test_runner/TestCommon.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TestCommon_h +#define TestCommon_h + +#include <stdio.h> +#include <string> + +#include "base/compiler_specific.h" +#include "third_party/WebKit/public/platform/WebCommon.h" + +#if defined(WIN32) +#define snprintf(str, size, ...) _snprintf_s(str, size, size, __VA_ARGS__) +#endif + +namespace WebTestRunner { + +inline bool isASCIIAlpha(char ch) { return (ch | 0x20) >= 'a' && (ch | 0x20) <= 'z'; } + +inline bool isNotASCIIAlpha(char ch) { return !isASCIIAlpha(ch); } + +std::string normalizeLayoutTestURL(const std::string& url); + +} + +#endif // TestCommon_h diff --git a/content/shell/renderer/test_runner/TestInterfaces.cpp b/content/shell/renderer/test_runner/TestInterfaces.cpp new file mode 100644 index 0000000..6f1fc4d --- /dev/null +++ b/content/shell/renderer/test_runner/TestInterfaces.cpp @@ -0,0 +1,191 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/TestInterfaces.h" + +#include <string> + +#include "content/shell/renderer/test_runner/AccessibilityController.h" +#include "content/shell/renderer/test_runner/EventSender.h" +#include "content/shell/renderer/test_runner/GamepadController.h" +#include "content/shell/renderer/test_runner/TestRunner.h" +#include "content/shell/renderer/test_runner/TextInputController.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" +#include "third_party/WebKit/public/web/WebView.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +TestInterfaces::TestInterfaces() + : m_accessibilityController(new AccessibilityController()) + , m_eventSender(new EventSender(this)) + , m_gamepadController(new GamepadController()) + , m_textInputController(new TextInputController()) + , m_testRunner(new TestRunner(this)) + , m_delegate(0) +{ + blink::setLayoutTestMode(true); + + // NOTE: please don't put feature specific enable flags here, + // instead add them to RuntimeEnabledFeatures.in + + resetAll(); +} + +TestInterfaces::~TestInterfaces() +{ + m_accessibilityController->setWebView(0); + m_eventSender->setWebView(0); + // m_gamepadController doesn't depend on WebView. + m_textInputController->setWebView(0); + m_testRunner->setWebView(0, 0); + + m_accessibilityController->setDelegate(0); + m_eventSender->setDelegate(0); + m_gamepadController->setDelegate(0); + // m_textInputController doesn't depend on WebTestDelegate. + m_testRunner->setDelegate(0); +} + +void TestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) +{ + m_proxy = proxy; + m_accessibilityController->setWebView(webView); + m_eventSender->setWebView(webView); + // m_gamepadController doesn't depend on WebView. + m_textInputController->setWebView(webView); + m_testRunner->setWebView(webView, proxy); +} + +void TestInterfaces::setDelegate(WebTestDelegate* delegate) +{ + m_accessibilityController->setDelegate(delegate); + m_eventSender->setDelegate(delegate); + m_gamepadController->setDelegate(delegate); + // m_textInputController doesn't depend on WebTestDelegate. + m_testRunner->setDelegate(delegate); + m_delegate = delegate; +} + +void TestInterfaces::bindTo(WebFrame* frame) +{ + m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController")); + m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender")); + m_gamepadController->bindToJavascript(frame, WebString::fromUTF8("gamepadController")); + m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController")); + m_testRunner->bindToJavascript(frame, WebString::fromUTF8("testRunner")); + m_testRunner->bindToJavascript(frame, WebString::fromUTF8("layoutTestController")); +} + +void TestInterfaces::resetTestHelperControllers() +{ + m_accessibilityController->reset(); + m_eventSender->reset(); + m_gamepadController->reset(); + // m_textInputController doesn't have any state to reset. + WebCache::clear(); +} + +void TestInterfaces::resetAll() +{ + resetTestHelperControllers(); + m_testRunner->reset(); +} + +void TestInterfaces::setTestIsRunning(bool running) +{ + m_testRunner->setTestIsRunning(running); +} + +void TestInterfaces::configureForTestWithURL(const WebURL& testURL, bool generatePixels) +{ + string spec = GURL(testURL).spec(); + m_testRunner->setShouldGeneratePixelResults(generatePixels); + if (spec.find("loading/") != string::npos) + m_testRunner->setShouldDumpFrameLoadCallbacks(true); + if (spec.find("/dumpAsText/") != string::npos) { + m_testRunner->setShouldDumpAsText(true); + m_testRunner->setShouldGeneratePixelResults(false); + } + if (spec.find("/inspector/") != string::npos) + m_testRunner->showDevTools(); + if (spec.find("/viewsource/") != string::npos) { + m_testRunner->setShouldEnableViewSource(true); + m_testRunner->setShouldGeneratePixelResults(false); + m_testRunner->setShouldDumpAsMarkup(true); + } +} + +void TestInterfaces::windowOpened(WebTestProxyBase* proxy) +{ + m_windowList.push_back(proxy); +} + +void TestInterfaces::windowClosed(WebTestProxyBase* proxy) +{ + vector<WebTestProxyBase*>::iterator pos = find(m_windowList.begin(), m_windowList.end(), proxy); + if (pos == m_windowList.end()) { + BLINK_ASSERT_NOT_REACHED(); + return; + } + m_windowList.erase(pos); +} + +AccessibilityController* TestInterfaces::accessibilityController() +{ + return m_accessibilityController.get(); +} + +EventSender* TestInterfaces::eventSender() +{ + return m_eventSender.get(); +} + +TestRunner* TestInterfaces::testRunner() +{ + return m_testRunner.get(); +} + +WebTestDelegate* TestInterfaces::delegate() +{ + return m_delegate; +} + +WebTestProxyBase* TestInterfaces::proxy() +{ + return m_proxy; +} + +const vector<WebTestProxyBase*>& TestInterfaces::windowList() +{ + return m_windowList; +} + +WebThemeEngine* TestInterfaces::themeEngine() +{ +#if defined(USE_DEFAULT_RENDER_THEME) || !(defined(WIN32) || defined(__APPLE__) || defined(ANDROID)) + if (!m_themeEngine.get()) + m_themeEngine.reset(new WebTestThemeEngineMock()); + return m_themeEngine.get(); +#elif defined(WIN32) + if (!m_themeEngine.get()) + m_themeEngine.reset(new WebTestThemeEngineWin()); + return m_themeEngine.get(); +#elif defined(__APPLE__) + if (!m_themeEngine.get()) + m_themeEngine.reset(new WebTestThemeEngineMac()); + return m_themeEngine.get(); +#else + return 0; +#endif +} + +} diff --git a/content/shell/renderer/test_runner/TestInterfaces.h b/content/shell/renderer/test_runner/TestInterfaces.h new file mode 100644 index 0000000..fccdc1c --- /dev/null +++ b/content/shell/renderer/test_runner/TestInterfaces.h @@ -0,0 +1,83 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TestInterfaces_h +#define TestInterfaces_h + +#include <vector> + +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +#if defined(USE_DEFAULT_RENDER_THEME) +#include "content/shell/renderer/test_runner/WebTestThemeEngineMock.h" +#elif defined(WIN32) +#include "content/shell/renderer/test_runner/WebTestThemeEngineWin.h" +#elif defined(__APPLE__) +#include "content/shell/renderer/test_runner/WebTestThemeEngineMac.h" +#endif + +namespace blink { +class WebFrame; +class WebThemeEngine; +class WebURL; +class WebView; +} + +namespace WebTestRunner { + +class AccessibilityController; +class EventSender; +class GamepadController; +class TestRunner; +class TextInputController; +class WebTestDelegate; +class WebTestProxyBase; + +class TestInterfaces : public blink::WebNonCopyable { +public: + TestInterfaces(); + ~TestInterfaces(); + + void setWebView(blink::WebView*, WebTestProxyBase*); + void setDelegate(WebTestDelegate*); + void bindTo(blink::WebFrame*); + void resetTestHelperControllers(); + void resetAll(); + void setTestIsRunning(bool); + void configureForTestWithURL(const blink::WebURL&, bool generatePixels); + + void windowOpened(WebTestProxyBase*); + void windowClosed(WebTestProxyBase*); + + AccessibilityController* accessibilityController(); + EventSender* eventSender(); + TestRunner* testRunner(); + WebTestDelegate* delegate(); + WebTestProxyBase* proxy(); + const std::vector<WebTestProxyBase*>& windowList(); + blink::WebThemeEngine* themeEngine(); + +private: + WebScopedPtr<AccessibilityController> m_accessibilityController; + WebScopedPtr<EventSender> m_eventSender; + WebScopedPtr<GamepadController> m_gamepadController; + WebScopedPtr<TextInputController> m_textInputController; + WebScopedPtr<TestRunner> m_testRunner; + WebTestDelegate* m_delegate; + WebTestProxyBase* m_proxy; + + std::vector<WebTestProxyBase*> m_windowList; +#if defined(USE_DEFAULT_RENDER_THEME) + WebScopedPtr<WebTestThemeEngineMock> m_themeEngine; +#elif defined(WIN32) + WebScopedPtr<WebTestThemeEngineWin> m_themeEngine; +#elif defined(__APPLE__) + WebScopedPtr<WebTestThemeEngineMac> m_themeEngine; +#endif +}; + +} + +#endif // TestInterfaces_h diff --git a/content/shell/renderer/test_runner/TestPlugin.cpp b/content/shell/renderer/test_runner/TestPlugin.cpp new file mode 100644 index 0000000..eada1dc --- /dev/null +++ b/content/shell/renderer/test_runner/TestPlugin.cpp @@ -0,0 +1,560 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/TestPlugin.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/Platform.h" +#include "third_party/WebKit/public/platform/WebCompositorSupport.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" +#include "third_party/WebKit/public/web/WebTouchPoint.h" +#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +// GLenum values copied from gl2.h. +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_ONE 1 +#define GL_TRIANGLES 0x0004 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DEPTH_TEST 0x0B71 +#define GL_BLEND 0x0BE2 +#define GL_SCISSOR_TEST 0x0B90 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_FLOAT 0x1406 +#define GL_RGBA 0x1908 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_NEAREST 0x2600 +#define GL_COLOR_BUFFER_BIT 0x4000 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_ARRAY_BUFFER 0x8892 +#define GL_STATIC_DRAW 0x88E4 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER 0x8D40 + +void premultiplyAlpha(const unsigned colorIn[3], float alpha, float colorOut[4]) +{ + for (int i = 0; i < 3; ++i) + colorOut[i] = (colorIn[i] / 255.0f) * alpha; + + colorOut[3] = alpha; +} + +const char* pointState(WebTouchPoint::State state) +{ + switch (state) { + case WebTouchPoint::StateReleased: + return "Released"; + case WebTouchPoint::StatePressed: + return "Pressed"; + case WebTouchPoint::StateMoved: + return "Moved"; + case WebTouchPoint::StateCancelled: + return "Cancelled"; + default: + return "Unknown"; + } + + BLINK_ASSERT_NOT_REACHED(); + return 0; +} + +void printTouchList(WebTestDelegate* delegate, const WebTouchPoint* points, int length) +{ + for (int i = 0; i < length; ++i) { + char buffer[100]; + snprintf(buffer, sizeof(buffer), "* %d, %d: %s\n", points[i].position.x, points[i].position.y, pointState(points[i].state)); + delegate->printMessage(buffer); + } +} + +void printEventDetails(WebTestDelegate* delegate, const WebInputEvent& event) +{ + if (WebInputEvent::isTouchEventType(event.type)) { + const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(event); + printTouchList(delegate, touch.touches, touch.touchesLength); + printTouchList(delegate, touch.changedTouches, touch.changedTouchesLength); + printTouchList(delegate, touch.targetTouches, touch.targetTouchesLength); + } else if (WebInputEvent::isMouseEventType(event.type) || event.type == WebInputEvent::MouseWheel) { + const WebMouseEvent& mouse = static_cast<const WebMouseEvent&>(event); + char buffer[100]; + snprintf(buffer, sizeof(buffer), "* %d, %d\n", mouse.x, mouse.y); + delegate->printMessage(buffer); + } else if (WebInputEvent::isGestureEventType(event.type)) { + const WebGestureEvent& gesture = static_cast<const WebGestureEvent&>(event); + char buffer[100]; + snprintf(buffer, sizeof(buffer), "* %d, %d\n", gesture.x, gesture.y); + delegate->printMessage(buffer); + } +} + +WebPluginContainer::TouchEventRequestType parseTouchEventRequestType(const WebString& string) +{ + if (string == WebString::fromUTF8("raw")) + return WebPluginContainer::TouchEventRequestTypeRaw; + if (string == WebString::fromUTF8("synthetic")) + return WebPluginContainer::TouchEventRequestTypeSynthesizedMouse; + return WebPluginContainer::TouchEventRequestTypeNone; +} + +void deferredDelete(void* context) +{ + TestPlugin* plugin = static_cast<TestPlugin*>(context); + delete plugin; +} + +} + + +TestPlugin::TestPlugin(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate) + : m_frame(frame) + , m_delegate(delegate) + , m_container(0) + , m_context(0) + , m_colorTexture(0) + , m_mailboxChanged(false) + , m_framebuffer(0) + , m_touchEventRequest(WebPluginContainer::TouchEventRequestTypeNone) + , m_reRequestTouchEvents(false) + , m_printEventDetails(false) + , m_printUserGestureStatus(false) + , m_canProcessDrag(false) +{ + static const WebString kAttributePrimitive = WebString::fromUTF8("primitive"); + static const WebString kAttributeBackgroundColor = WebString::fromUTF8("background-color"); + static const WebString kAttributePrimitiveColor = WebString::fromUTF8("primitive-color"); + static const WebString kAttributeOpacity = WebString::fromUTF8("opacity"); + static const WebString kAttributeAcceptsTouch = WebString::fromUTF8("accepts-touch"); + static const WebString kAttributeReRequestTouchEvents = WebString::fromUTF8("re-request-touch"); + static const WebString kAttributePrintEventDetails = WebString::fromUTF8("print-event-details"); + static const WebString kAttributeCanProcessDrag = WebString::fromUTF8("can-process-drag"); + static const WebString kAttributePrintUserGestureStatus = WebString::fromUTF8("print-user-gesture-status"); + + BLINK_ASSERT(params.attributeNames.size() == params.attributeValues.size()); + size_t size = params.attributeNames.size(); + for (size_t i = 0; i < size; ++i) { + const WebString& attributeName = params.attributeNames[i]; + const WebString& attributeValue = params.attributeValues[i]; + + if (attributeName == kAttributePrimitive) + m_scene.primitive = parsePrimitive(attributeValue); + else if (attributeName == kAttributeBackgroundColor) + parseColor(attributeValue, m_scene.backgroundColor); + else if (attributeName == kAttributePrimitiveColor) + parseColor(attributeValue, m_scene.primitiveColor); + else if (attributeName == kAttributeOpacity) + m_scene.opacity = parseOpacity(attributeValue); + else if (attributeName == kAttributeAcceptsTouch) + m_touchEventRequest = parseTouchEventRequestType(attributeValue); + else if (attributeName == kAttributeReRequestTouchEvents) + m_reRequestTouchEvents = parseBoolean(attributeValue); + else if (attributeName == kAttributePrintEventDetails) + m_printEventDetails = parseBoolean(attributeValue); + else if (attributeName == kAttributeCanProcessDrag) + m_canProcessDrag = parseBoolean(attributeValue); + else if (attributeName == kAttributePrintUserGestureStatus) + m_printUserGestureStatus = parseBoolean(attributeValue); + } +} + +TestPlugin::~TestPlugin() +{ +} + +bool TestPlugin::initialize(WebPluginContainer* container) +{ + WebGraphicsContext3D::Attributes attrs; + m_context = Platform::current()->createOffscreenGraphicsContext3D(attrs); + if (!m_context) + return false; + + if (!m_context->makeContextCurrent()) + return false; + + if (!initScene()) + return false; + + m_layer = WebScopedPtr<WebExternalTextureLayer>(Platform::current()->compositorSupport()->createExternalTextureLayer(this)); + m_container = container; + m_container->setWebLayer(m_layer->layer()); + if (m_reRequestTouchEvents) { + m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeSynthesizedMouse); + m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeRaw); + } + m_container->requestTouchEventType(m_touchEventRequest); + m_container->setWantsWheelEvents(true); + return true; +} + +void TestPlugin::destroy() +{ + if (m_layer.get()) + m_layer->clearTexture(); + if (m_container) + m_container->setWebLayer(0); + m_layer.reset(); + destroyScene(); + + delete m_context; + m_context = 0; + + m_container = 0; + m_frame = 0; + + Platform::current()->callOnMainThread(deferredDelete, this); +} + +void TestPlugin::updateGeometry(const WebRect& frameRect, const WebRect& clipRect, const WebVector<WebRect>& cutOutsRects, bool isVisible) +{ + if (clipRect == m_rect) + return; + m_rect = clipRect; + if (m_rect.isEmpty()) + return; + + m_context->reshapeWithScaleFactor(m_rect.width, m_rect.height, 1.f); + m_context->viewport(0, 0, m_rect.width, m_rect.height); + + m_context->bindTexture(GL_TEXTURE_2D, m_colorTexture); + m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_context->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rect.width, m_rect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); + m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexture, 0); + + drawScene(); + + m_context->genMailboxCHROMIUM(m_mailbox.name); + m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, m_mailbox.name); + + m_context->flush(); + m_layer->layer()->invalidate(); + m_mailboxChanged = true; +} + +bool TestPlugin::prepareMailbox(blink::WebExternalTextureMailbox* mailbox, blink::WebExternalBitmap*) +{ + if (!m_mailboxChanged) + return false; + *mailbox = m_mailbox; + m_mailboxChanged = false; + return true; +} + +void TestPlugin::mailboxReleased(const blink::WebExternalTextureMailbox&) +{ +} + +TestPlugin::Primitive TestPlugin::parsePrimitive(const WebString& string) +{ + static const WebString kPrimitiveNone = WebString::fromUTF8("none"); + static const WebString kPrimitiveTriangle = WebString::fromUTF8("triangle"); + + Primitive primitive = PrimitiveNone; + if (string == kPrimitiveNone) + primitive = PrimitiveNone; + else if (string == kPrimitiveTriangle) + primitive = PrimitiveTriangle; + else + BLINK_ASSERT_NOT_REACHED(); + return primitive; +} + +// FIXME: This method should already exist. Use it. +// For now just parse primary colors. +void TestPlugin::parseColor(const WebString& string, unsigned color[3]) +{ + color[0] = color[1] = color[2] = 0; + if (string == "black") + return; + + if (string == "red") + color[0] = 255; + else if (string == "green") + color[1] = 255; + else if (string == "blue") + color[2] = 255; + else + BLINK_ASSERT_NOT_REACHED(); +} + +float TestPlugin::parseOpacity(const WebString& string) +{ + return static_cast<float>(atof(string.utf8().data())); +} + +bool TestPlugin::parseBoolean(const WebString& string) +{ + static const WebString kPrimitiveTrue = WebString::fromUTF8("true"); + return string == kPrimitiveTrue; +} + +bool TestPlugin::initScene() +{ + float color[4]; + premultiplyAlpha(m_scene.backgroundColor, m_scene.opacity, color); + + m_colorTexture = m_context->createTexture(); + m_framebuffer = m_context->createFramebuffer(); + + m_context->viewport(0, 0, m_rect.width, m_rect.height); + m_context->disable(GL_DEPTH_TEST); + m_context->disable(GL_SCISSOR_TEST); + + m_context->clearColor(color[0], color[1], color[2], color[3]); + + m_context->enable(GL_BLEND); + m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + return m_scene.primitive != PrimitiveNone ? initProgram() && initPrimitive() : true; +} + +void TestPlugin::drawScene() +{ + m_context->viewport(0, 0, m_rect.width, m_rect.height); + m_context->clear(GL_COLOR_BUFFER_BIT); + + if (m_scene.primitive != PrimitiveNone) + drawPrimitive(); +} + +void TestPlugin::destroyScene() +{ + if (m_scene.program) { + m_context->deleteProgram(m_scene.program); + m_scene.program = 0; + } + if (m_scene.vbo) { + m_context->deleteBuffer(m_scene.vbo); + m_scene.vbo = 0; + } + + if (m_framebuffer) { + m_context->deleteFramebuffer(m_framebuffer); + m_framebuffer = 0; + } + + if (m_colorTexture) { + m_context->deleteTexture(m_colorTexture); + m_colorTexture = 0; + } +} + +bool TestPlugin::initProgram() +{ + const string vertexSource( + "attribute vec4 position; \n" + "void main() { \n" + " gl_Position = position; \n" + "} \n" + ); + + const string fragmentSource( + "precision mediump float; \n" + "uniform vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "} \n" + ); + + m_scene.program = loadProgram(vertexSource, fragmentSource); + if (!m_scene.program) + return false; + + m_scene.colorLocation = m_context->getUniformLocation(m_scene.program, "color"); + m_scene.positionLocation = m_context->getAttribLocation(m_scene.program, "position"); + return true; +} + +bool TestPlugin::initPrimitive() +{ + BLINK_ASSERT(m_scene.primitive == PrimitiveTriangle); + + m_scene.vbo = m_context->createBuffer(); + if (!m_scene.vbo) + return false; + + const float vertices[] = { + 0.0f, 0.8f, 0.0f, + -0.8f, -0.8f, 0.0f, + 0.8f, -0.8f, 0.0f }; + m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo); + m_context->bufferData(GL_ARRAY_BUFFER, sizeof(vertices), 0, GL_STATIC_DRAW); + m_context->bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + return true; +} + +void TestPlugin::drawPrimitive() +{ + BLINK_ASSERT(m_scene.primitive == PrimitiveTriangle); + BLINK_ASSERT(m_scene.vbo); + BLINK_ASSERT(m_scene.program); + + m_context->useProgram(m_scene.program); + + // Bind primitive color. + float color[4]; + premultiplyAlpha(m_scene.primitiveColor, m_scene.opacity, color); + m_context->uniform4f(m_scene.colorLocation, color[0], color[1], color[2], color[3]); + + // Bind primitive vertices. + m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo); + m_context->enableVertexAttribArray(m_scene.positionLocation); + m_context->vertexAttribPointer(m_scene.positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); + m_context->drawArrays(GL_TRIANGLES, 0, 3); +} + +unsigned TestPlugin::loadShader(unsigned type, const string& source) +{ + unsigned shader = m_context->createShader(type); + if (shader) { + m_context->shaderSource(shader, source.data()); + m_context->compileShader(shader); + + int compiled = 0; + m_context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + m_context->deleteShader(shader); + shader = 0; + } + } + return shader; +} + +unsigned TestPlugin::loadProgram(const string& vertexSource, const string& fragmentSource) +{ + unsigned vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource); + unsigned fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource); + unsigned program = m_context->createProgram(); + if (vertexShader && fragmentShader && program) { + m_context->attachShader(program, vertexShader); + m_context->attachShader(program, fragmentShader); + m_context->linkProgram(program); + + int linked = 0; + m_context->getProgramiv(program, GL_LINK_STATUS, &linked); + if (!linked) { + m_context->deleteProgram(program); + program = 0; + } + } + if (vertexShader) + m_context->deleteShader(vertexShader); + if (fragmentShader) + m_context->deleteShader(fragmentShader); + + return program; +} + +bool TestPlugin::handleInputEvent(const WebInputEvent& event, WebCursorInfo& info) +{ + const char* eventName = 0; + switch (event.type) { + case WebInputEvent::Undefined: eventName = "unknown"; break; + + case WebInputEvent::MouseDown: eventName = "MouseDown"; break; + case WebInputEvent::MouseUp: eventName = "MouseUp"; break; + case WebInputEvent::MouseMove: eventName = "MouseMove"; break; + case WebInputEvent::MouseEnter: eventName = "MouseEnter"; break; + case WebInputEvent::MouseLeave: eventName = "MouseLeave"; break; + case WebInputEvent::ContextMenu: eventName = "ContextMenu"; break; + + case WebInputEvent::MouseWheel: eventName = "MouseWheel"; break; + + case WebInputEvent::RawKeyDown: eventName = "RawKeyDown"; break; + case WebInputEvent::KeyDown: eventName = "KeyDown"; break; + case WebInputEvent::KeyUp: eventName = "KeyUp"; break; + case WebInputEvent::Char: eventName = "Char"; break; + + case WebInputEvent::GestureScrollBegin: eventName = "GestureScrollBegin"; break; + case WebInputEvent::GestureScrollEnd: eventName = "GestureScrollEnd"; break; + case WebInputEvent::GestureScrollUpdateWithoutPropagation: + case WebInputEvent::GestureScrollUpdate: eventName = "GestureScrollUpdate"; break; + case WebInputEvent::GestureFlingStart: eventName = "GestureFlingStart"; break; + case WebInputEvent::GestureFlingCancel: eventName = "GestureFlingCancel"; break; + case WebInputEvent::GestureTap: eventName = "GestureTap"; break; + case WebInputEvent::GestureTapUnconfirmed: + eventName = "GestureTapUnconfirmed"; break; + case WebInputEvent::GestureTapDown: eventName = "GestureTapDown"; break; + case WebInputEvent::GestureShowPress: eventName = "GestureShowPress"; break; + case WebInputEvent::GestureTapCancel: eventName = "GestureTapCancel"; break; + case WebInputEvent::GestureDoubleTap: eventName = "GestureDoubleTap"; break; + case WebInputEvent::GestureTwoFingerTap: eventName = "GestureTwoFingerTap"; break; + case WebInputEvent::GestureLongPress: eventName = "GestureLongPress"; break; + case WebInputEvent::GestureLongTap: eventName = "GestureLongTap"; break; + case WebInputEvent::GesturePinchBegin: eventName = "GesturePinchBegin"; break; + case WebInputEvent::GesturePinchEnd: eventName = "GesturePinchEnd"; break; + case WebInputEvent::GesturePinchUpdate: eventName = "GesturePinchUpdate"; break; + + case WebInputEvent::TouchStart: eventName = "TouchStart"; break; + case WebInputEvent::TouchMove: eventName = "TouchMove"; break; + case WebInputEvent::TouchEnd: eventName = "TouchEnd"; break; + case WebInputEvent::TouchCancel: eventName = "TouchCancel"; break; + } + + m_delegate->printMessage(std::string("Plugin received event: ") + (eventName ? eventName : "unknown") + "\n"); + if (m_printEventDetails) + printEventDetails(m_delegate, event); + if (m_printUserGestureStatus) + m_delegate->printMessage(std::string("* ") + (WebUserGestureIndicator::isProcessingUserGesture() ? "" : "not ") + "handling user gesture\n"); + return false; +} + +bool TestPlugin::handleDragStatusUpdate(WebDragStatus dragStatus, const WebDragData&, WebDragOperationsMask, const WebPoint& position, const WebPoint& screenPosition) +{ + const char* dragStatusName = 0; + switch (dragStatus) { + case WebDragStatusEnter: + dragStatusName = "DragEnter"; + break; + case WebDragStatusOver: + dragStatusName = "DragOver"; + break; + case WebDragStatusLeave: + dragStatusName = "DragLeave"; + break; + case WebDragStatusDrop: + dragStatusName = "DragDrop"; + break; + case WebDragStatusUnknown: + BLINK_ASSERT_NOT_REACHED(); + } + m_delegate->printMessage(std::string("Plugin received event: ") + dragStatusName + "\n"); + return false; +} + +TestPlugin* TestPlugin::create(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate) +{ + return new TestPlugin(frame, params, delegate); +} + +const WebString& TestPlugin::mimeType() +{ + static const WebString kMimeType = WebString::fromUTF8("application/x-webkit-test-webplugin"); + return kMimeType; +} + +} diff --git a/content/shell/renderer/test_runner/TestPlugin.h b/content/shell/renderer/test_runner/TestPlugin.h new file mode 100644 index 0000000..73666fd --- /dev/null +++ b/content/shell/renderer/test_runner/TestPlugin.h @@ -0,0 +1,137 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TestPlugin_h +#define TestPlugin_h + +#include <string> + +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "third_party/WebKit/public/platform/WebExternalTextureLayer.h" +#include "third_party/WebKit/public/platform/WebExternalTextureLayerClient.h" +#include "third_party/WebKit/public/platform/WebExternalTextureMailbox.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebPlugin.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +// A fake implemention of blink::WebPlugin for testing purposes. +// +// It uses WebGraphicsContext3D to paint a scene consisiting of a primitive +// over a background. The primitive and background can be customized using +// the following plugin parameters: +// primitive: none (default), triangle. +// background-color: black (default), red, green, blue. +// primitive-color: black (default), red, green, blue. +// opacity: [0.0 - 1.0]. Default is 1.0. +// +// Whether the plugin accepts touch events or not can be customized using the +// 'accepts-touch' plugin parameter (defaults to false). +class TestPlugin : public blink::WebPlugin, public blink::WebExternalTextureLayerClient, public blink::WebNonCopyable { +public: + static TestPlugin* create(blink::WebFrame*, const blink::WebPluginParams&, WebTestDelegate*); + virtual ~TestPlugin(); + + static const blink::WebString& mimeType(); + + // WebPlugin methods: + virtual bool initialize(blink::WebPluginContainer*); + virtual void destroy(); + virtual NPObject* scriptableObject() { return 0; } + virtual bool canProcessDrag() const { return m_canProcessDrag; } + virtual void paint(blink::WebCanvas*, const blink::WebRect&) { } + virtual void updateGeometry(const blink::WebRect& frameRect, const blink::WebRect& clipRect, const blink::WebVector<blink::WebRect>& cutOutsRects, bool isVisible); + virtual void updateFocus(bool) { } + virtual void updateVisibility(bool) { } + virtual bool acceptsInputEvents() { return true; } + virtual bool handleInputEvent(const blink::WebInputEvent&, blink::WebCursorInfo&); + virtual bool handleDragStatusUpdate(blink::WebDragStatus, const blink::WebDragData&, blink::WebDragOperationsMask, const blink::WebPoint& position, const blink::WebPoint& screenPosition); + virtual void didReceiveResponse(const blink::WebURLResponse&) { } + virtual void didReceiveData(const char* data, int dataLength) { } + virtual void didFinishLoading() { } + virtual void didFailLoading(const blink::WebURLError&) { } + virtual void didFinishLoadingFrameRequest(const blink::WebURL&, void* notifyData) { } + virtual void didFailLoadingFrameRequest(const blink::WebURL&, void* notifyData, const blink::WebURLError&) { } + virtual bool isPlaceholder() { return false; } + + // WebExternalTextureLayerClient methods: + virtual blink::WebGraphicsContext3D* context() { return 0; } + virtual bool prepareMailbox(blink::WebExternalTextureMailbox*, blink::WebExternalBitmap*); + virtual void mailboxReleased(const blink::WebExternalTextureMailbox&); + +private: + TestPlugin(blink::WebFrame*, const blink::WebPluginParams&, WebTestDelegate*); + + enum Primitive { + PrimitiveNone, + PrimitiveTriangle + }; + + struct Scene { + Primitive primitive; + unsigned backgroundColor[3]; + unsigned primitiveColor[3]; + float opacity; + + unsigned vbo; + unsigned program; + int colorLocation; + int positionLocation; + + Scene() + : primitive(PrimitiveNone) + , opacity(1.0f) // Fully opaque. + , vbo(0) + , program(0) + , colorLocation(-1) + , positionLocation(-1) + { + backgroundColor[0] = backgroundColor[1] = backgroundColor[2] = 0; + primitiveColor[0] = primitiveColor[1] = primitiveColor[2] = 0; + } + }; + + // Functions for parsing plugin parameters. + Primitive parsePrimitive(const blink::WebString&); + void parseColor(const blink::WebString&, unsigned color[3]); + float parseOpacity(const blink::WebString&); + bool parseBoolean(const blink::WebString&); + + // Functions for loading and drawing scene. + bool initScene(); + void drawScene(); + void destroyScene(); + bool initProgram(); + bool initPrimitive(); + void drawPrimitive(); + unsigned loadShader(unsigned type, const std::string& source); + unsigned loadProgram(const std::string& vertexSource, const std::string& fragmentSource); + + blink::WebFrame* m_frame; + WebTestDelegate* m_delegate; + blink::WebPluginContainer* m_container; + + blink::WebRect m_rect; + blink::WebGraphicsContext3D* m_context; + unsigned m_colorTexture; + blink::WebExternalTextureMailbox m_mailbox; + bool m_mailboxChanged; + unsigned m_framebuffer; + Scene m_scene; + WebScopedPtr<blink::WebExternalTextureLayer> m_layer; + + blink::WebPluginContainer::TouchEventRequestType m_touchEventRequest; + // Requests touch events from the WebPluginContainerImpl multiple times to tickle webkit.org/b/108381 + bool m_reRequestTouchEvents; + bool m_printEventDetails; + bool m_printUserGestureStatus; + bool m_canProcessDrag; +}; + +} + +#endif // TestPlugin_h diff --git a/content/shell/renderer/test_runner/TestRunner.cpp b/content/shell/renderer/test_runner/TestRunner.cpp new file mode 100644 index 0000000..030abf5 --- /dev/null +++ b/content/shell/renderer/test_runner/TestRunner.cpp @@ -0,0 +1,2132 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org) + * Copyright (C) 2012 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "content/shell/renderer/test_runner/TestRunner.h" + +#include <limits> + +#include "content/shell/common/test_runner/WebPreferences.h" +#include "content/shell/renderer/test_runner/MockWebSpeechInputController.h" +#include "content/shell/renderer/test_runner/MockWebSpeechRecognizer.h" +#include "content/shell/renderer/test_runner/NotificationPresenter.h" +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/WebPermissions.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "third_party/WebKit/public/platform/WebData.h" +#include "third_party/WebKit/public/platform/WebDeviceMotionData.h" +#include "third_party/WebKit/public/platform/WebDeviceOrientationData.h" +#include "third_party/WebKit/public/platform/WebPoint.h" +#include "third_party/WebKit/public/platform/WebURLResponse.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDataSource.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFindOptions.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebGeolocationClientMock.h" +#include "third_party/WebKit/public/web/WebInputElement.h" +#include "third_party/WebKit/public/web/WebMIDIClientMock.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebSecurityPolicy.h" +#include "third_party/WebKit/public/web/WebSerializedScriptValue.h" +#include "third_party/WebKit/public/web/WebSettings.h" +#include "third_party/WebKit/public/web/WebSurroundingText.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "v8/include/v8.h" + +#if defined(__linux__) || defined(ANDROID) +#include "third_party/WebKit/public/web/linux/WebFontRendering.h" +#endif + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +class InvokeCallbackTask : public WebMethodTask<TestRunner> { +public: + InvokeCallbackTask(TestRunner* object, WebScopedPtr<CppVariant> callbackArguments) + : WebMethodTask<TestRunner>(object) + , m_callbackArguments(callbackArguments) + { + } + + virtual void runIfValid() + { + CppVariant invokeResult; + m_callbackArguments->invokeDefault(m_callbackArguments.get(), 1, invokeResult); + } + +private: + WebScopedPtr<CppVariant> m_callbackArguments; +}; + +} + +TestRunner::WorkQueue::~WorkQueue() +{ + reset(); +} + +void TestRunner::WorkQueue::processWorkSoon() +{ + if (m_controller->topLoadingFrame()) + return; + + if (!m_queue.empty()) { + // We delay processing queued work to avoid recursion problems. + m_controller->m_delegate->postTask(new WorkQueueTask(this)); + } else if (!m_controller->m_waitUntilDone) + m_controller->m_delegate->testFinished(); +} + +void TestRunner::WorkQueue::processWork() +{ + // Quit doing work once a load is in progress. + while (!m_queue.empty()) { + bool startedLoad = m_queue.front()->run(m_controller->m_delegate, m_controller->m_webView); + delete m_queue.front(); + m_queue.pop_front(); + if (startedLoad) + return; + } + + if (!m_controller->m_waitUntilDone && !m_controller->topLoadingFrame()) + m_controller->m_delegate->testFinished(); +} + +void TestRunner::WorkQueue::reset() +{ + m_frozen = false; + while (!m_queue.empty()) { + delete m_queue.front(); + m_queue.pop_front(); + } +} + +void TestRunner::WorkQueue::addWork(WorkItem* work) +{ + if (m_frozen) { + delete work; + return; + } + m_queue.push_back(work); +} + + +TestRunner::TestRunner(TestInterfaces* interfaces) + : m_testIsRunning(false) + , m_closeRemainingWindows(false) + , m_workQueue(this) + , m_testInterfaces(interfaces) + , m_delegate(0) + , m_webView(0) + , m_pageOverlay(0) + , m_webPermissions(new WebPermissions) + , m_notificationPresenter(new NotificationPresenter) +{ + // Initialize the map that associates methods of this class with the names + // they will use when called by JavaScript. The actual binding of those + // names to their methods will be done by calling bindToJavaScript() (defined + // by CppBoundClass, the parent to TestRunner). + + // Methods controlling test execution. + bindMethod("notifyDone", &TestRunner::notifyDone); + bindMethod("queueBackNavigation", &TestRunner::queueBackNavigation); + bindMethod("queueForwardNavigation", &TestRunner::queueForwardNavigation); + bindMethod("queueLoadingScript", &TestRunner::queueLoadingScript); + bindMethod("queueLoad", &TestRunner::queueLoad); + bindMethod("queueLoadHTMLString", &TestRunner::queueLoadHTMLString); + bindMethod("queueNonLoadingScript", &TestRunner::queueNonLoadingScript); + bindMethod("queueReload", &TestRunner::queueReload); + bindMethod("setCloseRemainingWindowsWhenComplete", &TestRunner::setCloseRemainingWindowsWhenComplete); + bindMethod("resetTestHelperControllers", &TestRunner::resetTestHelperControllers); + bindMethod("setCustomPolicyDelegate", &TestRunner::setCustomPolicyDelegate); + bindMethod("waitForPolicyDelegate", &TestRunner::waitForPolicyDelegate); + bindMethod("waitUntilDone", &TestRunner::waitUntilDone); + bindMethod("windowCount", &TestRunner::windowCount); + // Methods implemented in terms of chromium's public WebKit API. + bindMethod("setTabKeyCyclesThroughElements", &TestRunner::setTabKeyCyclesThroughElements); + bindMethod("execCommand", &TestRunner::execCommand); + bindMethod("isCommandEnabled", &TestRunner::isCommandEnabled); + bindMethod("callShouldCloseOnWebView", &TestRunner::callShouldCloseOnWebView); + bindMethod("setDomainRelaxationForbiddenForURLScheme", &TestRunner::setDomainRelaxationForbiddenForURLScheme); + bindMethod("evaluateScriptInIsolatedWorldAndReturnValue", &TestRunner::evaluateScriptInIsolatedWorldAndReturnValue); + bindMethod("evaluateScriptInIsolatedWorld", &TestRunner::evaluateScriptInIsolatedWorld); + bindMethod("setIsolatedWorldSecurityOrigin", &TestRunner::setIsolatedWorldSecurityOrigin); + bindMethod("setIsolatedWorldContentSecurityPolicy", &TestRunner::setIsolatedWorldContentSecurityPolicy); + bindMethod("addOriginAccessWhitelistEntry", &TestRunner::addOriginAccessWhitelistEntry); + bindMethod("removeOriginAccessWhitelistEntry", &TestRunner::removeOriginAccessWhitelistEntry); + bindMethod("hasCustomPageSizeStyle", &TestRunner::hasCustomPageSizeStyle); + bindMethod("forceRedSelectionColors", &TestRunner::forceRedSelectionColors); + bindMethod("injectStyleSheet", &TestRunner::injectStyleSheet); + bindMethod("startSpeechInput", &TestRunner::startSpeechInput); + bindMethod("findString", &TestRunner::findString); + bindMethod("setValueForUser", &TestRunner::setValueForUser); + bindMethod("selectionAsMarkup", &TestRunner::selectionAsMarkup); + bindMethod("setTextSubpixelPositioning", &TestRunner::setTextSubpixelPositioning); + bindMethod("setPageVisibility", &TestRunner::setPageVisibility); + bindMethod("setTextDirection", &TestRunner::setTextDirection); + bindMethod("textSurroundingNode", &TestRunner::textSurroundingNode); + bindMethod("useUnfortunateSynchronousResizeMode", &TestRunner::useUnfortunateSynchronousResizeMode); + bindMethod("disableAutoResizeMode", &TestRunner::disableAutoResizeMode); + bindMethod("enableAutoResizeMode", &TestRunner::enableAutoResizeMode); + bindMethod("setMockDeviceMotion", &TestRunner::setMockDeviceMotion); + bindMethod("setMockDeviceOrientation", &TestRunner::setMockDeviceOrientation); + bindMethod("didAcquirePointerLock", &TestRunner::didAcquirePointerLock); + bindMethod("didLosePointerLock", &TestRunner::didLosePointerLock); + bindMethod("didNotAcquirePointerLock", &TestRunner::didNotAcquirePointerLock); + bindMethod("setPointerLockWillRespondAsynchronously", &TestRunner::setPointerLockWillRespondAsynchronously); + bindMethod("setPointerLockWillFailSynchronously", &TestRunner::setPointerLockWillFailSynchronously); + + // The following modify WebPreferences. + bindMethod("setPopupBlockingEnabled", &TestRunner::setPopupBlockingEnabled); + bindMethod("setJavaScriptCanAccessClipboard", &TestRunner::setJavaScriptCanAccessClipboard); + bindMethod("setXSSAuditorEnabled", &TestRunner::setXSSAuditorEnabled); + bindMethod("setAllowUniversalAccessFromFileURLs", &TestRunner::setAllowUniversalAccessFromFileURLs); + bindMethod("setAllowFileAccessFromFileURLs", &TestRunner::setAllowFileAccessFromFileURLs); + bindMethod("overridePreference", &TestRunner::overridePreference); + bindMethod("setPluginsEnabled", &TestRunner::setPluginsEnabled); + + // The following modify the state of the TestRunner. + bindMethod("dumpEditingCallbacks", &TestRunner::dumpEditingCallbacks); + bindMethod("dumpAsText", &TestRunner::dumpAsText); + bindMethod("dumpAsTextWithPixelResults", &TestRunner::dumpAsTextWithPixelResults); + bindMethod("dumpChildFramesAsText", &TestRunner::dumpChildFramesAsText); + bindMethod("dumpChildFrameScrollPositions", &TestRunner::dumpChildFrameScrollPositions); + bindMethod("dumpIconChanges", &TestRunner::dumpIconChanges); + bindMethod("setAudioData", &TestRunner::setAudioData); + bindMethod("dumpFrameLoadCallbacks", &TestRunner::dumpFrameLoadCallbacks); + bindMethod("dumpPingLoaderCallbacks", &TestRunner::dumpPingLoaderCallbacks); + bindMethod("dumpUserGestureInFrameLoadCallbacks", &TestRunner::dumpUserGestureInFrameLoadCallbacks); + bindMethod("dumpTitleChanges", &TestRunner::dumpTitleChanges); + bindMethod("dumpCreateView", &TestRunner::dumpCreateView); + bindMethod("setCanOpenWindows", &TestRunner::setCanOpenWindows); + bindMethod("dumpResourceLoadCallbacks", &TestRunner::dumpResourceLoadCallbacks); + bindMethod("dumpResourceRequestCallbacks", &TestRunner::dumpResourceRequestCallbacks); + bindMethod("dumpResourceResponseMIMETypes", &TestRunner::dumpResourceResponseMIMETypes); + bindMethod("dumpPermissionClientCallbacks", &TestRunner::dumpPermissionClientCallbacks); + bindMethod("setImagesAllowed", &TestRunner::setImagesAllowed); + bindMethod("setScriptsAllowed", &TestRunner::setScriptsAllowed); + bindMethod("setStorageAllowed", &TestRunner::setStorageAllowed); + bindMethod("setPluginsAllowed", &TestRunner::setPluginsAllowed); + bindMethod("setAllowDisplayOfInsecureContent", &TestRunner::setAllowDisplayOfInsecureContent); + bindMethod("setAllowRunningOfInsecureContent", &TestRunner::setAllowRunningOfInsecureContent); + bindMethod("dumpStatusCallbacks", &TestRunner::dumpWindowStatusChanges); + bindMethod("dumpProgressFinishedCallback", &TestRunner::dumpProgressFinishedCallback); + bindMethod("dumpSpellCheckCallbacks", &TestRunner::dumpSpellCheckCallbacks); + bindMethod("dumpBackForwardList", &TestRunner::dumpBackForwardList); + bindMethod("dumpSelectionRect", &TestRunner::dumpSelectionRect); + bindMethod("testRepaint", &TestRunner::testRepaint); + bindMethod("repaintSweepHorizontally", &TestRunner::repaintSweepHorizontally); + bindMethod("setPrinting", &TestRunner::setPrinting); + bindMethod("setShouldStayOnPageAfterHandlingBeforeUnload", &TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload); + bindMethod("setWillSendRequestClearHeader", &TestRunner::setWillSendRequestClearHeader); + bindMethod("dumpResourceRequestPriorities", &TestRunner::dumpResourceRequestPriorities); + + // The following methods interact with the WebTestProxy. + // The following methods interact with the WebTestDelegate. + bindMethod("showWebInspector", &TestRunner::showWebInspector); + bindMethod("closeWebInspector", &TestRunner::closeWebInspector); + bindMethod("evaluateInWebInspector", &TestRunner::evaluateInWebInspector); + bindMethod("clearAllDatabases", &TestRunner::clearAllDatabases); + bindMethod("setDatabaseQuota", &TestRunner::setDatabaseQuota); + bindMethod("setAlwaysAcceptCookies", &TestRunner::setAlwaysAcceptCookies); + bindMethod("setWindowIsKey", &TestRunner::setWindowIsKey); + bindMethod("pathToLocalResource", &TestRunner::pathToLocalResource); + bindMethod("setBackingScaleFactor", &TestRunner::setBackingScaleFactor); + bindMethod("setPOSIXLocale", &TestRunner::setPOSIXLocale); + bindMethod("numberOfPendingGeolocationPermissionRequests", &TestRunner:: numberOfPendingGeolocationPermissionRequests); + bindMethod("setGeolocationPermission", &TestRunner::setGeolocationPermission); + bindMethod("setMockGeolocationPositionUnavailableError", &TestRunner::setMockGeolocationPositionUnavailableError); + bindMethod("setMockGeolocationPosition", &TestRunner::setMockGeolocationPosition); + bindMethod("setMIDIAccessorResult", &TestRunner::setMIDIAccessorResult); + bindMethod("setMIDISysExPermission", &TestRunner::setMIDISysExPermission); + bindMethod("grantWebNotificationPermission", &TestRunner::grantWebNotificationPermission); + bindMethod("simulateLegacyWebNotificationClick", &TestRunner::simulateLegacyWebNotificationClick); + bindMethod("cancelAllActiveNotifications", &TestRunner::cancelAllActiveNotifications); + bindMethod("addMockSpeechInputResult", &TestRunner::addMockSpeechInputResult); + bindMethod("setMockSpeechInputDumpRect", &TestRunner::setMockSpeechInputDumpRect); + bindMethod("addMockSpeechRecognitionResult", &TestRunner::addMockSpeechRecognitionResult); + bindMethod("setMockSpeechRecognitionError", &TestRunner::setMockSpeechRecognitionError); + bindMethod("wasMockSpeechRecognitionAborted", &TestRunner::wasMockSpeechRecognitionAborted); + bindMethod("display", &TestRunner::display); + bindMethod("displayInvalidatedRegion", &TestRunner::displayInvalidatedRegion); + bindMethod("isChooserShown", &TestRunner::isChooserShown); + + // The following modify WebPageOverlays. + bindMethod("addWebPageOverlay", &TestRunner::addWebPageOverlay); + bindMethod("removeWebPageOverlay", &TestRunner::removeWebPageOverlay); + + // Properties. + bindProperty("globalFlag", &m_globalFlag); + bindProperty("platformName", &m_platformName); + bindProperty("tooltipText", &m_tooltipText); + bindProperty("disableNotifyDone", &m_disableNotifyDone); + + // webHistoryItemCount is used by tests in LayoutTests\http\tests\history + bindProperty("webHistoryItemCount", &m_webHistoryItemCount); + bindProperty("interceptPostMessage", &m_interceptPostMessage); + + // The following are stubs. + bindMethod("dumpDatabaseCallbacks", &TestRunner::notImplemented); + bindMethod("denyWebNotificationPermission", &TestRunner::notImplemented); + bindMethod("removeAllWebNotificationPermissions", &TestRunner::notImplemented); + bindMethod("simulateWebNotificationClick", &TestRunner::notImplemented); + bindMethod("setIconDatabaseEnabled", &TestRunner::notImplemented); + bindMethod("setScrollbarPolicy", &TestRunner::notImplemented); + bindMethod("clearAllApplicationCaches", &TestRunner::notImplemented); + bindMethod("clearApplicationCacheForOrigin", &TestRunner::notImplemented); + bindMethod("clearBackForwardList", &TestRunner::notImplemented); + bindMethod("keepWebHistory", &TestRunner::notImplemented); + bindMethod("setApplicationCacheOriginQuota", &TestRunner::notImplemented); + bindMethod("setCallCloseOnWebViews", &TestRunner::notImplemented); + bindMethod("setMainFrameIsFirstResponder", &TestRunner::notImplemented); + bindMethod("setUseDashboardCompatibilityMode", &TestRunner::notImplemented); + bindMethod("deleteAllLocalStorage", &TestRunner::notImplemented); + bindMethod("localStorageDiskUsageForOrigin", &TestRunner::notImplemented); + bindMethod("originsWithLocalStorage", &TestRunner::notImplemented); + bindMethod("deleteLocalStorageForOrigin", &TestRunner::notImplemented); + bindMethod("observeStorageTrackerNotifications", &TestRunner::notImplemented); + bindMethod("syncLocalStorage", &TestRunner::notImplemented); + bindMethod("addDisallowedURL", &TestRunner::notImplemented); + bindMethod("applicationCacheDiskUsageForOrigin", &TestRunner::notImplemented); + bindMethod("abortModal", &TestRunner::notImplemented); + + // The fallback method is called when an unknown method is invoked. + bindFallbackMethod(&TestRunner::fallbackMethod); +} + +TestRunner::~TestRunner() +{ +} + +void TestRunner::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; + m_webPermissions->setDelegate(delegate); + m_notificationPresenter->setDelegate(delegate); +} + +void TestRunner::setWebView(WebView* webView, WebTestProxyBase* proxy) +{ + m_webView = webView; + m_proxy = proxy; +} + +void TestRunner::reset() +{ + if (m_webView) { + m_webView->setZoomLevel(0); + m_webView->setTextZoomFactor(1); + m_webView->setTabKeyCyclesThroughElements(true); +#if !defined(__APPLE__) && !defined(WIN32) // Actually, TOOLKIT_GTK + // (Constants copied because we can't depend on the header that defined + // them from this file.) + m_webView->setSelectionColors(0xff1e90ff, 0xff000000, 0xffc8c8c8, 0xff323232); +#endif + m_webView->removeInjectedStyleSheets(); + m_webView->setVisibilityState(WebPageVisibilityStateVisible, true); + m_webView->mainFrame()->enableViewSourceMode(false); + + if (m_pageOverlay) { + m_webView->removePageOverlay(m_pageOverlay); + delete m_pageOverlay; + m_pageOverlay = 0; + } + } + + m_topLoadingFrame = 0; + m_waitUntilDone = false; + m_policyDelegateEnabled = false; + m_policyDelegateIsPermissive = false; + m_policyDelegateShouldNotifyDone = false; + + WebSecurityPolicy::resetOriginAccessWhitelists(); +#if defined(__linux__) || defined(ANDROID) + WebFontRendering::setSubpixelPositioning(false); +#endif + + if (m_delegate) { + // Reset the default quota for each origin to 5MB + m_delegate->setDatabaseQuota(5 * 1024 * 1024); + m_delegate->setDeviceScaleFactor(1); + m_delegate->setAcceptAllCookies(false); + m_delegate->setLocale(""); + m_delegate->useUnfortunateSynchronousResizeMode(false); + m_delegate->disableAutoResizeMode(WebSize()); + m_delegate->deleteAllCookies(); + } + + m_dumpEditingCallbacks = false; + m_dumpAsText = false; + m_dumpAsMarkup = false; + m_generatePixelResults = true; + m_dumpChildFrameScrollPositions = false; + m_dumpChildFramesAsText = false; + m_dumpIconChanges = false; + m_dumpAsAudio = false; + m_dumpFrameLoadCallbacks = false; + m_dumpPingLoaderCallbacks = false; + m_dumpUserGestureInFrameLoadCallbacks = false; + m_dumpTitleChanges = false; + m_dumpCreateView = false; + m_canOpenWindows = false; + m_dumpResourceLoadCallbacks = false; + m_dumpResourceRequestCallbacks = false; + m_dumpResourceResponseMIMETypes = false; + m_dumpWindowStatusChanges = false; + m_dumpProgressFinishedCallback = false; + m_dumpSpellCheckCallbacks = false; + m_dumpBackForwardList = false; + m_dumpSelectionRect = false; + m_testRepaint = false; + m_sweepHorizontally = false; + m_isPrinting = false; + m_midiAccessorResult = true; + m_shouldStayOnPageAfterHandlingBeforeUnload = false; + m_shouldDumpResourcePriorities = false; + + m_httpHeadersToClear.clear(); + + m_globalFlag.set(false); + m_webHistoryItemCount.set(0); + m_interceptPostMessage.set(false); + m_platformName.set("chromium"); + m_tooltipText.set(""); + m_disableNotifyDone.set(false); + + m_webPermissions->reset(); + + m_notificationPresenter->reset(); + + m_pointerLocked = false; + m_pointerLockPlannedResult = PointerLockWillSucceed; + + m_taskList.revokeAll(); + m_workQueue.reset(); + + if (m_closeRemainingWindows && m_delegate) + m_delegate->closeRemainingWindows(); + else + m_closeRemainingWindows = true; +} + + +void TestRunner::setTestIsRunning(bool running) +{ + m_testIsRunning = running; +} + +bool TestRunner::shouldDumpEditingCallbacks() const +{ + return m_dumpEditingCallbacks; +} + +void TestRunner::checkResponseMimeType() +{ + // Text output: the test page can request different types of output + // which we handle here. + if (!m_dumpAsText) { + string mimeType = m_webView->mainFrame()->dataSource()->response().mimeType().utf8(); + if (mimeType == "text/plain") { + m_dumpAsText = true; + m_generatePixelResults = false; + } + } +} + +bool TestRunner::shouldDumpAsText() +{ + checkResponseMimeType(); + return m_dumpAsText; +} + +void TestRunner::setShouldDumpAsText(bool value) +{ + m_dumpAsText = value; +} + +bool TestRunner::shouldDumpAsMarkup() +{ + return m_dumpAsMarkup; +} + +void TestRunner::setShouldDumpAsMarkup(bool value) +{ + m_dumpAsMarkup = value; +} + +bool TestRunner::shouldGeneratePixelResults() +{ + checkResponseMimeType(); + return m_generatePixelResults; +} + +void TestRunner::setShouldGeneratePixelResults(bool value) +{ + m_generatePixelResults = value; +} + +bool TestRunner::shouldDumpChildFrameScrollPositions() const +{ + return m_dumpChildFrameScrollPositions; +} + +bool TestRunner::shouldDumpChildFramesAsText() const +{ + return m_dumpChildFramesAsText; +} + +bool TestRunner::shouldDumpAsAudio() const +{ + return m_dumpAsAudio; +} + +const WebArrayBufferView* TestRunner::audioData() const +{ + return &m_audioData; +} + +bool TestRunner::shouldDumpFrameLoadCallbacks() const +{ + return m_testIsRunning && m_dumpFrameLoadCallbacks; +} + +void TestRunner::setShouldDumpFrameLoadCallbacks(bool value) +{ + m_dumpFrameLoadCallbacks = value; +} + +bool TestRunner::shouldDumpPingLoaderCallbacks() const +{ + return m_testIsRunning && m_dumpPingLoaderCallbacks; +} + +void TestRunner::setShouldDumpPingLoaderCallbacks(bool value) +{ + m_dumpPingLoaderCallbacks = value; +} + +void TestRunner::setShouldEnableViewSource(bool value) +{ + m_webView->mainFrame()->enableViewSourceMode(value); +} + +bool TestRunner::shouldDumpUserGestureInFrameLoadCallbacks() const +{ + return m_testIsRunning && m_dumpUserGestureInFrameLoadCallbacks; +} + +bool TestRunner::shouldDumpTitleChanges() const +{ + return m_dumpTitleChanges; +} + +bool TestRunner::shouldDumpIconChanges() const +{ + return m_dumpIconChanges; +} + +bool TestRunner::shouldDumpCreateView() const +{ + return m_dumpCreateView; +} + +bool TestRunner::canOpenWindows() const +{ + return m_canOpenWindows; +} + +bool TestRunner::shouldDumpResourceLoadCallbacks() const +{ + return m_testIsRunning && m_dumpResourceLoadCallbacks; +} + +bool TestRunner::shouldDumpResourceRequestCallbacks() const +{ + return m_testIsRunning && m_dumpResourceRequestCallbacks; +} + +bool TestRunner::shouldDumpResourceResponseMIMETypes() const +{ + return m_testIsRunning && m_dumpResourceResponseMIMETypes; +} + +WebPermissionClient* TestRunner::webPermissions() const +{ + return m_webPermissions.get(); +} + +bool TestRunner::shouldDumpStatusCallbacks() const +{ + return m_dumpWindowStatusChanges; +} + +bool TestRunner::shouldDumpProgressFinishedCallback() const +{ + return m_dumpProgressFinishedCallback; +} + +bool TestRunner::shouldDumpSpellCheckCallbacks() const +{ + return m_dumpSpellCheckCallbacks; +} + +bool TestRunner::shouldDumpBackForwardList() const +{ + return m_dumpBackForwardList; +} + +bool TestRunner::shouldDumpSelectionRect() const +{ + return m_dumpSelectionRect; +} + +bool TestRunner::testRepaint() const +{ + return m_testRepaint; +} + +bool TestRunner::sweepHorizontally() const +{ + return m_sweepHorizontally; +} + +bool TestRunner::isPrinting() const +{ + return m_isPrinting; +} + +bool TestRunner::shouldStayOnPageAfterHandlingBeforeUnload() const +{ + return m_shouldStayOnPageAfterHandlingBeforeUnload; +} + +const std::set<std::string>* TestRunner::httpHeadersToClear() const +{ + return &m_httpHeadersToClear; +} + +void TestRunner::setTopLoadingFrame(WebFrame* frame, bool clear) +{ + if (frame->top()->view() != m_webView) + return; + if (!m_testIsRunning) + return; + if (clear) { + m_topLoadingFrame = 0; + locationChangeDone(); + } else if (!m_topLoadingFrame) + m_topLoadingFrame = frame; +} + +WebFrame* TestRunner::topLoadingFrame() const +{ + return m_topLoadingFrame; +} + +void TestRunner::policyDelegateDone() +{ + BLINK_ASSERT(m_waitUntilDone); + m_delegate->testFinished(); + m_waitUntilDone = false; +} + +bool TestRunner::policyDelegateEnabled() const +{ + return m_policyDelegateEnabled; +} + +bool TestRunner::policyDelegateIsPermissive() const +{ + return m_policyDelegateIsPermissive; +} + +bool TestRunner::policyDelegateShouldNotifyDone() const +{ + return m_policyDelegateShouldNotifyDone; +} + +bool TestRunner::shouldInterceptPostMessage() const +{ + return m_interceptPostMessage.isBool() && m_interceptPostMessage.toBoolean(); +} + +bool TestRunner::shouldDumpResourcePriorities() const +{ + return m_shouldDumpResourcePriorities; +} + +WebNotificationPresenter* TestRunner::notificationPresenter() const +{ + return m_notificationPresenter.get(); +} + +bool TestRunner::requestPointerLock() +{ + switch (m_pointerLockPlannedResult) { + case PointerLockWillSucceed: + m_delegate->postDelayedTask(new HostMethodTask(this, &TestRunner::didAcquirePointerLockInternal), 0); + return true; + case PointerLockWillRespondAsync: + BLINK_ASSERT(!m_pointerLocked); + return true; + case PointerLockWillFailSync: + BLINK_ASSERT(!m_pointerLocked); + return false; + default: + BLINK_ASSERT_NOT_REACHED(); + return false; + } +} + +void TestRunner::requestPointerUnlock() +{ + m_delegate->postDelayedTask(new HostMethodTask(this, &TestRunner::didLosePointerLockInternal), 0); +} + +bool TestRunner::isPointerLocked() +{ + return m_pointerLocked; +} + +void TestRunner::setToolTipText(const blink::WebString& text) +{ + m_tooltipText.set(text.utf8()); +} + +bool TestRunner::midiAccessorResult() +{ + return m_midiAccessorResult; +} + +TestRunner::TestPageOverlay::TestPageOverlay(blink::WebView* webView) : m_webView(webView) +{ +} + +TestRunner::TestPageOverlay::~TestPageOverlay() +{ +} + +void TestRunner::TestPageOverlay::paintPageOverlay(blink::WebCanvas* canvas) +{ + SkRect rect = SkRect::MakeWH(m_webView->size().width, m_webView->size().height); + SkPaint paint; + paint.setColor(SK_ColorCYAN); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawRect(rect, paint); +} + +void TestRunner::didAcquirePointerLockInternal() +{ + m_pointerLocked = true; + m_webView->didAcquirePointerLock(); + + // Reset planned result to default. + m_pointerLockPlannedResult = PointerLockWillSucceed; +} + +void TestRunner::didNotAcquirePointerLockInternal() +{ + BLINK_ASSERT(!m_pointerLocked); + m_pointerLocked = false; + m_webView->didNotAcquirePointerLock(); + + // Reset planned result to default. + m_pointerLockPlannedResult = PointerLockWillSucceed; +} + +void TestRunner::didLosePointerLockInternal() +{ + bool wasLocked = m_pointerLocked; + m_pointerLocked = false; + if (wasLocked) + m_webView->didLosePointerLock(); +} + +void TestRunner::showDevTools() +{ + m_delegate->showDevTools(); +} + +void TestRunner::waitUntilDone(const CppArgumentList&, CppVariant* result) +{ + m_waitUntilDone = true; + result->setNull(); +} + +void TestRunner::notifyDone(const CppArgumentList&, CppVariant* result) +{ + if (m_disableNotifyDone.toBoolean()) + return; + + // Test didn't timeout. Kill the timeout timer. + taskList()->revokeAll(); + + completeNotifyDone(); + result->setNull(); +} + +void TestRunner::completeNotifyDone() +{ + if (m_waitUntilDone && !topLoadingFrame() && m_workQueue.isEmpty()) + m_delegate->testFinished(); + m_waitUntilDone = false; +} + +class WorkItemBackForward : public TestRunner::WorkItem { +public: + WorkItemBackForward(int distance) : m_distance(distance) { } + bool run(WebTestDelegate* delegate, WebView*) + { + delegate->goToOffset(m_distance); + return true; // FIXME: Did it really start a navigation? + } + +private: + int m_distance; +}; + +void TestRunner::queueBackNavigation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isNumber()) + m_workQueue.addWork(new WorkItemBackForward(-arguments[0].toInt32())); + result->setNull(); +} + +void TestRunner::queueForwardNavigation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isNumber()) + m_workQueue.addWork(new WorkItemBackForward(arguments[0].toInt32())); + result->setNull(); +} + +class WorkItemReload : public TestRunner::WorkItem { +public: + bool run(WebTestDelegate* delegate, WebView*) + { + delegate->reload(); + return true; + } +}; + +void TestRunner::queueReload(const CppArgumentList&, CppVariant* result) +{ + m_workQueue.addWork(new WorkItemReload); + result->setNull(); +} + +class WorkItemLoadingScript : public TestRunner::WorkItem { +public: + WorkItemLoadingScript(const string& script) : m_script(script) { } + bool run(WebTestDelegate*, WebView* webView) + { + webView->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script))); + return true; // FIXME: Did it really start a navigation? + } + +private: + string m_script; +}; + +class WorkItemNonLoadingScript : public TestRunner::WorkItem { +public: + WorkItemNonLoadingScript(const string& script) : m_script(script) { } + bool run(WebTestDelegate*, WebView* webView) + { + webView->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script))); + return false; + } + +private: + string m_script; +}; + +void TestRunner::queueLoadingScript(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) + m_workQueue.addWork(new WorkItemLoadingScript(arguments[0].toString())); + result->setNull(); +} + +void TestRunner::queueNonLoadingScript(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) + m_workQueue.addWork(new WorkItemNonLoadingScript(arguments[0].toString())); + result->setNull(); +} + +class WorkItemLoad : public TestRunner::WorkItem { +public: + WorkItemLoad(const WebURL& url, const string& target) + : m_url(url) + , m_target(target) { } + bool run(WebTestDelegate* delegate, WebView*) + { + delegate->loadURLForFrame(m_url, m_target); + return true; // FIXME: Did it really start a navigation? + } + +private: + WebURL m_url; + string m_target; +}; + +void TestRunner::queueLoad(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + // FIXME: Implement WebURL::resolve() and avoid GURL. + GURL currentURL = m_webView->mainFrame()->document().url(); + GURL fullURL = currentURL.Resolve(arguments[0].toString()); + + string target = ""; + if (arguments.size() > 1 && arguments[1].isString()) + target = arguments[1].toString(); + + m_workQueue.addWork(new WorkItemLoad(fullURL, target)); + } + result->setNull(); +} + +class WorkItemLoadHTMLString : public TestRunner::WorkItem { +public: + WorkItemLoadHTMLString(const std::string& html, const WebURL& baseURL) + : m_html(html) + , m_baseURL(baseURL) { } + WorkItemLoadHTMLString(const std::string& html, const WebURL& baseURL, const WebURL& unreachableURL) + : m_html(html) + , m_baseURL(baseURL) + , m_unreachableURL(unreachableURL) { } + bool run(WebTestDelegate*, WebView* webView) + { + webView->mainFrame()->loadHTMLString( + blink::WebData(m_html.data(), m_html.length()), m_baseURL, m_unreachableURL); + return true; + } + +private: + std::string m_html; + WebURL m_baseURL; + WebURL m_unreachableURL; +}; + +void TestRunner::queueLoadHTMLString(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string html = arguments[0].toString(); + WebURL baseURL(GURL("")); + if (arguments.size() > 1 && arguments[1].isString()) + baseURL = WebURL(GURL(arguments[1].toString())); + if (arguments.size() > 2 && arguments[2].isString()) + m_workQueue.addWork(new WorkItemLoadHTMLString(html, baseURL, WebURL(GURL(arguments[2].toString())))); + else + m_workQueue.addWork(new WorkItemLoadHTMLString(html, baseURL)); + } + result->setNull(); +} + +void TestRunner::locationChangeDone() +{ + m_webHistoryItemCount.set(m_delegate->navigationEntryCount()); + + // No more new work after the first complete load. + m_workQueue.setFrozen(true); + + if (!m_waitUntilDone) + m_workQueue.processWorkSoon(); +} + +void TestRunner::windowCount(const CppArgumentList&, CppVariant* result) +{ + result->set(static_cast<int>(m_testInterfaces->windowList().size())); +} + +void TestRunner::setCloseRemainingWindowsWhenComplete(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_closeRemainingWindows = arguments[0].value.boolValue; + result->setNull(); +} + +void TestRunner::resetTestHelperControllers(const CppArgumentList& arguments, CppVariant* result) +{ + m_testInterfaces->resetTestHelperControllers(); + + result->setNull(); +} + +void TestRunner::setCustomPolicyDelegate(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_policyDelegateEnabled = arguments[0].value.boolValue; + m_policyDelegateIsPermissive = false; + if (arguments.size() > 1 && arguments[1].isBool()) + m_policyDelegateIsPermissive = arguments[1].value.boolValue; + } + result->setNull(); +} + +void TestRunner::waitForPolicyDelegate(const CppArgumentList&, CppVariant* result) +{ + m_policyDelegateEnabled = true; + m_policyDelegateShouldNotifyDone = true; + m_waitUntilDone = true; + result->setNull(); +} + +void TestRunner::dumpPermissionClientCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_webPermissions->setDumpCallbacks(true); + result->setNull(); +} + +void TestRunner::setImagesAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setImagesAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void TestRunner::setScriptsAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setScriptsAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void TestRunner::setStorageAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setStorageAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void TestRunner::setPluginsAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setPluginsAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void TestRunner::setAllowDisplayOfInsecureContent(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setDisplayingInsecureContentAllowed(arguments[0].toBoolean()); + + result->setNull(); +} + +void TestRunner::setAllowRunningOfInsecureContent(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webPermissions->setRunningInsecureContentAllowed(arguments[0].value.boolValue); + + result->setNull(); +} + +void TestRunner::dumpWindowStatusChanges(const CppArgumentList&, CppVariant* result) +{ + m_dumpWindowStatusChanges = true; + result->setNull(); +} + +void TestRunner::dumpProgressFinishedCallback(const CppArgumentList&, CppVariant* result) +{ + m_dumpProgressFinishedCallback = true; + result->setNull(); +} + +void TestRunner::dumpSpellCheckCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpSpellCheckCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpBackForwardList(const CppArgumentList&, CppVariant* result) +{ + m_dumpBackForwardList = true; + result->setNull(); +} + +void TestRunner::dumpSelectionRect(const CppArgumentList& arguments, CppVariant* result) +{ + m_dumpSelectionRect = true; + result->setNull(); +} + +void TestRunner::testRepaint(const CppArgumentList&, CppVariant* result) +{ + m_testRepaint = true; + result->setNull(); +} + +void TestRunner::repaintSweepHorizontally(const CppArgumentList&, CppVariant* result) +{ + m_sweepHorizontally = true; + result->setNull(); +} + +void TestRunner::setPrinting(const CppArgumentList& arguments, CppVariant* result) +{ + m_isPrinting = true; + result->setNull(); +} + +void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() == 1 && arguments[0].isBool()) + m_shouldStayOnPageAfterHandlingBeforeUnload = arguments[0].toBoolean(); + + result->setNull(); +} + +void TestRunner::setWillSendRequestClearHeader(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string header = arguments[0].toString(); + if (!header.empty()) + m_httpHeadersToClear.insert(header); + } + result->setNull(); +} + +void TestRunner::setTabKeyCyclesThroughElements(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_webView->setTabKeyCyclesThroughElements(arguments[0].toBoolean()); + result->setNull(); +} + +void TestRunner::execCommand(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() <= 0 || !arguments[0].isString()) + return; + + std::string command = arguments[0].toString(); + std::string value(""); + // Ignore the second parameter (which is userInterface) + // since this command emulates a manual action. + if (arguments.size() >= 3 && arguments[2].isString()) + value = arguments[2].toString(); + + // Note: webkit's version does not return the boolean, so neither do we. + m_webView->focusedFrame()->executeCommand(WebString::fromUTF8(command), WebString::fromUTF8(value)); +} + +void TestRunner::isCommandEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() <= 0 || !arguments[0].isString()) { + result->setNull(); + return; + } + + std::string command = arguments[0].toString(); + bool rv = m_webView->focusedFrame()->isCommandEnabled(WebString::fromUTF8(command)); + result->set(rv); +} + +void TestRunner::callShouldCloseOnWebView(const CppArgumentList&, CppVariant* result) +{ + result->set(m_webView->dispatchBeforeUnloadEvent()); +} + +void TestRunner::setDomainRelaxationForbiddenForURLScheme(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 2 || !arguments[0].isBool() || !arguments[1].isString()) + return; + m_webView->setDomainRelaxationForbidden(cppVariantToBool(arguments[0]), cppVariantToWebString(arguments[1])); +} + +void TestRunner::evaluateScriptInIsolatedWorldAndReturnValue(const CppArgumentList& arguments, CppVariant* result) +{ + v8::HandleScope scope(v8::Isolate::GetCurrent()); + WebVector<v8::Local<v8::Value> > values; + if (arguments.size() >= 2 && arguments[0].isNumber() && arguments[1].isString()) { + WebScriptSource source(cppVariantToWebString(arguments[1])); + // This relies on the iframe focusing itself when it loads. This is a bit + // sketchy, but it seems to be what other tests do. + m_webView->focusedFrame()->executeScriptInIsolatedWorld(arguments[0].toInt32(), &source, 1, 1, &values); + } + result->setNull(); + // Since only one script was added, only one result is expected + if (values.size() == 1 && !values[0].IsEmpty()) { + v8::Local<v8::Value> scriptValue = values[0]; + // FIXME: There are many more types that can be handled. + if (scriptValue->IsString()) { + v8::String::Utf8Value utf8V8(scriptValue); + result->set(std::string(*utf8V8)); + } else if (scriptValue->IsBoolean()) + result->set(scriptValue->ToBoolean()->Value()); + else if (scriptValue->IsNumber()) { + if (scriptValue->IsInt32()) + result->set(scriptValue->ToInt32()->Value()); + else + result->set(scriptValue->ToNumber()->Value()); + } else if (scriptValue->IsNull()) + result->setNull(); + } +} + +void TestRunner::evaluateScriptInIsolatedWorld(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() >= 2 && arguments[0].isNumber() && arguments[1].isString()) { + WebScriptSource source(cppVariantToWebString(arguments[1])); + // This relies on the iframe focusing itself when it loads. This is a bit + // sketchy, but it seems to be what other tests do. + m_webView->focusedFrame()->executeScriptInIsolatedWorld(arguments[0].toInt32(), &source, 1, 1); + } + result->setNull(); +} + +void TestRunner::setIsolatedWorldSecurityOrigin(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 || !arguments[0].isNumber() || !(arguments[1].isString() || arguments[1].isNull())) + return; + + WebSecurityOrigin origin; + if (arguments[1].isString()) + origin = WebSecurityOrigin::createFromString(cppVariantToWebString(arguments[1])); + m_webView->focusedFrame()->setIsolatedWorldSecurityOrigin(arguments[0].toInt32(), origin); +} + +void TestRunner::setIsolatedWorldContentSecurityPolicy(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; + + m_webView->focusedFrame()->setIsolatedWorldContentSecurityPolicy(arguments[0].toInt32(), cppVariantToWebString(arguments[1])); +} + +void TestRunner::addOriginAccessWhitelistEntry(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 4 || !arguments[0].isString() || !arguments[1].isString() + || !arguments[2].isString() || !arguments[3].isBool()) + return; + + blink::WebURL url(GURL(arguments[0].toString())); + if (!url.isValid()) + return; + + WebSecurityPolicy::addOriginAccessWhitelistEntry( + url, + cppVariantToWebString(arguments[1]), + cppVariantToWebString(arguments[2]), + arguments[3].toBoolean()); +} + +void TestRunner::removeOriginAccessWhitelistEntry(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 4 || !arguments[0].isString() || !arguments[1].isString() + || !arguments[2].isString() || !arguments[3].isBool()) + return; + + blink::WebURL url(GURL(arguments[0].toString())); + if (!url.isValid()) + return; + + WebSecurityPolicy::removeOriginAccessWhitelistEntry( + url, + cppVariantToWebString(arguments[1]), + cppVariantToWebString(arguments[2]), + arguments[3].toBoolean()); +} + +void TestRunner::hasCustomPageSizeStyle(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(false); + int pageIndex = 0; + if (arguments.size() > 1) + return; + if (arguments.size() == 1) + pageIndex = cppVariantToInt32(arguments[0]); + WebFrame* frame = m_webView->mainFrame(); + if (!frame) + return; + result->set(frame->hasCustomPageSizeStyle(pageIndex)); +} + +void TestRunner::forceRedSelectionColors(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_webView->setSelectionColors(0xffee0000, 0xff00ee00, 0xff000000, 0xffc0c0c0); +} + +void TestRunner::injectStyleSheet(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isString() || !arguments[1].isBool()) + return; + WebView::injectStyleSheet( + cppVariantToWebString(arguments[0]), WebVector<WebString>(), + arguments[1].toBoolean() ? WebView::InjectStyleInAllFrames : WebView::InjectStyleInTopFrameOnly); +} + +void TestRunner::startSpeechInput(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 1) + return; + + WebElement element; + if (!WebBindings::getElement(arguments[0].value.objectValue, &element)) + return; + + WebInputElement* input = toWebInputElement(&element); + if (!input) + return; + + if (!input->isSpeechInputEnabled()) + return; + + input->startSpeechInput(); +} + +void TestRunner::findString(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + WebFindOptions findOptions; + bool wrapAround = false; + if (arguments.size() >= 2) { + vector<string> optionsArray = arguments[1].toStringVector(); + findOptions.matchCase = true; + findOptions.findNext = true; + + for (size_t i = 0; i < optionsArray.size(); ++i) { + const std::string& option = optionsArray[i]; + if (option == "CaseInsensitive") + findOptions.matchCase = false; + else if (option == "Backwards") + findOptions.forward = false; + else if (option == "StartInSelection") + findOptions.findNext = false; + else if (option == "AtWordStarts") + findOptions.wordStart = true; + else if (option == "TreatMedialCapitalAsWordStart") + findOptions.medialCapitalAsWordStart = true; + else if (option == "WrapAround") + wrapAround = true; + } + } + + WebFrame* frame = m_webView->mainFrame(); + const bool findResult = frame->find(0, cppVariantToWebString(arguments[0]), findOptions, wrapAround, 0); + frame->stopFinding(false); + result->set(findResult); +} + +void TestRunner::setValueForUser(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2) + return; + + WebElement element; + if (!WebBindings::getElement(arguments[0].value.objectValue, &element)) + return; + + WebInputElement* input = toWebInputElement(&element); + if (!input) + return; + + input->setValue(cppVariantToWebString(arguments[1]), true); +} + +void TestRunner::selectionAsMarkup(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(m_webView->mainFrame()->selectionAsMarkup().utf8()); +} + +void TestRunner::setTextSubpixelPositioning(const CppArgumentList& arguments, CppVariant* result) +{ +#if defined(__linux__) || defined(ANDROID) + // Since FontConfig doesn't provide a variable to control subpixel positioning, we'll fall back + // to setting it globally for all fonts. + if (arguments.size() > 0 && arguments[0].isBool()) + WebFontRendering::setSubpixelPositioning(arguments[0].value.boolValue); +#endif + result->setNull(); +} + +void TestRunner::setPageVisibility(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string newVisibility = arguments[0].toString(); + if (newVisibility == "visible") + m_webView->setVisibilityState(WebPageVisibilityStateVisible, false); + else if (newVisibility == "hidden") + m_webView->setVisibilityState(WebPageVisibilityStateHidden, false); + else if (newVisibility == "prerender") + m_webView->setVisibilityState(WebPageVisibilityStatePrerender, false); + } +} + +void TestRunner::setTextDirection(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 1 || !arguments[0].isString()) + return; + + // Map a direction name to a WebTextDirection value. + std::string directionName = arguments[0].toString(); + blink::WebTextDirection direction; + if (directionName == "auto") + direction = blink::WebTextDirectionDefault; + else if (directionName == "rtl") + direction = blink::WebTextDirectionRightToLeft; + else if (directionName == "ltr") + direction = blink::WebTextDirectionLeftToRight; + else + return; + + m_webView->setTextDirection(direction); +} + +void TestRunner::textSurroundingNode(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 4 || !arguments[0].isObject() || !arguments[1].isNumber() || !arguments[2].isNumber() || !arguments[3].isNumber()) + return; + + WebNode node; + if (!WebBindings::getNode(arguments[0].value.objectValue, &node)) + return; + + if (node.isNull() || !node.isTextNode()) + return; + + WebPoint point(arguments[1].toInt32(), arguments[2].toInt32()); + unsigned maxLength = arguments[3].toInt32(); + + WebSurroundingText surroundingText; + surroundingText.initialize(node, point, maxLength); + if (surroundingText.isNull()) + return; + + result->set(surroundingText.textContent().utf8()); +} + +void TestRunner::dumpResourceRequestPriorities(const CppArgumentList& arguments, CppVariant* result) +{ + m_shouldDumpResourcePriorities = true; + result->setNull(); +} + +void TestRunner::useUnfortunateSynchronousResizeMode(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_delegate->useUnfortunateSynchronousResizeMode(true); +} + +void TestRunner::enableAutoResizeMode(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 4) { + result->set(false); + return; + } + int minWidth = cppVariantToInt32(arguments[0]); + int minHeight = cppVariantToInt32(arguments[1]); + blink::WebSize minSize(minWidth, minHeight); + + int maxWidth = cppVariantToInt32(arguments[2]); + int maxHeight = cppVariantToInt32(arguments[3]); + blink::WebSize maxSize(maxWidth, maxHeight); + + m_delegate->enableAutoResizeMode(minSize, maxSize); + result->set(true); +} + +void TestRunner::disableAutoResizeMode(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() !=2) { + result->set(false); + return; + } + int newWidth = cppVariantToInt32(arguments[0]); + int newHeight = cppVariantToInt32(arguments[1]); + blink::WebSize newSize(newWidth, newHeight); + + m_delegate->disableAutoResizeMode(newSize); + result->set(true); +} + +void TestRunner::setMockDeviceMotion(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 19 + || !arguments[0].isBool() || !arguments[1].isNumber() // acceleration.x + || !arguments[2].isBool() || !arguments[3].isNumber() // acceleration.y + || !arguments[4].isBool() || !arguments[5].isNumber() // acceleration.z + || !arguments[6].isBool() || !arguments[7].isNumber() // accelerationIncludingGravity.x + || !arguments[8].isBool() || !arguments[9].isNumber() // accelerationIncludingGravity.y + || !arguments[10].isBool() || !arguments[11].isNumber() // accelerationIncludingGravity.z + || !arguments[12].isBool() || !arguments[13].isNumber() // rotationRate.alpha + || !arguments[14].isBool() || !arguments[15].isNumber() // rotationRate.beta + || !arguments[16].isBool() || !arguments[17].isNumber() // rotationRate.gamma + || !arguments[18].isNumber()) // interval + return; + + WebDeviceMotionData motion; + + // acceleration + motion.hasAccelerationX = arguments[0].toBoolean(); + motion.accelerationX = arguments[1].toDouble(); + motion.hasAccelerationY = arguments[2].toBoolean(); + motion.accelerationY = arguments[3].toDouble(); + motion.hasAccelerationZ = arguments[4].toBoolean(); + motion.accelerationZ = arguments[5].toDouble(); + + // accelerationIncludingGravity + motion.hasAccelerationIncludingGravityX = arguments[6].toBoolean(); + motion.accelerationIncludingGravityX = arguments[7].toDouble(); + motion.hasAccelerationIncludingGravityY = arguments[8].toBoolean(); + motion.accelerationIncludingGravityY = arguments[9].toDouble(); + motion.hasAccelerationIncludingGravityZ = arguments[10].toBoolean(); + motion.accelerationIncludingGravityZ = arguments[11].toDouble(); + + // rotationRate + motion.hasRotationRateAlpha = arguments[12].toBoolean(); + motion.rotationRateAlpha = arguments[13].toDouble(); + motion.hasRotationRateBeta = arguments[14].toBoolean(); + motion.rotationRateBeta = arguments[15].toDouble(); + motion.hasRotationRateGamma = arguments[16].toBoolean(); + motion.rotationRateGamma = arguments[17].toDouble(); + + // interval + motion.interval = arguments[18].toDouble(); + + m_delegate->setDeviceMotionData(motion); +} + +void TestRunner::setMockDeviceOrientation(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 8 + || !arguments[0].isBool() || !arguments[1].isNumber() // alpha + || !arguments[2].isBool() || !arguments[3].isNumber() // beta + || !arguments[4].isBool() || !arguments[5].isNumber() // gamma + || !arguments[6].isBool() || !arguments[7].isBool()) // absolute + return; + + WebDeviceOrientationData orientation; + + // alpha + orientation.hasAlpha = arguments[0].toBoolean(); + orientation.alpha = arguments[1].toDouble(); + + // beta + orientation.hasBeta = arguments[2].toBoolean(); + orientation.beta = arguments[3].toDouble(); + + // gamma + orientation.hasGamma = arguments[4].toBoolean(); + orientation.gamma = arguments[5].toDouble(); + + // absolute + orientation.hasAbsolute = arguments[6].toBoolean(); + orientation.absolute = arguments[7].toBoolean(); + + m_delegate->setDeviceOrientationData(orientation); +} + +void TestRunner::setPopupBlockingEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + bool blockPopups = arguments[0].toBoolean(); + m_delegate->preferences()->javaScriptCanOpenWindowsAutomatically = !blockPopups; + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::setJavaScriptCanAccessClipboard(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_delegate->preferences()->javaScriptCanAccessClipboard = arguments[0].value.boolValue; + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::setXSSAuditorEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_delegate->preferences()->XSSAuditorEnabled = arguments[0].value.boolValue; + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::setAllowUniversalAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_delegate->preferences()->allowUniversalAccessFromFileURLs = arguments[0].value.boolValue; + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::setAllowFileAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_delegate->preferences()->allowFileAccessFromFileURLs = arguments[0].value.boolValue; + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::overridePreference(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2 || !arguments[0].isString()) + return; + + string key = arguments[0].toString(); + CppVariant value = arguments[1]; + WebPreferences* prefs = m_delegate->preferences(); + if (key == "WebKitDefaultFontSize") + prefs->defaultFontSize = cppVariantToInt32(value); + else if (key == "WebKitMinimumFontSize") + prefs->minimumFontSize = cppVariantToInt32(value); + else if (key == "WebKitDefaultTextEncodingName") + prefs->defaultTextEncodingName = cppVariantToWebString(value); + else if (key == "WebKitJavaScriptEnabled") + prefs->javaScriptEnabled = cppVariantToBool(value); + else if (key == "WebKitSupportsMultipleWindows") + prefs->supportsMultipleWindows = cppVariantToBool(value); + else if (key == "WebKitDisplayImagesKey") + prefs->loadsImagesAutomatically = cppVariantToBool(value); + else if (key == "WebKitPluginsEnabled") + prefs->pluginsEnabled = cppVariantToBool(value); + else if (key == "WebKitJavaEnabled") + prefs->javaEnabled = cppVariantToBool(value); + else if (key == "WebKitOfflineWebApplicationCacheEnabled") + prefs->offlineWebApplicationCacheEnabled = cppVariantToBool(value); + else if (key == "WebKitTabToLinksPreferenceKey") + prefs->tabsToLinks = cppVariantToBool(value); + else if (key == "WebKitWebGLEnabled") + prefs->experimentalWebGLEnabled = cppVariantToBool(value); + else if (key == "WebKitCSSRegionsEnabled") + prefs->experimentalCSSRegionsEnabled = cppVariantToBool(value); + else if (key == "WebKitCSSGridLayoutEnabled") + prefs->experimentalCSSGridLayoutEnabled = cppVariantToBool(value); + else if (key == "WebKitHyperlinkAuditingEnabled") + prefs->hyperlinkAuditingEnabled = cppVariantToBool(value); + else if (key == "WebKitEnableCaretBrowsing") + prefs->caretBrowsingEnabled = cppVariantToBool(value); + else if (key == "WebKitAllowDisplayingInsecureContent") + prefs->allowDisplayOfInsecureContent = cppVariantToBool(value); + else if (key == "WebKitAllowRunningInsecureContent") + prefs->allowRunningOfInsecureContent = cppVariantToBool(value); + else if (key == "WebKitCSSCustomFilterEnabled") + prefs->cssCustomFilterEnabled = cppVariantToBool(value); + else if (key == "WebKitShouldRespectImageOrientation") + prefs->shouldRespectImageOrientation = cppVariantToBool(value); + else if (key == "WebKitWebAudioEnabled") + BLINK_ASSERT(cppVariantToBool(value)); + else { + string message("Invalid name for preference: "); + message.append(key); + printErrorMessage(message); + } + m_delegate->applyPreferences(); +} + +void TestRunner::setPluginsEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_delegate->preferences()->pluginsEnabled = arguments[0].toBoolean(); + m_delegate->applyPreferences(); + } + result->setNull(); +} + +void TestRunner::showWebInspector(const CppArgumentList&, CppVariant* result) +{ + showDevTools(); + result->setNull(); +} + +void TestRunner::closeWebInspector(const CppArgumentList& args, CppVariant* result) +{ + m_delegate->closeDevTools(); + result->setNull(); +} + +void TestRunner::isChooserShown(const CppArgumentList&, CppVariant* result) +{ + result->set(m_proxy->isChooserShown()); +} + +void TestRunner::evaluateInWebInspector(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; + m_delegate->evaluateInWebInspector(arguments[0].toInt32(), arguments[1].toString()); +} + +void TestRunner::clearAllDatabases(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_delegate->clearAllDatabases(); +} + +void TestRunner::setDatabaseQuota(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if ((arguments.size() >= 1) && arguments[0].isNumber()) + m_delegate->setDatabaseQuota(arguments[0].toInt32()); +} + +void TestRunner::setAlwaysAcceptCookies(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0) + m_delegate->setAcceptAllCookies(cppVariantToBool(arguments[0])); + result->setNull(); +} + +void TestRunner::setWindowIsKey(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_delegate->setFocus(m_proxy, arguments[0].value.boolValue); + result->setNull(); +} + +void TestRunner::pathToLocalResource(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() <= 0 || !arguments[0].isString()) + return; + + result->set(m_delegate->pathToLocalResource(arguments[0].toString())); +} + +void TestRunner::setBackingScaleFactor(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isObject()) + return; + + float value = arguments[0].value.doubleValue; + m_delegate->setDeviceScaleFactor(value); + m_proxy->discardBackingStore(); + + WebScopedPtr<CppVariant> callbackArguments(new CppVariant()); + callbackArguments->set(arguments[1]); + result->setNull(); + m_delegate->postTask(new InvokeCallbackTask(this, callbackArguments)); +} + +void TestRunner::setPOSIXLocale(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() == 1 && arguments[0].isString()) + m_delegate->setLocale(arguments[0].toString()); +} + +void TestRunner::numberOfPendingGeolocationPermissionRequests(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(m_proxy->geolocationClientMock()->numberOfPendingPermissionRequests()); +} + +// FIXME: For greater test flexibility, we should be able to set each page's geolocation mock individually. +// https://bugs.webkit.org/show_bug.cgi?id=52368 +void TestRunner::setGeolocationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + for (unsigned i = 0; i < windowList.size(); ++i) + windowList.at(i)->geolocationClientMock()->setPermission(arguments[0].toBoolean()); +} + +void TestRunner::setMockGeolocationPosition(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + for (unsigned i = 0; i < windowList.size(); ++i) + windowList.at(i)->geolocationClientMock()->setPosition(arguments[0].toDouble(), arguments[1].toDouble(), arguments[2].toDouble()); +} + +void TestRunner::setMockGeolocationPositionUnavailableError(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 1 || !arguments[0].isString()) + return; + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + for (unsigned i = 0; i < windowList.size(); ++i) + windowList.at(i)->geolocationClientMock()->setPositionUnavailableError(WebString::fromUTF8(arguments[0].toString())); +} + +void TestRunner::setMIDIAccessorResult(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + m_midiAccessorResult = arguments[0].toBoolean(); +} + +void TestRunner::setMIDISysExPermission(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); + for (unsigned i = 0; i < windowList.size(); ++i) + windowList.at(i)->midiClientMock()->setSysExPermission(arguments[0].toBoolean()); +} + +void TestRunner::grantWebNotificationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + m_notificationPresenter->grantPermission(WebString::fromUTF8(arguments[0].toString())); + result->set(true); +} + +void TestRunner::simulateLegacyWebNotificationClick(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + result->set(m_notificationPresenter->simulateClick(WebString::fromUTF8(arguments[0].toString()))); +} + +void TestRunner::cancelAllActiveNotifications(const CppArgumentList& arguments, CppVariant* result) +{ + m_notificationPresenter->cancelAllActiveNotifications(); + result->set(true); +} + +void TestRunner::addMockSpeechInputResult(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 3 || !arguments[0].isString() || !arguments[1].isNumber() || !arguments[2].isString()) + return; + +#if ENABLE_INPUT_SPEECH + m_proxy->speechInputControllerMock()->addMockRecognitionResult(WebString::fromUTF8(arguments[0].toString()), arguments[1].toDouble(), WebString::fromUTF8(arguments[2].toString())); +#endif +} + +void TestRunner::setMockSpeechInputDumpRect(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + +#if ENABLE_INPUT_SPEECH + m_proxy->speechInputControllerMock()->setDumpRect(arguments[0].toBoolean()); +#endif +} + +void TestRunner::addMockSpeechRecognitionResult(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isString() || !arguments[1].isNumber()) + return; + + m_proxy->speechRecognizerMock()->addMockResult(WebString::fromUTF8(arguments[0].toString()), arguments[1].toDouble()); +} + +void TestRunner::setMockSpeechRecognitionError(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2 || !arguments[0].isString() || !arguments[1].isString()) + return; + + m_proxy->speechRecognizerMock()->setError(WebString::fromUTF8(arguments[0].toString()), WebString::fromUTF8(arguments[1].toString())); +} + +void TestRunner::wasMockSpeechRecognitionAborted(const CppArgumentList&, CppVariant* result) +{ + result->set(m_proxy->speechRecognizerMock()->wasAborted()); +} + +void TestRunner::addWebPageOverlay(const CppArgumentList&, CppVariant* result) +{ + if (m_webView && !m_pageOverlay) { + m_pageOverlay = new TestPageOverlay(m_webView); + m_webView->addPageOverlay(m_pageOverlay, 0); + } + result->setNull(); +} + +void TestRunner::removeWebPageOverlay(const CppArgumentList&, CppVariant* result) +{ + if (m_webView && m_pageOverlay) { + m_webView->removePageOverlay(m_pageOverlay); + delete m_pageOverlay; + m_pageOverlay = 0; + } + + result->setNull(); +} + +void TestRunner::display(const CppArgumentList& arguments, CppVariant* result) +{ + m_proxy->display(); + result->setNull(); +} + +void TestRunner::displayInvalidatedRegion(const CppArgumentList& arguments, CppVariant* result) +{ + m_proxy->displayInvalidatedRegion(); + result->setNull(); +} + +void TestRunner::dumpEditingCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpEditingCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpAsText(const CppArgumentList&, CppVariant* result) +{ + m_dumpAsText = true; + m_generatePixelResults = false; + + result->setNull(); +} + +void TestRunner::dumpAsTextWithPixelResults(const CppArgumentList&, CppVariant* result) +{ + m_dumpAsText = true; + m_generatePixelResults = true; + + result->setNull(); +} + +void TestRunner::dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant* result) +{ + m_dumpChildFrameScrollPositions = true; + result->setNull(); +} + +void TestRunner::dumpChildFramesAsText(const CppArgumentList&, CppVariant* result) +{ + m_dumpChildFramesAsText = true; + result->setNull(); +} + +void TestRunner::dumpIconChanges(const CppArgumentList&, CppVariant* result) +{ + m_dumpIconChanges = true; + result->setNull(); +} + +void TestRunner::setAudioData(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isObject()) + return; + + // Check that passed-in object is, in fact, an ArrayBufferView. + NPObject* npobject = NPVARIANT_TO_OBJECT(arguments[0]); + if (!npobject) + return; + if (!WebBindings::getArrayBufferView(npobject, &m_audioData)) + return; + + m_dumpAsAudio = true; +} + +void TestRunner::dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpFrameLoadCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpPingLoaderCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpPingLoaderCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpUserGestureInFrameLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpUserGestureInFrameLoadCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpTitleChanges(const CppArgumentList&, CppVariant* result) +{ + m_dumpTitleChanges = true; + result->setNull(); +} + +void TestRunner::dumpCreateView(const CppArgumentList&, CppVariant* result) +{ + m_dumpCreateView = true; + result->setNull(); +} + +void TestRunner::setCanOpenWindows(const CppArgumentList&, CppVariant* result) +{ + m_canOpenWindows = true; + result->setNull(); +} + +void TestRunner::dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceLoadCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpResourceRequestCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceRequestCallbacks = true; + result->setNull(); +} + +void TestRunner::dumpResourceResponseMIMETypes(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceResponseMIMETypes = true; + result->setNull(); +} + +// Need these conversions because the format of the value for booleans +// may vary - for example, on mac "1" and "0" are used for boolean. +bool TestRunner::cppVariantToBool(const CppVariant& value) +{ + if (value.isBool()) + return value.toBoolean(); + if (value.isNumber()) + return value.toInt32() != 0; + if (value.isString()) { + string valueString = value.toString(); + if (valueString == "true" || valueString == "1") + return true; + if (valueString == "false" || valueString == "0") + return false; + } + printErrorMessage("Invalid value. Expected boolean value."); + return false; +} + +int32_t TestRunner::cppVariantToInt32(const CppVariant& value) +{ + if (value.isNumber()) + return value.toInt32(); + if (value.isString()) { + string stringSource = value.toString(); + const char* source = stringSource.data(); + char* end; + long number = strtol(source, &end, 10); + if (end == source + stringSource.length() && number >= numeric_limits<int32_t>::min() && number <= numeric_limits<int32_t>::max()) + return static_cast<int32_t>(number); + } + printErrorMessage("Invalid value for preference. Expected integer value."); + return 0; +} + +WebString TestRunner::cppVariantToWebString(const CppVariant& value) +{ + if (!value.isString()) { + printErrorMessage("Invalid value for preference. Expected string value."); + return WebString(); + } + return WebString::fromUTF8(value.toString()); +} + +void TestRunner::printErrorMessage(const string& text) +{ + m_delegate->printMessage(string("CONSOLE MESSAGE: ") + text + "\n"); +} + +void TestRunner::fallbackMethod(const CppArgumentList&, CppVariant* result) +{ + printErrorMessage("JavaScript ERROR: unknown method called on TestRunner"); + result->setNull(); +} + +void TestRunner::notImplemented(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void TestRunner::didAcquirePointerLock(const CppArgumentList&, CppVariant* result) +{ + didAcquirePointerLockInternal(); + result->setNull(); +} + +void TestRunner::didNotAcquirePointerLock(const CppArgumentList&, CppVariant* result) +{ + didNotAcquirePointerLockInternal(); + result->setNull(); +} + +void TestRunner::didLosePointerLock(const CppArgumentList&, CppVariant* result) +{ + didLosePointerLockInternal(); + result->setNull(); +} + +void TestRunner::setPointerLockWillRespondAsynchronously(const CppArgumentList&, CppVariant* result) +{ + m_pointerLockPlannedResult = PointerLockWillRespondAsync; + result->setNull(); +} + +void TestRunner::setPointerLockWillFailSynchronously(const CppArgumentList&, CppVariant* result) +{ + m_pointerLockPlannedResult = PointerLockWillFailSync; + result->setNull(); +} + +} diff --git a/content/shell/renderer/test_runner/TestRunner.h b/content/shell/renderer/test_runner/TestRunner.h new file mode 100644 index 0000000..34c1cbf --- /dev/null +++ b/content/shell/renderer/test_runner/TestRunner.h @@ -0,0 +1,737 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org) + * Copyright (C) 2012 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TestRunner_h +#define TestRunner_h + +#include <deque> +#include <set> +#include <string> + +#include "content/shell/renderer/test_runner/CppBoundClass.h" +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebArrayBufferView.h" +#include "third_party/WebKit/public/web/WebPageOverlay.h" +#include "third_party/WebKit/public/web/WebTextDirection.h" +#include "third_party/skia/include/core/SkCanvas.h" + +namespace blink { +class WebArrayBufferView; +class WebNotificationPresenter; +class WebPageOverlay; +class WebPermissionClient; +class WebView; +} + +namespace WebTestRunner { + +class NotificationPresenter; +class TestInterfaces; +class WebPermissions; +class WebTestDelegate; +class WebTestProxyBase; + +class TestRunner : public WebTestRunner, public CppBoundClass { +public: + explicit TestRunner(TestInterfaces*); + virtual ~TestRunner(); + + void setDelegate(WebTestDelegate*); + void setWebView(blink::WebView*, WebTestProxyBase*); + + void reset(); + + WebTaskList* taskList() { return &m_taskList; } + + void setTestIsRunning(bool); + bool testIsRunning() const { return m_testIsRunning; } + + // WebTestRunner implementation. + virtual bool shouldGeneratePixelResults() OVERRIDE; + virtual bool shouldDumpAsAudio() const OVERRIDE; + virtual const blink::WebArrayBufferView* audioData() const OVERRIDE; + virtual bool shouldDumpBackForwardList() const OVERRIDE; + virtual blink::WebPermissionClient* webPermissions() const OVERRIDE; + + // Methods used by WebTestProxyBase. + bool shouldDumpSelectionRect() const; + bool testRepaint() const; + bool sweepHorizontally() const; + bool isPrinting() const; + bool shouldDumpAsText(); + bool shouldDumpAsTextWithPixelResults(); + bool shouldDumpAsMarkup(); + bool shouldDumpChildFrameScrollPositions() const; + bool shouldDumpChildFramesAsText() const; + void showDevTools(); + void setShouldDumpAsText(bool); + void setShouldDumpAsMarkup(bool); + void setShouldGeneratePixelResults(bool); + void setShouldDumpFrameLoadCallbacks(bool); + void setShouldDumpPingLoaderCallbacks(bool); + void setShouldEnableViewSource(bool); + bool shouldDumpEditingCallbacks() const; + bool shouldDumpFrameLoadCallbacks() const; + bool shouldDumpPingLoaderCallbacks() const; + bool shouldDumpUserGestureInFrameLoadCallbacks() const; + bool shouldDumpTitleChanges() const; + bool shouldDumpIconChanges() const; + bool shouldDumpCreateView() const; + bool canOpenWindows() const; + bool shouldDumpResourceLoadCallbacks() const; + bool shouldDumpResourceRequestCallbacks() const; + bool shouldDumpResourceResponseMIMETypes() const; + bool shouldDumpStatusCallbacks() const; + bool shouldDumpProgressFinishedCallback() const; + bool shouldDumpSpellCheckCallbacks() const; + bool shouldStayOnPageAfterHandlingBeforeUnload() const; + const std::set<std::string>* httpHeadersToClear() const; + void setTopLoadingFrame(blink::WebFrame*, bool); + blink::WebFrame* topLoadingFrame() const; + void policyDelegateDone(); + bool policyDelegateEnabled() const; + bool policyDelegateIsPermissive() const; + bool policyDelegateShouldNotifyDone() const; + bool shouldInterceptPostMessage() const; + bool shouldDumpResourcePriorities() const; + blink::WebNotificationPresenter* notificationPresenter() const; + bool requestPointerLock(); + void requestPointerUnlock(); + bool isPointerLocked(); + void setToolTipText(const blink::WebString&); + + bool midiAccessorResult(); + + // A single item in the work queue. + class WorkItem { + public: + virtual ~WorkItem() { } + + // Returns true if this started a load. + virtual bool run(WebTestDelegate*, blink::WebView*) = 0; + }; + +private: + friend class WorkQueue; + + // Helper class for managing events queued by methods like queueLoad or + // queueScript. + class WorkQueue { + public: + WorkQueue(TestRunner* controller) : m_frozen(false), m_controller(controller) { } + virtual ~WorkQueue(); + void processWorkSoon(); + + // Reset the state of the class between tests. + void reset(); + + void addWork(WorkItem*); + + void setFrozen(bool frozen) { m_frozen = frozen; } + bool isEmpty() { return m_queue.empty(); } + WebTaskList* taskList() { return &m_taskList; } + + private: + void processWork(); + class WorkQueueTask: public WebMethodTask<WorkQueue> { + public: + WorkQueueTask(WorkQueue* object): WebMethodTask<WorkQueue>(object) { } + virtual void runIfValid() { m_object->processWork(); } + }; + + WebTaskList m_taskList; + std::deque<WorkItem*> m_queue; + bool m_frozen; + TestRunner* m_controller; + }; + /////////////////////////////////////////////////////////////////////////// + // Methods dealing with the test logic + + // By default, tests end when page load is complete. These methods are used + // to delay the completion of the test until notifyDone is called. + void waitUntilDone(const CppArgumentList&, CppVariant*); + void notifyDone(const CppArgumentList&, CppVariant*); + + // Methods for adding actions to the work queue. Used in conjunction with + // waitUntilDone/notifyDone above. + void queueBackNavigation(const CppArgumentList&, CppVariant*); + void queueForwardNavigation(const CppArgumentList&, CppVariant*); + void queueReload(const CppArgumentList&, CppVariant*); + void queueLoadingScript(const CppArgumentList&, CppVariant*); + void queueNonLoadingScript(const CppArgumentList&, CppVariant*); + void queueLoad(const CppArgumentList&, CppVariant*); + void queueLoadHTMLString(const CppArgumentList&, CppVariant*); + + + // Causes navigation actions just printout the intended navigation instead + // of taking you to the page. This is used for cases like mailto, where you + // don't actually want to open the mail program. + void setCustomPolicyDelegate(const CppArgumentList&, CppVariant*); + + // Delays completion of the test until the policy delegate runs. + void waitForPolicyDelegate(const CppArgumentList&, CppVariant*); + + // Functions for dealing with windows. By default we block all new windows. + void windowCount(const CppArgumentList&, CppVariant*); + void setCloseRemainingWindowsWhenComplete(const CppArgumentList&, CppVariant*); + + void resetTestHelperControllers(const CppArgumentList&, CppVariant*); + + /////////////////////////////////////////////////////////////////////////// + // Methods implemented entirely in terms of chromium's public WebKit API + + // Method that controls whether pressing Tab key cycles through page elements + // or inserts a '\t' char in text area + void setTabKeyCyclesThroughElements(const CppArgumentList&, CppVariant*); + + // Executes an internal command (superset of document.execCommand() commands). + void execCommand(const CppArgumentList&, CppVariant*); + + // Checks if an internal command is currently available. + void isCommandEnabled(const CppArgumentList&, CppVariant*); + + void callShouldCloseOnWebView(const CppArgumentList&, CppVariant*); + void setDomainRelaxationForbiddenForURLScheme(const CppArgumentList&, CppVariant*); + void evaluateScriptInIsolatedWorldAndReturnValue(const CppArgumentList&, CppVariant*); + void evaluateScriptInIsolatedWorld(const CppArgumentList&, CppVariant*); + void setIsolatedWorldSecurityOrigin(const CppArgumentList&, CppVariant*); + void setIsolatedWorldContentSecurityPolicy(const CppArgumentList&, CppVariant*); + + // Allows layout tests to manage origins' whitelisting. + void addOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + void removeOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + + // Returns true if the current page box has custom page size style for + // printing. + void hasCustomPageSizeStyle(const CppArgumentList&, CppVariant*); + + // Forces the selection colors for testing under Linux. + void forceRedSelectionColors(const CppArgumentList&, CppVariant*); + + // Adds a style sheet to be injected into new documents. + void injectStyleSheet(const CppArgumentList&, CppVariant*); + + void startSpeechInput(const CppArgumentList&, CppVariant*); + + void findString(const CppArgumentList&, CppVariant*); + + // Expects the first argument to be an input element and the second argument to be a string value. + // Forwards the setValueForUser() call to the element. + void setValueForUser(const CppArgumentList&, CppVariant*); + + void selectionAsMarkup(const CppArgumentList&, CppVariant*); + + // Enables or disables subpixel positioning (i.e. fractional X positions for + // glyphs) in text rendering on Linux. Since this method changes global + // settings, tests that call it must use their own custom font family for + // all text that they render. If not, an already-cached style will be used, + // resulting in the changed setting being ignored. + void setTextSubpixelPositioning(const CppArgumentList&, CppVariant*); + + // Switch the visibility of the page. + void setPageVisibility(const CppArgumentList&, CppVariant*); + + // Changes the direction of the focused element. + void setTextDirection(const CppArgumentList&, CppVariant*); + + // Retrieves the text surrounding a position in a text node. + // Expects the first argument to be a text node, the second and third to be + // point coordinates relative to the node and the fourth the maximum text + // length to retrieve. + void textSurroundingNode(const CppArgumentList&, CppVariant*); + + // After this function is called, all window-sizing machinery is + // short-circuited inside the renderer. This mode is necessary for + // some tests that were written before browsers had multi-process architecture + // and rely on window resizes to happen synchronously. + // The function has "unfortunate" it its name because we must strive to remove all tests + // that rely on this... well, unfortunate behavior. See http://crbug.com/309760 for the plan. + void useUnfortunateSynchronousResizeMode(const CppArgumentList&, CppVariant*); + + void enableAutoResizeMode(const CppArgumentList&, CppVariant*); + void disableAutoResizeMode(const CppArgumentList&, CppVariant*); + + // Device Motion / Device Orientation related functions + void setMockDeviceMotion(const CppArgumentList&, CppVariant*); + void setMockDeviceOrientation(const CppArgumentList&, CppVariant*); + + void didAcquirePointerLock(const CppArgumentList&, CppVariant*); + void didNotAcquirePointerLock(const CppArgumentList&, CppVariant*); + void didLosePointerLock(const CppArgumentList&, CppVariant*); + void setPointerLockWillFailSynchronously(const CppArgumentList&, CppVariant*); + void setPointerLockWillRespondAsynchronously(const CppArgumentList&, CppVariant*); + + /////////////////////////////////////////////////////////////////////////// + // Methods modifying WebPreferences. + + // Set the WebPreference that controls webkit's popup blocking. + void setPopupBlockingEnabled(const CppArgumentList&, CppVariant*); + + void setJavaScriptCanAccessClipboard(const CppArgumentList&, CppVariant*); + void setXSSAuditorEnabled(const CppArgumentList&, CppVariant*); + void setAllowUniversalAccessFromFileURLs(const CppArgumentList&, CppVariant*); + void setAllowFileAccessFromFileURLs(const CppArgumentList&, CppVariant*); + void overridePreference(const CppArgumentList&, CppVariant*); + + // Enable or disable plugins. + void setPluginsEnabled(const CppArgumentList&, CppVariant*); + + /////////////////////////////////////////////////////////////////////////// + // Methods that modify the state of TestRunner + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each editing command. It takes no arguments, and + // ignores any that may be present. + void dumpEditingCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump pages as + // plain text, rather than as a text representation of the renderer's state. + // The pixel results will not be generated for this test. + void dumpAsText(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump pages as + // plain text, rather than as a text representation of the renderer's state. + // It will also generate a pixel dump for the test. + void dumpAsTextWithPixelResults(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print out the + // scroll offsets of the child frames. It ignores all. + void dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to recursively + // dump all frames as plain text if the dumpAsText flag is set. + // It takes no arguments, and ignores any that may be present. + void dumpChildFramesAsText(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print out the + // information about icon changes notifications from WebKit. + void dumpIconChanges(const CppArgumentList&, CppVariant*); + + // Deals with Web Audio WAV file data. + void setAudioData(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each frame load callback. It takes no arguments, and + // ignores any that may be present. + void dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each PingLoader dispatch. It takes no arguments, and + // ignores any that may be present. + void dumpPingLoaderCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // user gesture status text for some frame load callbacks. It takes no + // arguments, and ignores any that may be present. + void dumpUserGestureInFrameLoadCallbacks(const CppArgumentList&, CppVariant*); + + void dumpTitleChanges(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump all calls to + // WebViewClient::createView(). + // It takes no arguments, and ignores any that may be present. + void dumpCreateView(const CppArgumentList&, CppVariant*); + + void setCanOpenWindows(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump a descriptive + // line for each resource load callback. It takes no arguments, and ignores + // any that may be present. + void dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each element that requested a resource. It takes no + // arguments, and ignores any that may be present. + void dumpResourceRequestCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump the MIME type + // for each resource that was loaded. It takes no arguments, and ignores any + // that may be present. + void dumpResourceResponseMIMETypes(const CppArgumentList&, CppVariant*); + + // WebPermissionClient related. + void setImagesAllowed(const CppArgumentList&, CppVariant*); + void setScriptsAllowed(const CppArgumentList&, CppVariant*); + void setStorageAllowed(const CppArgumentList&, CppVariant*); + void setPluginsAllowed(const CppArgumentList&, CppVariant*); + void setAllowDisplayOfInsecureContent(const CppArgumentList&, CppVariant*); + void setAllowRunningOfInsecureContent(const CppArgumentList&, CppVariant*); + void dumpPermissionClientCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump all calls + // to window.status(). + // It takes no arguments, and ignores any that may be present. + void dumpWindowStatusChanges(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for the progress finished callback. It takes no + // arguments, and ignores any that may be present. + void dumpProgressFinishedCallback(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump all + // the lines of descriptive text about spellcheck execution. + void dumpSpellCheckCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print out a text + // representation of the back/forward list. It ignores all arguments. + void dumpBackForwardList(const CppArgumentList&, CppVariant*); + + void dumpSelectionRect(const CppArgumentList&, CppVariant*); + void testRepaint(const CppArgumentList&, CppVariant*); + void repaintSweepHorizontally(const CppArgumentList&, CppVariant*); + + // Causes layout to happen as if targetted to printed pages. + void setPrinting(const CppArgumentList&, CppVariant*); + + void setShouldStayOnPageAfterHandlingBeforeUnload(const CppArgumentList&, CppVariant*); + + // Causes WillSendRequest to clear certain headers. + void setWillSendRequestClearHeader(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump a descriptive + // line for each resource load's priority and any time that priority + // changes. It takes no arguments, and ignores any that may be present. + void dumpResourceRequestPriorities(const CppArgumentList&, CppVariant*); + + /////////////////////////////////////////////////////////////////////////// + // Methods interacting with the WebTestProxy + + /////////////////////////////////////////////////////////////////////////// + // Methods forwarding to the WebTestDelegate + + // Shows DevTools window. + void showWebInspector(const CppArgumentList&, CppVariant*); + void closeWebInspector(const CppArgumentList&, CppVariant*); + + // Inspect chooser state + void isChooserShown(const CppArgumentList&, CppVariant*); + + // Allows layout tests to exec scripts at WebInspector side. + void evaluateInWebInspector(const CppArgumentList&, CppVariant*); + + // Clears all databases. + void clearAllDatabases(const CppArgumentList&, CppVariant*); + // Sets the default quota for all origins + void setDatabaseQuota(const CppArgumentList&, CppVariant*); + + // Changes the cookie policy from the default to allow all cookies. + void setAlwaysAcceptCookies(const CppArgumentList&, CppVariant*); + + // Gives focus to the window. + void setWindowIsKey(const CppArgumentList&, CppVariant*); + + // Converts a URL starting with file:///tmp/ to the local mapping. + void pathToLocalResource(const CppArgumentList&, CppVariant*); + + // Used to set the device scale factor. + void setBackingScaleFactor(const CppArgumentList&, CppVariant*); + + // Calls setlocale(LC_ALL, ...) for a specified locale. + // Resets between tests. + void setPOSIXLocale(const CppArgumentList&, CppVariant*); + + // Gets the number of geolocation permissions requests pending. + void numberOfPendingGeolocationPermissionRequests(const CppArgumentList&, CppVariant*); + + // Geolocation related functions. + void setGeolocationPermission(const CppArgumentList&, CppVariant*); + void setMockGeolocationPosition(const CppArgumentList&, CppVariant*); + void setMockGeolocationPositionUnavailableError(const CppArgumentList&, CppVariant*); + + // MIDI function to control permission handling. + void setMIDIAccessorResult(const CppArgumentList&, CppVariant*); + void setMIDISysExPermission(const CppArgumentList&, CppVariant*); + + // Grants permission for desktop notifications to an origin + void grantWebNotificationPermission(const CppArgumentList&, CppVariant*); + // Simulates a click on a desktop notification. + void simulateLegacyWebNotificationClick(const CppArgumentList&, CppVariant*); + // Cancel all active desktop notifications. + void cancelAllActiveNotifications(const CppArgumentList& arguments, CppVariant* result); + + // Speech input related functions. + void addMockSpeechInputResult(const CppArgumentList&, CppVariant*); + void setMockSpeechInputDumpRect(const CppArgumentList&, CppVariant*); + void addMockSpeechRecognitionResult(const CppArgumentList&, CppVariant*); + void setMockSpeechRecognitionError(const CppArgumentList&, CppVariant*); + void wasMockSpeechRecognitionAborted(const CppArgumentList&, CppVariant*); + + // WebPageOverlay related functions. Permits the adding and removing of only + // one opaque overlay. + void addWebPageOverlay(const CppArgumentList&, CppVariant*); + void removeWebPageOverlay(const CppArgumentList&, CppVariant*); + + void display(const CppArgumentList&, CppVariant*); + void displayInvalidatedRegion(const CppArgumentList&, CppVariant*); + + ////////////////////////////////////////////////////////////////////////// + // Fallback and stub methods + + // The fallback method is called when a nonexistent method is called on + // the layout test controller object. + // It is usefull to catch typos in the JavaScript code (a few layout tests + // do have typos in them) and it allows the script to continue running in + // that case (as the Mac does). + void fallbackMethod(const CppArgumentList&, CppVariant*); + + // Stub for not implemented methods. + void notImplemented(const CppArgumentList&, CppVariant*); + + /////////////////////////////////////////////////////////////////////////// + // Internal helpers + void checkResponseMimeType(); + void completeNotifyDone(); + class HostMethodTask : public WebMethodTask<TestRunner> { + public: + typedef void (TestRunner::*CallbackMethodType)(); + HostMethodTask(TestRunner* object, CallbackMethodType callback) + : WebMethodTask<TestRunner>(object) + , m_callback(callback) + { } + + virtual void runIfValid() { (m_object->*m_callback)(); } + + private: + CallbackMethodType m_callback; + }; + class TestPageOverlay : public blink::WebPageOverlay { + public: + explicit TestPageOverlay(blink::WebView*); + virtual void paintPageOverlay(blink::WebCanvas*) OVERRIDE; + virtual ~TestPageOverlay(); + private: + blink::WebView* m_webView; + }; + void didAcquirePointerLockInternal(); + void didNotAcquirePointerLockInternal(); + void didLosePointerLockInternal(); + + bool cppVariantToBool(const CppVariant&); + int32_t cppVariantToInt32(const CppVariant&); + blink::WebString cppVariantToWebString(const CppVariant&); + + void printErrorMessage(const std::string&); + + // In the Mac code, this is called to trigger the end of a test after the + // page has finished loading. From here, we can generate the dump for the + // test. + void locationChangeDone(); + + bool m_testIsRunning; + + // When reset is called, go through and close all but the main test shell + // window. By default, set to true but toggled to false using + // setCloseRemainingWindowsWhenComplete(). + bool m_closeRemainingWindows; + + // If true, don't dump output until notifyDone is called. + bool m_waitUntilDone; + + // Causes navigation actions just printout the intended navigation instead + // of taking you to the page. This is used for cases like mailto, where you + // don't actually want to open the mail program. + bool m_policyDelegateEnabled; + + // Toggles the behavior of the policy delegate. If true, then navigations + // will be allowed. Otherwise, they will be ignored (dropped). + bool m_policyDelegateIsPermissive; + + // If true, the policy delegate will signal layout test completion. + bool m_policyDelegateShouldNotifyDone; + + WorkQueue m_workQueue; + + // globalFlag is used by a number of layout tests in http/tests/security/dataURL. + CppVariant m_globalFlag; + + // Bound variable to return the name of this platform (chromium). + CppVariant m_platformName; + + // Bound variable counting the number of top URLs visited. + CppVariant m_webHistoryItemCount; + + // Bound variable to set whether postMessages should be intercepted or not + CppVariant m_interceptPostMessage; + + // Bound variable to store the last tooltip text + CppVariant m_tooltipText; + + // Bound variable to disable notifyDone calls. This is used in GC leak + // tests, where existing LayoutTests are loaded within an iframe. The GC + // test harness will set this flag to ignore the notifyDone calls from the + // target LayoutTest. + CppVariant m_disableNotifyDone; + + // If true, the test_shell will write a descriptive line for each editing + // command. + bool m_dumpEditingCallbacks; + + // If true, the test_shell will generate pixel results in dumpAsText mode + bool m_generatePixelResults; + + // If true, the test_shell will produce a plain text dump rather than a + // text representation of the renderer. + bool m_dumpAsText; + + // If true and if dump_as_text_ is true, the test_shell will recursively + // dump all frames as plain text. + bool m_dumpChildFramesAsText; + + // If true, the test_shell will produce a dump of the DOM rather than a text + // representation of the renderer. + bool m_dumpAsMarkup; + + // If true, the test_shell will print out the child frame scroll offsets as + // well. + bool m_dumpChildFrameScrollPositions; + + // If true, the test_shell will print out the icon change notifications. + bool m_dumpIconChanges; + + // If true, the test_shell will output a base64 encoded WAVE file. + bool m_dumpAsAudio; + + // If true, the test_shell will output a descriptive line for each frame + // load callback. + bool m_dumpFrameLoadCallbacks; + + // If true, the test_shell will output a descriptive line for each + // PingLoader dispatched. + bool m_dumpPingLoaderCallbacks; + + // If true, the test_shell will output a line of the user gesture status + // text for some frame load callbacks. + bool m_dumpUserGestureInFrameLoadCallbacks; + + // If true, output a message when the page title is changed. + bool m_dumpTitleChanges; + + // If true, output a descriptive line each time WebViewClient::createView + // is invoked. + bool m_dumpCreateView; + + // If true, new windows can be opened via javascript or by plugins. By + // default, set to false and can be toggled to true using + // setCanOpenWindows(). + bool m_canOpenWindows; + + // If true, the test_shell will output a descriptive line for each resource + // load callback. + bool m_dumpResourceLoadCallbacks; + + // If true, the test_shell will output a descriptive line for each resource + // request callback. + bool m_dumpResourceRequestCallbacks; + + // If true, the test_shell will output the MIME type for each resource that + // was loaded. + bool m_dumpResourceResponseMIMETypes; + + // If true, the test_shell will dump all changes to window.status. + bool m_dumpWindowStatusChanges; + + // If true, the test_shell will output a descriptive line for the progress + // finished callback. + bool m_dumpProgressFinishedCallback; + + // If true, the test_shell will output descriptive test for spellcheck + // execution. + bool m_dumpSpellCheckCallbacks; + + // If true, the test_shell will produce a dump of the back forward list as + // well. + bool m_dumpBackForwardList; + + // If true, the test_shell will draw the bounds of the current selection rect + // taking possible transforms of the selection rect into account. + bool m_dumpSelectionRect; + + // If true, pixel dump will be produced as a series of 1px-tall, view-wide + // individual paints over the height of the view. + bool m_testRepaint; + + // If true and test_repaint_ is true as well, pixel dump will be produced as + // a series of 1px-wide, view-tall paints across the width of the view. + bool m_sweepHorizontally; + + // If true, layout is to target printed pages. + bool m_isPrinting; + + // If false, MockWebMIDIAccessor fails on startSession() for testing. + bool m_midiAccessorResult; + + bool m_shouldStayOnPageAfterHandlingBeforeUnload; + + bool m_shouldDumpResourcePriorities; + + std::set<std::string> m_httpHeadersToClear; + + // WAV audio data is stored here. + blink::WebArrayBufferView m_audioData; + + // Used for test timeouts. + WebTaskList m_taskList; + + TestInterfaces* m_testInterfaces; + WebTestDelegate* m_delegate; + blink::WebView* m_webView; + TestPageOverlay* m_pageOverlay; + WebTestProxyBase* m_proxy; + + // This is non-0 IFF a load is in progress. + blink::WebFrame* m_topLoadingFrame; + + // WebPermissionClient mock object. + WebScopedPtr<WebPermissions> m_webPermissions; + + WebScopedPtr<NotificationPresenter> m_notificationPresenter; + + bool m_pointerLocked; + enum { + PointerLockWillSucceed, + PointerLockWillRespondAsync, + PointerLockWillFailSync, + } m_pointerLockPlannedResult; +}; + +} + +#endif // TestRunner_h diff --git a/content/shell/renderer/test_runner/TextInputController.cpp b/content/shell/renderer/test_runner/TextInputController.cpp new file mode 100644 index 0000000..e2811a0 --- /dev/null +++ b/content/shell/renderer/test_runner/TextInputController.cpp @@ -0,0 +1,193 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/TextInputController.h" + +#include <string> +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebCompositionUnderline.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebRange.h" +#include "third_party/WebKit/public/web/WebView.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +TextInputController::TextInputController() +{ + bindMethod("doCommand", &TextInputController::doCommand); + bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange); + bindMethod("hasMarkedText", &TextInputController::hasMarkedText); + bindMethod("insertText", &TextInputController::insertText); + bindMethod("markedRange", &TextInputController::markedRange); + bindMethod("selectedRange", &TextInputController::selectedRange); + bindMethod("setMarkedText", &TextInputController::setMarkedText); + bindMethod("unmarkText", &TextInputController::unmarkText); + bindMethod("setComposition", &TextInputController::setComposition); +} + +void TextInputController::insertText(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + m_webView->confirmComposition(WebString::fromUTF8(arguments[0].toString())); +} + +void TextInputController::doCommand(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = m_webView->mainFrame(); + if (!mainFrame) + return; + + if (arguments.size() >= 1 && arguments[0].isString()) + mainFrame->executeCommand(WebString::fromUTF8(arguments[0].toString())); +} + +void TextInputController::setMarkedText(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 3 || !arguments[0].isString() + || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; + + WebString text(WebString::fromUTF8(arguments[0].toString())); + int start = arguments[1].toInt32(); + int length = arguments[2].toInt32(); + + // Split underline into up to 3 elements (before, selection, and after). + vector<WebCompositionUnderline> underlines; + WebCompositionUnderline underline; + if (!start) { + underline.endOffset = length; + } else { + underline.endOffset = start; + underlines.push_back(underline); + underline.startOffset = start; + underline.endOffset = start + length; + } + underline.thick = true; + underlines.push_back(underline); + if (start + length < static_cast<int>(text.length())) { + underline.startOffset = underline.endOffset; + underline.endOffset = text.length(); + underline.thick = false; + underlines.push_back(underline); + } + + m_webView->setComposition(text, underlines, start, start + length); +} + +void TextInputController::unmarkText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + m_webView->confirmComposition(); +} + +void TextInputController::hasMarkedText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = m_webView->mainFrame(); + if (!mainFrame) + return; + + result->set(mainFrame->hasMarkedText()); +} + +void TextInputController::markedRange(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = m_webView->mainFrame(); + if (!mainFrame) + return; + + WebRange range = mainFrame->markedRange(); + vector<int> intArray(2); + intArray[0] = range.startOffset(); + intArray[1] = range.endOffset(); + + NPObject* resultArray = WebBindings::makeIntArray(intArray); + result->set(resultArray); + WebBindings::releaseObject(resultArray); +} + +void TextInputController::selectedRange(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = m_webView->mainFrame(); + if (!mainFrame) + return; + + WebRange range = mainFrame->selectionRange(); + vector<int> intArray(2); + intArray[0] = range.startOffset(); + intArray[1] = range.endOffset(); + + NPObject* resultArray = WebBindings::makeIntArray(intArray); + result->set(resultArray); + WebBindings::releaseObject(resultArray); +} + +void TextInputController::firstRectForCharacterRange(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* frame = m_webView->focusedFrame(); + if (!frame) + return; + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebRect rect; + if (!frame->firstRectForCharacterRange(arguments[0].toInt32(), arguments[1].toInt32(), rect)) + return; + + vector<int> intArray(4); + intArray[0] = rect.x; + intArray[1] = rect.y; + intArray[2] = rect.width; + intArray[3] = rect.height; + + NPObject* resultArray = WebBindings::makeIntArray(intArray); + result->set(resultArray); + WebBindings::releaseObject(resultArray); +} + +void TextInputController::setComposition(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1) + return; + + // Sends a keydown event with key code = 0xE5 to emulate input method behavior. + WebKeyboardEvent keyDown; + keyDown.type = WebInputEvent::RawKeyDown; + keyDown.modifiers = 0; + keyDown.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY + keyDown.setKeyIdentifierFromWindowsKeyCode(); + m_webView->handleInputEvent(keyDown); + + WebVector<WebCompositionUnderline> underlines; + WebString text(WebString::fromUTF8(arguments[0].toString())); + m_webView->setComposition(text, underlines, 0, text.length()); +} + +} diff --git a/content/shell/renderer/test_runner/TextInputController.h b/content/shell/renderer/test_runner/TextInputController.h new file mode 100644 index 0000000..2277f01 --- /dev/null +++ b/content/shell/renderer/test_runner/TextInputController.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TextInputController is bound to window.textInputController in Javascript +// when DRT is running. Layout tests use it to exercise various corners of +// text input. + +#ifndef TextInputController_h +#define TextInputController_h + +#include "content/shell/renderer/test_runner/CppBoundClass.h" + +namespace blink { +class WebView; +} + +namespace WebTestRunner { + +class TextInputController : public CppBoundClass { +public: + TextInputController(); + + void setWebView(blink::WebView* webView) { m_webView = webView; } + + void insertText(const CppArgumentList&, CppVariant*); + void doCommand(const CppArgumentList&, CppVariant*); + void setMarkedText(const CppArgumentList&, CppVariant*); + void unmarkText(const CppArgumentList&, CppVariant*); + void hasMarkedText(const CppArgumentList&, CppVariant*); + void markedRange(const CppArgumentList&, CppVariant*); + void selectedRange(const CppArgumentList&, CppVariant*); + void firstRectForCharacterRange(const CppArgumentList&, CppVariant*); + void setComposition(const CppArgumentList&, CppVariant*); + +private: + blink::WebView* m_webView; +}; + +} + +#endif // TextInputController_h diff --git a/content/shell/renderer/test_runner/WebAXObjectProxy.cpp b/content/shell/renderer/test_runner/WebAXObjectProxy.cpp new file mode 100644 index 0000000..993af65 --- /dev/null +++ b/content/shell/renderer/test_runner/WebAXObjectProxy.cpp @@ -0,0 +1,1161 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebAXObjectProxy.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/WebPoint.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/WebString.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +// Map role value to string, matching Safari/Mac platform implementation to +// avoid rebaselining layout tests. +string roleToString(WebAXRole role) +{ + string result = "AXRole: AX"; + switch (role) { + case WebAXRoleAlertDialog: + return result.append("AlertDialog"); + case WebAXRoleAlert: + return result.append("Alert"); + case WebAXRoleAnnotation: + return result.append("Annotation"); + case WebAXRoleApplication: + return result.append("Application"); + case WebAXRoleArticle: + return result.append("Article"); + case WebAXRoleBanner: + return result.append("Banner"); + case WebAXRoleBrowser: + return result.append("Browser"); + case WebAXRoleBusyIndicator: + return result.append("BusyIndicator"); + case WebAXRoleButton: + return result.append("Button"); + case WebAXRoleCanvas: + return result.append("Canvas"); + case WebAXRoleCell: + return result.append("Cell"); + case WebAXRoleCheckBox: + return result.append("CheckBox"); + case WebAXRoleColorWell: + return result.append("ColorWell"); + case WebAXRoleColumnHeader: + return result.append("ColumnHeader"); + case WebAXRoleColumn: + return result.append("Column"); + case WebAXRoleComboBox: + return result.append("ComboBox"); + case WebAXRoleComplementary: + return result.append("Complementary"); + case WebAXRoleContentInfo: + return result.append("ContentInfo"); + case WebAXRoleDefinition: + return result.append("Definition"); + case WebAXRoleDescriptionListDetail: + return result.append("DescriptionListDetail"); + case WebAXRoleDescriptionListTerm: + return result.append("DescriptionListTerm"); + case WebAXRoleDialog: + return result.append("Dialog"); + case WebAXRoleDirectory: + return result.append("Directory"); + case WebAXRoleDisclosureTriangle: + return result.append("DisclosureTriangle"); + case WebAXRoleDiv: + return result.append("Div"); + case WebAXRoleDocument: + return result.append("Document"); + case WebAXRoleDrawer: + return result.append("Drawer"); + case WebAXRoleEditableText: + return result.append("EditableText"); + case WebAXRoleFooter: + return result.append("Footer"); + case WebAXRoleForm: + return result.append("Form"); + case WebAXRoleGrid: + return result.append("Grid"); + case WebAXRoleGroup: + return result.append("Group"); + case WebAXRoleGrowArea: + return result.append("GrowArea"); + case WebAXRoleHeading: + return result.append("Heading"); + case WebAXRoleHelpTag: + return result.append("HelpTag"); + case WebAXRoleHorizontalRule: + return result.append("HorizontalRule"); + case WebAXRoleIgnored: + return result.append("Ignored"); + case WebAXRoleImageMapLink: + return result.append("ImageMapLink"); + case WebAXRoleImageMap: + return result.append("ImageMap"); + case WebAXRoleImage: + return result.append("Image"); + case WebAXRoleIncrementor: + return result.append("Incrementor"); + case WebAXRoleInlineTextBox: + return result.append("InlineTextBox"); + case WebAXRoleLabel: + return result.append("Label"); + case WebAXRoleLegend: + return result.append("Legend"); + case WebAXRoleLink: + return result.append("Link"); + case WebAXRoleListBoxOption: + return result.append("ListBoxOption"); + case WebAXRoleListBox: + return result.append("ListBox"); + case WebAXRoleListItem: + return result.append("ListItem"); + case WebAXRoleListMarker: + return result.append("ListMarker"); + case WebAXRoleList: + return result.append("List"); + case WebAXRoleLog: + return result.append("Log"); + case WebAXRoleMain: + return result.append("Main"); + case WebAXRoleMarquee: + return result.append("Marquee"); + case WebAXRoleMathElement: + return result.append("MathElement"); + case WebAXRoleMath: + return result.append("Math"); + case WebAXRoleMatte: + return result.append("Matte"); + case WebAXRoleMenuBar: + return result.append("MenuBar"); + case WebAXRoleMenuButton: + return result.append("MenuButton"); + case WebAXRoleMenuItem: + return result.append("MenuItem"); + case WebAXRoleMenuListOption: + return result.append("MenuListOption"); + case WebAXRoleMenuListPopup: + return result.append("MenuListPopup"); + case WebAXRoleMenu: + return result.append("Menu"); + case WebAXRoleNavigation: + return result.append("Navigation"); + case WebAXRoleNote: + return result.append("Note"); + case WebAXRoleOutline: + return result.append("Outline"); + case WebAXRoleParagraph: + return result.append("Paragraph"); + case WebAXRolePopUpButton: + return result.append("PopUpButton"); + case WebAXRolePresentational: + return result.append("Presentational"); + case WebAXRoleProgressIndicator: + return result.append("ProgressIndicator"); + case WebAXRoleRadioButton: + return result.append("RadioButton"); + case WebAXRoleRadioGroup: + return result.append("RadioGroup"); + case WebAXRoleRegion: + return result.append("Region"); + case WebAXRoleRootWebArea: + return result.append("RootWebArea"); + case WebAXRoleRowHeader: + return result.append("RowHeader"); + case WebAXRoleRow: + return result.append("Row"); + case WebAXRoleRulerMarker: + return result.append("RulerMarker"); + case WebAXRoleRuler: + return result.append("Ruler"); + case WebAXRoleSVGRoot: + return result.append("SVGRoot"); + case WebAXRoleScrollArea: + return result.append("ScrollArea"); + case WebAXRoleScrollBar: + return result.append("ScrollBar"); + case WebAXRoleSeamlessWebArea: + return result.append("SeamlessWebArea"); + case WebAXRoleSearch: + return result.append("Search"); + case WebAXRoleSheet: + return result.append("Sheet"); + case WebAXRoleSlider: + return result.append("Slider"); + case WebAXRoleSliderThumb: + return result.append("SliderThumb"); + case WebAXRoleSpinButtonPart: + return result.append("SpinButtonPart"); + case WebAXRoleSpinButton: + return result.append("SpinButton"); + case WebAXRoleSplitGroup: + return result.append("SplitGroup"); + case WebAXRoleSplitter: + return result.append("Splitter"); + case WebAXRoleStaticText: + return result.append("StaticText"); + case WebAXRoleStatus: + return result.append("Status"); + case WebAXRoleSystemWide: + return result.append("SystemWide"); + case WebAXRoleTabGroup: + return result.append("TabGroup"); + case WebAXRoleTabList: + return result.append("TabList"); + case WebAXRoleTabPanel: + return result.append("TabPanel"); + case WebAXRoleTab: + return result.append("Tab"); + case WebAXRoleTableHeaderContainer: + return result.append("TableHeaderContainer"); + case WebAXRoleTable: + return result.append("Table"); + case WebAXRoleTextArea: + return result.append("TextArea"); + case WebAXRoleTextField: + return result.append("TextField"); + case WebAXRoleTimer: + return result.append("Timer"); + case WebAXRoleToggleButton: + return result.append("ToggleButton"); + case WebAXRoleToolbar: + return result.append("Toolbar"); + case WebAXRoleTreeGrid: + return result.append("TreeGrid"); + case WebAXRoleTreeItem: + return result.append("TreeItem"); + case WebAXRoleTree: + return result.append("Tree"); + case WebAXRoleUnknown: + return result.append("Unknown"); + case WebAXRoleUserInterfaceTooltip: + return result.append("UserInterfaceTooltip"); + case WebAXRoleValueIndicator: + return result.append("ValueIndicator"); + case WebAXRoleWebArea: + return result.append("WebArea"); + case WebAXRoleWindow: + return result.append("Window"); + default: + return result.append("Unknown"); + } +} + +string getDescription(const WebAXObject& object) +{ + string description = object.accessibilityDescription().utf8(); + return description.insert(0, "AXDescription: "); +} + +string getHelpText(const WebAXObject& object) +{ + string helpText = object.helpText().utf8(); + return helpText.insert(0, "AXHelp: "); +} + +string getStringValue(const WebAXObject& object) +{ + string value; + if (object.role() == WebAXRoleColorWell) { + int r, g, b; + char buffer[100]; + object.colorValue(r, g, b); + snprintf(buffer, sizeof(buffer), "rgb %7.5f %7.5f %7.5f 1", r / 255., g / 255., b / 255.); + value = buffer; + } else { + value = object.stringValue().utf8(); + } + return value.insert(0, "AXValue: "); +} + +string getRole(const WebAXObject& object) +{ + string roleString = roleToString(object.role()); + + // Special-case canvas with fallback content because Chromium wants to + // treat this as essentially a separate role that it can map differently depending + // on the platform. + if (object.role() == WebAXRoleCanvas && object.canvasHasFallbackContent()) + roleString += "WithFallbackContent"; + + return roleString; +} + +string getTitle(const WebAXObject& object) +{ + string title = object.title().utf8(); + return title.insert(0, "AXTitle: "); +} + +string getOrientation(const WebAXObject& object) +{ + if (object.isVertical()) + return "AXOrientation: AXVerticalOrientation"; + + return "AXOrientation: AXHorizontalOrientation"; +} + +string getValueDescription(const WebAXObject& object) +{ + string valueDescription = object.valueDescription().utf8(); + return valueDescription.insert(0, "AXValueDescription: "); +} + +string getAttributes(const WebAXObject& object) +{ + // FIXME: Concatenate all attributes of the AXObject. + string attributes(getTitle(object)); + attributes.append("\n"); + attributes.append(getRole(object)); + attributes.append("\n"); + attributes.append(getDescription(object)); + return attributes; +} + +WebRect boundsForCharacter(const WebAXObject& object, int characterIndex) +{ + BLINK_ASSERT(object.role() == WebAXRoleStaticText); + int end = 0; + for (unsigned i = 0; i < object.childCount(); i++) { + WebAXObject inlineTextBox = object.childAt(i); + BLINK_ASSERT(inlineTextBox.role() == WebAXRoleInlineTextBox); + int start = end; + end += inlineTextBox.stringValue().length(); + if (end <= characterIndex) + continue; + WebRect inlineTextBoxRect = inlineTextBox.boundingBoxRect(); + int localIndex = characterIndex - start; + WebVector<int> characterOffsets; + inlineTextBox.characterOffsets(characterOffsets); + BLINK_ASSERT(characterOffsets.size() > 0 && characterOffsets.size() == inlineTextBox.stringValue().length()); + switch (inlineTextBox.textDirection()) { + case WebAXTextDirectionLR: { + if (localIndex) { + int left = inlineTextBoxRect.x + characterOffsets[localIndex - 1]; + int width = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; + return WebRect(left, inlineTextBoxRect.y, width, inlineTextBoxRect.height); + } + return WebRect(inlineTextBoxRect.x, inlineTextBoxRect.y, characterOffsets[0], inlineTextBoxRect.height); + } + case WebAXTextDirectionRL: { + int right = inlineTextBoxRect.x + inlineTextBoxRect.width; + + if (localIndex) { + int left = right - characterOffsets[localIndex]; + int width = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; + return WebRect(left, inlineTextBoxRect.y, width, inlineTextBoxRect.height); + } + int left = right - characterOffsets[0]; + return WebRect(left, inlineTextBoxRect.y, characterOffsets[0], inlineTextBoxRect.height); + } + case WebAXTextDirectionTB: { + if (localIndex) { + int top = inlineTextBoxRect.y + characterOffsets[localIndex - 1]; + int height = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; + return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, height); + } + return WebRect(inlineTextBoxRect.x, inlineTextBoxRect.y, inlineTextBoxRect.width, characterOffsets[0]); + } + case WebAXTextDirectionBT: { + int bottom = inlineTextBoxRect.y + inlineTextBoxRect.height; + + if (localIndex) { + int top = bottom - characterOffsets[localIndex]; + int height = characterOffsets[localIndex] - characterOffsets[localIndex - 1]; + return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, height); + } + int top = bottom - characterOffsets[0]; + return WebRect(inlineTextBoxRect.x, top, inlineTextBoxRect.width, characterOffsets[0]); + } + } + } + + BLINK_ASSERT(false); + return WebRect(); +} + +void getBoundariesForOneWord(const WebAXObject& object, int characterIndex, int& wordStart, int& wordEnd) +{ + int end = 0; + for (unsigned i = 0; i < object.childCount(); i++) { + WebAXObject inlineTextBox = object.childAt(i); + BLINK_ASSERT(inlineTextBox.role() == WebAXRoleInlineTextBox); + int start = end; + end += inlineTextBox.stringValue().length(); + if (end <= characterIndex) + continue; + int localIndex = characterIndex - start; + + WebVector<int> starts; + WebVector<int> ends; + inlineTextBox.wordBoundaries(starts, ends); + size_t wordCount = starts.size(); + BLINK_ASSERT(ends.size() == wordCount); + + // If there are no words, use the InlineTextBox boundaries. + if (!wordCount) { + wordStart = start; + wordEnd = end; + return; + } + + // Look for a character within any word other than the last. + for (size_t j = 0; j < wordCount - 1; j++) { + if (localIndex <= ends[j]) { + wordStart = start + starts[j]; + wordEnd = start + ends[j]; + return; + } + } + + // Return the last word by default. + wordStart = start + starts[wordCount - 1]; + wordEnd = start + ends[wordCount - 1]; + return; + } +} + +// Collects attributes into a string, delimited by dashes. Used by all methods +// that output lists of attributes: attributesOfLinkedUIElementsCallback, +// AttributesOfChildrenCallback, etc. +class AttributesCollector { +public: + void collectAttributes(const WebAXObject& object) + { + m_attributes.append("\n------------\n"); + m_attributes.append(getAttributes(object)); + } + + string attributes() const { return m_attributes; } + +private: + string m_attributes; +}; + +} + +WebAXObjectProxy::WebAXObjectProxy(const WebAXObject& object, Factory* factory) + : m_accessibilityObject(object) + , m_factory(factory) +{ + + BLINK_ASSERT(factory); + + // + // Properties + // + + bindProperty("role", &WebAXObjectProxy::roleGetterCallback); + bindProperty("title", &WebAXObjectProxy::titleGetterCallback); + bindProperty("description", &WebAXObjectProxy::descriptionGetterCallback); + bindProperty("helpText", &WebAXObjectProxy::helpTextGetterCallback); + bindProperty("stringValue", &WebAXObjectProxy::stringValueGetterCallback); + bindProperty("x", &WebAXObjectProxy::xGetterCallback); + bindProperty("y", &WebAXObjectProxy::yGetterCallback); + bindProperty("width", &WebAXObjectProxy::widthGetterCallback); + bindProperty("height", &WebAXObjectProxy::heightGetterCallback); + bindProperty("intValue", &WebAXObjectProxy::intValueGetterCallback); + bindProperty("minValue", &WebAXObjectProxy::minValueGetterCallback); + bindProperty("maxValue", &WebAXObjectProxy::maxValueGetterCallback); + bindProperty("valueDescription", &WebAXObjectProxy::valueDescriptionGetterCallback); + bindProperty("childrenCount", &WebAXObjectProxy::childrenCountGetterCallback); + bindProperty("insertionPointLineNumber", &WebAXObjectProxy::insertionPointLineNumberGetterCallback); + bindProperty("selectedTextRange", &WebAXObjectProxy::selectedTextRangeGetterCallback); + bindProperty("isEnabled", &WebAXObjectProxy::isEnabledGetterCallback); + bindProperty("isRequired", &WebAXObjectProxy::isRequiredGetterCallback); + bindProperty("isFocused", &WebAXObjectProxy::isFocusedGetterCallback); + bindProperty("isFocusable", &WebAXObjectProxy::isFocusableGetterCallback); + bindProperty("isSelected", &WebAXObjectProxy::isSelectedGetterCallback); + bindProperty("isSelectable", &WebAXObjectProxy::isSelectableGetterCallback); + bindProperty("isMultiSelectable", &WebAXObjectProxy::isMultiSelectableGetterCallback); + bindProperty("isSelectedOptionActive", &WebAXObjectProxy::isSelectedOptionActiveGetterCallback); + bindProperty("isExpanded", &WebAXObjectProxy::isExpandedGetterCallback); + bindProperty("isChecked", &WebAXObjectProxy::isCheckedGetterCallback); + bindProperty("isVisible", &WebAXObjectProxy::isVisibleGetterCallback); + bindProperty("isOffScreen", &WebAXObjectProxy::isOffScreenGetterCallback); + bindProperty("isCollapsed", &WebAXObjectProxy::isCollapsedGetterCallback); + bindProperty("hasPopup", &WebAXObjectProxy::hasPopupGetterCallback); + bindProperty("isValid", &WebAXObjectProxy::isValidGetterCallback); + bindProperty("isReadOnly", &WebAXObjectProxy::isReadOnlyGetterCallback); + bindProperty("orientation", &WebAXObjectProxy::orientationGetterCallback); + bindProperty("clickPointX", &WebAXObjectProxy::clickPointXGetterCallback); + bindProperty("clickPointY", &WebAXObjectProxy::clickPointYGetterCallback); + bindProperty("rowCount", &WebAXObjectProxy::rowCountGetterCallback); + bindProperty("columnCount", &WebAXObjectProxy::columnCountGetterCallback); + bindProperty("isClickable", &WebAXObjectProxy::isClickableGetterCallback); + + // + // Methods + // + + bindMethod("allAttributes", &WebAXObjectProxy::allAttributesCallback); + bindMethod("attributesOfChildren", &WebAXObjectProxy::attributesOfChildrenCallback); + bindMethod("lineForIndex", &WebAXObjectProxy::lineForIndexCallback); + bindMethod("boundsForRange", &WebAXObjectProxy::boundsForRangeCallback); + bindMethod("childAtIndex", &WebAXObjectProxy::childAtIndexCallback); + bindMethod("elementAtPoint", &WebAXObjectProxy::elementAtPointCallback); + bindMethod("tableHeader", &WebAXObjectProxy::tableHeaderCallback); + bindMethod("rowIndexRange", &WebAXObjectProxy::rowIndexRangeCallback); + bindMethod("columnIndexRange", &WebAXObjectProxy::columnIndexRangeCallback); + bindMethod("cellForColumnAndRow", &WebAXObjectProxy::cellForColumnAndRowCallback); + bindMethod("titleUIElement", &WebAXObjectProxy::titleUIElementCallback); + bindMethod("setSelectedTextRange", &WebAXObjectProxy::setSelectedTextRangeCallback); + bindMethod("isAttributeSettable", &WebAXObjectProxy::isAttributeSettableCallback); + bindMethod("isPressActionSupported", &WebAXObjectProxy::isPressActionSupportedCallback); + bindMethod("isIncrementActionSupported", &WebAXObjectProxy::isIncrementActionSupportedCallback); + bindMethod("isDecrementActionSupported", &WebAXObjectProxy::isDecrementActionSupportedCallback); + bindMethod("parentElement", &WebAXObjectProxy::parentElementCallback); + bindMethod("increment", &WebAXObjectProxy::incrementCallback); + bindMethod("decrement", &WebAXObjectProxy::decrementCallback); + bindMethod("showMenu", &WebAXObjectProxy::showMenuCallback); + bindMethod("press", &WebAXObjectProxy::pressCallback); + bindMethod("isEqual", &WebAXObjectProxy::isEqualCallback); + bindMethod("addNotificationListener", &WebAXObjectProxy::addNotificationListenerCallback); + bindMethod("removeNotificationListener", &WebAXObjectProxy::removeNotificationListenerCallback); + bindMethod("takeFocus", &WebAXObjectProxy::takeFocusCallback); + bindMethod("scrollToMakeVisible", &WebAXObjectProxy::scrollToMakeVisibleCallback); + bindMethod("scrollToMakeVisibleWithSubFocus", &WebAXObjectProxy::scrollToMakeVisibleWithSubFocusCallback); + bindMethod("scrollToGlobalPoint", &WebAXObjectProxy::scrollToGlobalPointCallback); + bindMethod("wordStart", &WebAXObjectProxy::wordStartCallback); + bindMethod("wordEnd", &WebAXObjectProxy::wordEndCallback); + + bindFallbackMethod(&WebAXObjectProxy::fallbackCallback); +} + +WebAXObjectProxy* WebAXObjectProxy::getChildAtIndex(unsigned index) +{ + return m_factory->getOrCreate(accessibilityObject().childAt(index)); +} + +bool WebAXObjectProxy::isEqual(const blink::WebAXObject& other) +{ + return accessibilityObject().equals(other); +} + +void WebAXObjectProxy::notificationReceived(const char* notificationName) +{ + size_t callbackCount = m_notificationCallbacks.size(); + for (size_t i = 0; i < callbackCount; i++) { + CppVariant notificationNameArgument; + notificationNameArgument.set(notificationName); + CppVariant invokeResult; + m_notificationCallbacks[i].invokeDefault(¬ificationNameArgument, 1, invokeResult); + } +} + +// +// Properties +// + +void WebAXObjectProxy::roleGetterCallback(CppVariant* result) +{ + result->set(getRole(accessibilityObject())); +} + +void WebAXObjectProxy::titleGetterCallback(CppVariant* result) +{ + result->set(getTitle(accessibilityObject())); +} + +void WebAXObjectProxy::descriptionGetterCallback(CppVariant* result) +{ + result->set(getDescription(accessibilityObject())); +} + +void WebAXObjectProxy::helpTextGetterCallback(CppVariant* result) +{ + result->set(getHelpText(accessibilityObject())); +} + +void WebAXObjectProxy::stringValueGetterCallback(CppVariant* result) +{ + result->set(getStringValue(accessibilityObject())); +} + +void WebAXObjectProxy::xGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().x); +} + +void WebAXObjectProxy::yGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().y); +} + +void WebAXObjectProxy::widthGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().width); +} + +void WebAXObjectProxy::heightGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().height); +} + +void WebAXObjectProxy::intValueGetterCallback(CppVariant* result) +{ + if (accessibilityObject().supportsRangeValue()) + result->set(accessibilityObject().valueForRange()); + else if (accessibilityObject().role() == WebAXRoleHeading) + result->set(accessibilityObject().headingLevel()); + else + result->set(atoi(accessibilityObject().stringValue().utf8().data())); +} + +void WebAXObjectProxy::minValueGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().minValueForRange()); +} + +void WebAXObjectProxy::maxValueGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().maxValueForRange()); +} + +void WebAXObjectProxy::valueDescriptionGetterCallback(CppVariant* result) +{ + result->set(getValueDescription(accessibilityObject())); +} + +void WebAXObjectProxy::childrenCountGetterCallback(CppVariant* result) +{ + int count = 1; // Root object always has only one child, the WebView. + if (!isRoot()) + count = accessibilityObject().childCount(); + result->set(count); +} + +void WebAXObjectProxy::insertionPointLineNumberGetterCallback(CppVariant* result) +{ + if (!accessibilityObject().isFocused()) { + result->set(-1); + return; + } + + int lineNumber = accessibilityObject().selectionEndLineNumber(); + result->set(lineNumber); +} + +void WebAXObjectProxy::selectedTextRangeGetterCallback(CppVariant* result) +{ + unsigned selectionStart = accessibilityObject().selectionStart(); + unsigned selectionEnd = accessibilityObject().selectionEnd(); + char buffer[100]; + snprintf(buffer, sizeof(buffer), "{%d, %d}", selectionStart, selectionEnd - selectionStart); + + result->set(std::string(buffer)); +} + +void WebAXObjectProxy::isEnabledGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isEnabled()); +} + +void WebAXObjectProxy::isRequiredGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isRequired()); +} + +void WebAXObjectProxy::isFocusedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isFocused()); +} + +void WebAXObjectProxy::isFocusableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().canSetFocusAttribute()); +} + +void WebAXObjectProxy::isSelectedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isSelected()); +} + +void WebAXObjectProxy::isSelectableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().canSetSelectedAttribute()); +} + +void WebAXObjectProxy::isMultiSelectableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isMultiSelectable()); +} + +void WebAXObjectProxy::isSelectedOptionActiveGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isSelectedOptionActive()); +} + +void WebAXObjectProxy::isExpandedGetterCallback(CppVariant* result) +{ + result->set(!accessibilityObject().isCollapsed()); +} + +void WebAXObjectProxy::isCheckedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isChecked()); +} + +void WebAXObjectProxy::isVisibleGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isVisible()); +} + +void WebAXObjectProxy::isOffScreenGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isOffScreen()); +} + +void WebAXObjectProxy::isCollapsedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isCollapsed()); +} + +void WebAXObjectProxy::hasPopupGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().ariaHasPopup()); +} + +void WebAXObjectProxy::isValidGetterCallback(CppVariant* result) +{ + result->set(!accessibilityObject().isDetached()); +} + +void WebAXObjectProxy::isReadOnlyGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isReadOnly()); +} + +void WebAXObjectProxy::orientationGetterCallback(CppVariant* result) +{ + result->set(getOrientation(accessibilityObject())); +} + +void WebAXObjectProxy::clickPointXGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().clickPoint().x); +} + +void WebAXObjectProxy::clickPointYGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().clickPoint().y); +} + +void WebAXObjectProxy::rowCountGetterCallback(CppVariant* result) +{ + result->set(static_cast<int32_t>(accessibilityObject().rowCount())); +} + +void WebAXObjectProxy::columnCountGetterCallback(CppVariant* result) +{ + result->set(static_cast<int32_t>(accessibilityObject().columnCount())); +} + +void WebAXObjectProxy::isClickableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isClickable()); +} + +// +// Methods +// + +void WebAXObjectProxy::allAttributesCallback(const CppArgumentList&, CppVariant* result) +{ + result->set(getAttributes(accessibilityObject())); +} + +void WebAXObjectProxy::attributesOfChildrenCallback(const CppArgumentList& arguments, CppVariant* result) +{ + AttributesCollector collector; + unsigned size = accessibilityObject().childCount(); + for (unsigned i = 0; i < size; ++i) + collector.collectAttributes(accessibilityObject().childAt(i)); + result->set(collector.attributes()); +} + +void WebAXObjectProxy::lineForIndexCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (!arguments.size() || !arguments[0].isNumber()) { + result->setNull(); + return; + } + + int index = arguments[0].toInt32(); + + WebVector<int> lineBreaks; + accessibilityObject().lineBreaks(lineBreaks); + int line = 0; + int vectorSize = static_cast<int>(lineBreaks.size()); + while (line < vectorSize && lineBreaks[line] <= index) + line++; + result->set(line); +} + +void WebAXObjectProxy::boundsForRangeCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + if (accessibilityObject().role() != WebAXRoleStaticText) + return; + + int start = arguments[0].toInt32(); + int end = arguments[1].toInt32(); + int len = end - start; + + // Get the bounds for each character and union them into one large rectangle. + // This is just for testing so it doesn't need to be efficient. + WebRect bounds = boundsForCharacter(accessibilityObject(), start); + for (int i = 1; i < len; i++) { + WebRect next = boundsForCharacter(accessibilityObject(), start + i); + int right = std::max(bounds.x + bounds.width, next.x + next.width); + int bottom = std::max(bounds.y + bounds.height, next.y + next.height); + bounds.x = std::min(bounds.x, next.x); + bounds.y = std::min(bounds.y, next.y); + bounds.width = right - bounds.x; + bounds.height = bottom - bounds.y; + } + + char buffer[100]; + snprintf(buffer, sizeof(buffer), "{x: %d, y: %d, width: %d, height: %d}", bounds.x, bounds.y, bounds.width, bounds.height); + result->set(string(buffer)); +} + +void WebAXObjectProxy::childAtIndexCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (!arguments.size() || !arguments[0].isNumber()) { + result->setNull(); + return; + } + + WebAXObjectProxy* child = getChildAtIndex(arguments[0].toInt32()); + if (!child) { + result->setNull(); + return; + } + + result->set(*(child->getAsCppVariant())); +} + +void WebAXObjectProxy::elementAtPointCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + int x = arguments[0].toInt32(); + int y = arguments[1].toInt32(); + WebPoint point(x, y); + WebAXObject obj = accessibilityObject().hitTest(point); + if (obj.isNull()) + return; + + result->set(*(m_factory->getOrCreate(obj)->getAsCppVariant())); +} + +void WebAXObjectProxy::tableHeaderCallback(const CppArgumentList&, CppVariant* result) +{ + WebAXObject obj = accessibilityObject().headerContainerObject(); + if (obj.isNull()) { + result->setNull(); + return; + } + + result->set(*(m_factory->getOrCreate(obj)->getAsCppVariant())); +} + +void WebAXObjectProxy::rowIndexRangeCallback(const CppArgumentList&, CppVariant* result) +{ + unsigned rowIndex = accessibilityObject().cellRowIndex(); + unsigned rowSpan = accessibilityObject().cellRowSpan(); + char buffer[100]; + snprintf(buffer, sizeof(buffer), "{%d, %d}", rowIndex, rowSpan); + string value = buffer; + result->set(std::string(buffer)); +} + +void WebAXObjectProxy::columnIndexRangeCallback(const CppArgumentList&, CppVariant* result) +{ + unsigned columnIndex = accessibilityObject().cellColumnIndex(); + unsigned columnSpan = accessibilityObject().cellColumnSpan(); + char buffer[100]; + snprintf(buffer, sizeof(buffer), "{%d, %d}", columnIndex, columnSpan); + result->set(std::string(buffer)); +} + +void WebAXObjectProxy::cellForColumnAndRowCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + int column = arguments[0].toInt32(); + int row = arguments[1].toInt32(); + WebAXObject obj = accessibilityObject().cellForColumnAndRow(column, row); + if (obj.isNull()) { + result->setNull(); + return; + } + + result->set(*(m_factory->getOrCreate(obj)->getAsCppVariant())); +} + +void WebAXObjectProxy::titleUIElementCallback(const CppArgumentList&, CppVariant* result) +{ + WebAXObject obj = accessibilityObject().titleUIElement(); + if (obj.isNull()) { + result->setNull(); + return; + } + + result->set(*(m_factory->getOrCreate(obj)->getAsCppVariant())); +} + +void WebAXObjectProxy::setSelectedTextRangeCallback(const CppArgumentList&arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + int selectionStart = arguments[0].toInt32(); + int selectionEnd = selectionStart + arguments[1].toInt32(); + accessibilityObject().setSelectedTextRange(selectionStart, selectionEnd); +} + +void WebAXObjectProxy::isAttributeSettableCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 && !arguments[0].isString()) { + result->setNull(); + return; + } + + string attribute = arguments[0].toString(); + bool settable = false; + if (attribute == "AXValue") + settable = accessibilityObject().canSetValueAttribute(); + result->set(settable); +} + +void WebAXObjectProxy::isPressActionSupportedCallback(const CppArgumentList&, CppVariant* result) +{ + result->set(accessibilityObject().canPress()); +} + +void WebAXObjectProxy::isIncrementActionSupportedCallback(const CppArgumentList&, CppVariant* result) +{ + result->set(accessibilityObject().canIncrement()); +} + +void WebAXObjectProxy::isDecrementActionSupportedCallback(const CppArgumentList&, CppVariant* result) +{ + result->set(accessibilityObject().canDecrement()); +} + +void WebAXObjectProxy::parentElementCallback(const CppArgumentList&, CppVariant* result) +{ + WebAXObject parentObject = accessibilityObject().parentObject(); + while (parentObject.accessibilityIsIgnored()) + parentObject = parentObject.parentObject(); + WebAXObjectProxy* parent = m_factory->getOrCreate(parentObject); + if (!parent) { + result->setNull(); + return; + } + + result->set(*(parent->getAsCppVariant())); +} + +void WebAXObjectProxy::incrementCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().increment(); + result->setNull(); +} + +void WebAXObjectProxy::decrementCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().decrement(); + result->setNull(); +} + +void WebAXObjectProxy::showMenuCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void WebAXObjectProxy::pressCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().press(); + result->setNull(); +} + +void WebAXObjectProxy::isEqualCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + result->set(arguments[0].isEqual(*getAsCppVariant())); +} + +void WebAXObjectProxy::addNotificationListenerCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + m_notificationCallbacks.push_back(arguments[0]); + result->setNull(); +} + +void WebAXObjectProxy::removeNotificationListenerCallback(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void WebAXObjectProxy::takeFocusCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().setFocused(true); + result->setNull(); +} + +void WebAXObjectProxy::scrollToMakeVisibleCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().scrollToMakeVisible(); + result->setNull(); +} + +void WebAXObjectProxy::scrollToMakeVisibleWithSubFocusCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 4 + || !arguments[0].isNumber() + || !arguments[1].isNumber() + || !arguments[2].isNumber() + || !arguments[3].isNumber()) + return; + + int x = arguments[0].toInt32(); + int y = arguments[1].toInt32(); + int width = arguments[2].toInt32(); + int height = arguments[3].toInt32(); + accessibilityObject().scrollToMakeVisibleWithSubFocus(WebRect(x, y, width, height)); + result->setNull(); +} + +void WebAXObjectProxy::scrollToGlobalPointCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 + || !arguments[0].isNumber() + || !arguments[1].isNumber()) + return; + + int x = arguments[0].toInt32(); + int y = arguments[1].toInt32(); + + accessibilityObject().scrollToGlobalPoint(WebPoint(x, y)); + result->setNull(); +} + +void WebAXObjectProxy::wordStartCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 1 || !arguments[0].isNumber()) + return; + + if (accessibilityObject().role() != WebAXRoleStaticText) + return; + + int characterIndex = arguments[0].toInt32(); + int wordStart, wordEnd; + getBoundariesForOneWord(accessibilityObject(), characterIndex, wordStart, wordEnd); + result->set(wordStart); +} + +void WebAXObjectProxy::wordEndCallback(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 1 || !arguments[0].isNumber()) + return; + + if (accessibilityObject().role() != WebAXRoleStaticText) + return; + + int characterIndex = arguments[0].toInt32(); + int wordStart, wordEnd; + getBoundariesForOneWord(accessibilityObject(), characterIndex, wordStart, wordEnd); + result->set(wordEnd); +} + +void WebAXObjectProxy::fallbackCallback(const CppArgumentList &, CppVariant* result) +{ + result->setNull(); +} + +RootWebAXObjectProxy::RootWebAXObjectProxy(const WebAXObject &object, Factory *factory) + : WebAXObjectProxy(object, factory) { } + +WebAXObjectProxy* RootWebAXObjectProxy::getChildAtIndex(unsigned index) +{ + if (index) + return 0; + + return factory()->getOrCreate(accessibilityObject()); +} + + +WebAXObjectProxyList ::~WebAXObjectProxyList() +{ + clear(); +} + +void WebAXObjectProxyList::clear() +{ + for (ElementList::iterator i = m_elements.begin(); i != m_elements.end(); ++i) + delete (*i); + m_elements.clear(); +} + +WebAXObjectProxy* WebAXObjectProxyList::getOrCreate(const WebAXObject& object) +{ + if (object.isNull()) + return 0; + + size_t elementCount = m_elements.size(); + for (size_t i = 0; i < elementCount; i++) { + if (m_elements[i]->isEqual(object)) + return m_elements[i]; + } + + WebAXObjectProxy* element = new WebAXObjectProxy(object, this); + m_elements.push_back(element); + return element; +} + +WebAXObjectProxy* WebAXObjectProxyList::createRoot(const WebAXObject& object) +{ + WebAXObjectProxy* element = new RootWebAXObjectProxy(object, this); + m_elements.push_back(element); + return element; +} + +} diff --git a/content/shell/renderer/test_runner/WebAXObjectProxy.h b/content/shell/renderer/test_runner/WebAXObjectProxy.h new file mode 100644 index 0000000..a90273f --- /dev/null +++ b/content/shell/renderer/test_runner/WebAXObjectProxy.h @@ -0,0 +1,145 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebAXObjectProxy_h +#define WebAXObjectProxy_h + +#include <vector> + +#include "content/shell/renderer/test_runner/CppBoundClass.h" +#include "third_party/WebKit/public/web/WebAXObject.h" + +namespace WebTestRunner { + +class WebAXObjectProxy : public CppBoundClass { +public: + class Factory { + public: + virtual ~Factory() { } + virtual WebAXObjectProxy* getOrCreate(const blink::WebAXObject&) = 0; + }; + + WebAXObjectProxy(const blink::WebAXObject&, Factory*); + + virtual WebAXObjectProxy* getChildAtIndex(unsigned); + virtual bool isRoot() const { return false; } + virtual bool isEqual(const blink::WebAXObject&); + + virtual void notificationReceived(const char *notificationName); + +protected: + const blink::WebAXObject& accessibilityObject() const { return m_accessibilityObject; } + + Factory* factory() const { return m_factory; } + +private: + // Bound properties. + void roleGetterCallback(CppVariant*); + void titleGetterCallback(CppVariant*); + void descriptionGetterCallback(CppVariant*); + void helpTextGetterCallback(CppVariant*); + void stringValueGetterCallback(CppVariant*); + void xGetterCallback(CppVariant*); + void yGetterCallback(CppVariant*); + void widthGetterCallback(CppVariant*); + void heightGetterCallback(CppVariant*); + void intValueGetterCallback(CppVariant*); + void minValueGetterCallback(CppVariant*); + void maxValueGetterCallback(CppVariant*); + void valueDescriptionGetterCallback(CppVariant*); + void childrenCountGetterCallback(CppVariant*); + void insertionPointLineNumberGetterCallback(CppVariant*); + void selectedTextRangeGetterCallback(CppVariant*); + void isEnabledGetterCallback(CppVariant*); + void isRequiredGetterCallback(CppVariant*); + void isFocusedGetterCallback(CppVariant*); + void isFocusableGetterCallback(CppVariant*); + void isSelectedGetterCallback(CppVariant*); + void isSelectableGetterCallback(CppVariant*); + void isMultiSelectableGetterCallback(CppVariant*); + void isSelectedOptionActiveGetterCallback(CppVariant*); + void isExpandedGetterCallback(CppVariant*); + void isCheckedGetterCallback(CppVariant*); + void isVisibleGetterCallback(CppVariant*); + void isOffScreenGetterCallback(CppVariant*); + void isCollapsedGetterCallback(CppVariant*); + void hasPopupGetterCallback(CppVariant*); + void isValidGetterCallback(CppVariant*); + void isReadOnlyGetterCallback(CppVariant*); + void orientationGetterCallback(CppVariant*); + void clickPointXGetterCallback(CppVariant*); + void clickPointYGetterCallback(CppVariant*); + void rowCountGetterCallback(CppVariant*); + void columnCountGetterCallback(CppVariant*); + void isClickableGetterCallback(CppVariant*); + + // Bound methods. + void allAttributesCallback(const CppArgumentList&, CppVariant*); + void attributesOfChildrenCallback(const CppArgumentList&, CppVariant*); + void lineForIndexCallback(const CppArgumentList&, CppVariant*); + void boundsForRangeCallback(const CppArgumentList&, CppVariant*); + void childAtIndexCallback(const CppArgumentList&, CppVariant*); + void elementAtPointCallback(const CppArgumentList&, CppVariant*); + void tableHeaderCallback(const CppArgumentList&, CppVariant*); + void rowIndexRangeCallback(const CppArgumentList&, CppVariant*); + void columnIndexRangeCallback(const CppArgumentList&, CppVariant*); + void cellForColumnAndRowCallback(const CppArgumentList&, CppVariant*); + void titleUIElementCallback(const CppArgumentList&, CppVariant*); + void setSelectedTextRangeCallback(const CppArgumentList&, CppVariant*); + void isAttributeSettableCallback(const CppArgumentList&, CppVariant*); + void isPressActionSupportedCallback(const CppArgumentList&, CppVariant*); + void isIncrementActionSupportedCallback(const CppArgumentList&, CppVariant*); + void isDecrementActionSupportedCallback(const CppArgumentList&, CppVariant*); + void parentElementCallback(const CppArgumentList&, CppVariant*); + void incrementCallback(const CppArgumentList&, CppVariant*); + void decrementCallback(const CppArgumentList&, CppVariant*); + void showMenuCallback(const CppArgumentList&, CppVariant*); + void pressCallback(const CppArgumentList&, CppVariant*); + void isEqualCallback(const CppArgumentList&, CppVariant*); + void addNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void removeNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void takeFocusCallback(const CppArgumentList&, CppVariant*); + void scrollToMakeVisibleCallback(const CppArgumentList&, CppVariant*); + void scrollToMakeVisibleWithSubFocusCallback(const CppArgumentList&, CppVariant*); + void scrollToGlobalPointCallback(const CppArgumentList&, CppVariant*); + void wordStartCallback(const CppArgumentList&, CppVariant*); + void wordEndCallback(const CppArgumentList&, CppVariant*); + + void fallbackCallback(const CppArgumentList&, CppVariant*); + + blink::WebAXObject m_accessibilityObject; + Factory* m_factory; + std::vector<CppVariant> m_notificationCallbacks; +}; + + +class RootWebAXObjectProxy : public WebAXObjectProxy { +public: + RootWebAXObjectProxy(const blink::WebAXObject&, Factory*); + + virtual WebAXObjectProxy* getChildAtIndex(unsigned); + virtual bool isRoot() const { return true; } +}; + + +// Provides simple lifetime management of the WebAXObjectProxy instances: +// all WebAXObjectProxys ever created from the controller are stored in +// a list and cleared explicitly. +class WebAXObjectProxyList : public WebAXObjectProxy::Factory { +public: + WebAXObjectProxyList() { } + virtual ~WebAXObjectProxyList(); + + void clear(); + virtual WebAXObjectProxy* getOrCreate(const blink::WebAXObject&); + WebAXObjectProxy* createRoot(const blink::WebAXObject&); + +private: + typedef std::vector<WebAXObjectProxy*> ElementList; + ElementList m_elements; +}; + +} + +#endif // WebAXObjectProxy_h diff --git a/content/shell/renderer/test_runner/WebFrameTestProxy.h b/content/shell/renderer/test_runner/WebFrameTestProxy.h new file mode 100644 index 0000000..400d2bb --- /dev/null +++ b/content/shell/renderer/test_runner/WebFrameTestProxy.h @@ -0,0 +1,154 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebFrameTestProxy_h +#define WebFrameTestProxy_h + +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +namespace WebTestRunner { + +// Templetized wrapper around RenderFrameImpl objects, which implement +// the WebFrameClient interface. +template<class Base, typename P, typename R> +class WebFrameTestProxy : public Base, public blink::WebNonCopyable { +public: + WebFrameTestProxy(P p, R r) + : Base(p, r) + , m_baseProxy(0) + , m_version(0) { } + + virtual ~WebFrameTestProxy() { } + + void setBaseProxy(WebTestProxyBase* proxy) + { + m_baseProxy = proxy; + } + + void setVersion(int version) + { + m_version = version; + } + + blink::WebPlugin* createPlugin(blink::WebFrame* frame, const blink::WebPluginParams& params) + { + blink::WebPlugin* plugin = m_baseProxy->createPlugin(frame, params); + if (plugin) + return plugin; + return Base::createPlugin(frame, params); + } + + // WebFrameClient implementation. + virtual void didStartProvisionalLoad(blink::WebFrame* frame) + { + if (m_version > 2) + m_baseProxy->didStartProvisionalLoad(frame); + Base::didStartProvisionalLoad(frame); + } + virtual void didReceiveServerRedirectForProvisionalLoad(blink::WebFrame* frame) + { + Base::didReceiveServerRedirectForProvisionalLoad(frame); + } + virtual void didFailProvisionalLoad(blink::WebFrame* frame, const blink::WebURLError& error) + { + Base::didFailProvisionalLoad(frame, error); + } + virtual void didCommitProvisionalLoad(blink::WebFrame* frame, bool isNewNavigation) + { + Base::didCommitProvisionalLoad(frame, isNewNavigation); + } + virtual void didReceiveTitle(blink::WebFrame* frame, const blink::WebString& title, blink::WebTextDirection direction) + { + Base::didReceiveTitle(frame, title, direction); + } + virtual void didChangeIcon(blink::WebFrame* frame, blink::WebIconURL::Type iconType) + { + Base::didChangeIcon(frame, iconType); + } + virtual void didFinishDocumentLoad(blink::WebFrame* frame) + { + Base::didFinishDocumentLoad(frame); + } + virtual void didHandleOnloadEvents(blink::WebFrame* frame) + { + Base::didHandleOnloadEvents(frame); + } + virtual void didFailLoad(blink::WebFrame* frame, const blink::WebURLError& error) + { + Base::didFailLoad(frame, error); + } + virtual void didFinishLoad(blink::WebFrame* frame) + { + Base::didFinishLoad(frame); + } + virtual void didDetectXSS(blink::WebFrame* frame, const blink::WebURL& insecureURL, bool didBlockEntirePage) + { + // This is not implemented in RenderFrameImpl, so need to explicitly call + // into the base proxy. + m_baseProxy->didDetectXSS(frame, insecureURL, didBlockEntirePage); + Base::didDetectXSS(frame, insecureURL, didBlockEntirePage); + } + virtual void didDispatchPingLoader(blink::WebFrame* frame, const blink::WebURL& url) + { + // This is not implemented in RenderFrameImpl, so need to explicitly call + // into the base proxy. + m_baseProxy->didDispatchPingLoader(frame, url); + Base::didDispatchPingLoader(frame, url); + } + virtual void willRequestResource(blink::WebFrame* frame, const blink::WebCachedURLRequest& request) + { + // This is not implemented in RenderFrameImpl, so need to explicitly call + // into the base proxy. + m_baseProxy->willRequestResource(frame, request); + Base::willRequestResource(frame, request); + } + virtual void didCreateDataSource(blink::WebFrame* frame, blink::WebDataSource* ds) + { + Base::didCreateDataSource(frame, ds); + } + virtual void willSendRequest(blink::WebFrame* frame, unsigned identifier, blink::WebURLRequest& request, const blink::WebURLResponse& redirectResponse) + { + m_baseProxy->willSendRequest(frame, identifier, request, redirectResponse); + Base::willSendRequest(frame, identifier, request, redirectResponse); + } + virtual void didReceiveResponse(blink::WebFrame* frame, unsigned identifier, const blink::WebURLResponse& response) + { + m_baseProxy->didReceiveResponse(frame, identifier, response); + Base::didReceiveResponse(frame, identifier, response); + } + virtual void didChangeResourcePriority(blink::WebFrame* frame, unsigned identifier, const blink::WebURLRequest::Priority& priority) + { + // This is not implemented in RenderFrameImpl, so need to explicitly call + // into the base proxy. + m_baseProxy->didChangeResourcePriority(frame, identifier, priority); + Base::didChangeResourcePriority(frame, identifier, priority); + } + virtual void didFinishResourceLoad(blink::WebFrame* frame, unsigned identifier) + { + Base::didFinishResourceLoad(frame, identifier); + } + virtual blink::WebNavigationPolicy decidePolicyForNavigation(blink::WebFrame* frame, blink::WebDataSource::ExtraData* extraData, const blink::WebURLRequest& request, blink::WebNavigationType type, blink::WebNavigationPolicy defaultPolicy, bool isRedirect) + { + return Base::decidePolicyForNavigation(frame, extraData, request, type, defaultPolicy, isRedirect); + } + virtual bool willCheckAndDispatchMessageEvent(blink::WebFrame* sourceFrame, blink::WebFrame* targetFrame, blink::WebSecurityOrigin target, blink::WebDOMMessageEvent event) + { + if (m_baseProxy->willCheckAndDispatchMessageEvent(sourceFrame, targetFrame, target, event)) + return true; + return Base::willCheckAndDispatchMessageEvent(sourceFrame, targetFrame, target, event); + } + +private: + WebTestProxyBase* m_baseProxy; + + // This is used to incrementally change code between Blink and Chromium. + // It is used instead of a #define and is set by layouttest_support when + // creating this object. + int m_version; +}; + +} + +#endif // WebTestProxy_h diff --git a/content/shell/renderer/test_runner/WebPermissions.cpp b/content/shell/renderer/test_runner/WebPermissions.cpp new file mode 100644 index 0000000..6837172 --- /dev/null +++ b/content/shell/renderer/test_runner/WebPermissions.cpp @@ -0,0 +1,113 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebPermissions.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/WebURL.h" + +using namespace std; + +namespace WebTestRunner { + +WebPermissions::WebPermissions() + : m_delegate(0) +{ + reset(); +} + +WebPermissions::~WebPermissions() +{ +} + +bool WebPermissions::allowImage(blink::WebFrame*, bool enabledPerSettings, const blink::WebURL& imageURL) +{ + bool allowed = enabledPerSettings && m_imagesAllowed; + if (m_dumpCallbacks && m_delegate) + m_delegate->printMessage(std::string("PERMISSION CLIENT: allowImage(") + normalizeLayoutTestURL(imageURL.spec()) + "): " + (allowed ? "true" : "false") + "\n"); + return allowed; +} + +bool WebPermissions::allowScriptFromSource(blink::WebFrame*, bool enabledPerSettings, const blink::WebURL& scriptURL) +{ + bool allowed = enabledPerSettings && m_scriptsAllowed; + if (m_dumpCallbacks && m_delegate) + m_delegate->printMessage(std::string("PERMISSION CLIENT: allowScriptFromSource(") + normalizeLayoutTestURL(scriptURL.spec()) + "): " + (allowed ? "true" : "false") + "\n"); + return allowed; +} + +bool WebPermissions::allowStorage(blink::WebFrame*, bool) +{ + return m_storageAllowed; +} + +bool WebPermissions::allowPlugins(blink::WebFrame*, bool enabledPerSettings) +{ + return enabledPerSettings && m_pluginsAllowed; +} + +bool WebPermissions::allowDisplayingInsecureContent(blink::WebFrame*, bool enabledPerSettings, const blink::WebSecurityOrigin&, const blink::WebURL&) +{ + return enabledPerSettings || m_displayingInsecureContentAllowed; +} + +bool WebPermissions::allowRunningInsecureContent(blink::WebFrame*, bool enabledPerSettings, const blink::WebSecurityOrigin&, const blink::WebURL&) +{ + return enabledPerSettings || m_runningInsecureContentAllowed; +} + +void WebPermissions::setImagesAllowed(bool imagesAllowed) +{ + m_imagesAllowed = imagesAllowed; +} + +void WebPermissions::setScriptsAllowed(bool scriptsAllowed) +{ + m_scriptsAllowed = scriptsAllowed; +} + +void WebPermissions::setStorageAllowed(bool storageAllowed) +{ + m_storageAllowed = storageAllowed; +} + +void WebPermissions::setPluginsAllowed(bool pluginsAllowed) +{ + m_pluginsAllowed = pluginsAllowed; +} + +void WebPermissions::setDisplayingInsecureContentAllowed(bool allowed) +{ + m_displayingInsecureContentAllowed = allowed; +} + +void WebPermissions::setRunningInsecureContentAllowed(bool allowed) +{ + m_runningInsecureContentAllowed = allowed; +} + +void WebPermissions::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; +} + +void WebPermissions::setDumpCallbacks(bool dumpCallbacks) +{ + m_dumpCallbacks = dumpCallbacks; +} + +void WebPermissions::reset() +{ + m_dumpCallbacks = false; + m_imagesAllowed = true; + m_scriptsAllowed = true; + m_storageAllowed = true; + m_pluginsAllowed = true; + m_displayingInsecureContentAllowed = false; + m_runningInsecureContentAllowed = false; +} + +} diff --git a/content/shell/renderer/test_runner/WebPermissions.h b/content/shell/renderer/test_runner/WebPermissions.h new file mode 100644 index 0000000..f9294cd --- /dev/null +++ b/content/shell/renderer/test_runner/WebPermissions.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebPermissions_h +#define WebPermissions_h + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/web/WebPermissionClient.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +class WebPermissions : public blink::WebPermissionClient, public blink::WebNonCopyable { +public: + WebPermissions(); + virtual ~WebPermissions(); + + // Override WebPermissionClient methods. + virtual bool allowImage(blink::WebFrame*, bool enabledPerSettings, const blink::WebURL& imageURL); + virtual bool allowScriptFromSource(blink::WebFrame*, bool enabledPerSettings, const blink::WebURL& scriptURL); + virtual bool allowStorage(blink::WebFrame*, bool local); + virtual bool allowPlugins(blink::WebFrame*, bool enabledPerSettings); + virtual bool allowDisplayingInsecureContent(blink::WebFrame*, bool enabledPerSettings, const blink::WebSecurityOrigin&, const blink::WebURL&); + virtual bool allowRunningInsecureContent(blink::WebFrame*, bool enabledPerSettings, const blink::WebSecurityOrigin&, const blink::WebURL&); + + // Hooks to set the different policies. + void setImagesAllowed(bool); + void setScriptsAllowed(bool); + void setStorageAllowed(bool); + void setPluginsAllowed(bool); + void setDisplayingInsecureContentAllowed(bool); + void setRunningInsecureContentAllowed(bool); + + // Resets the policy to allow everything, except for running insecure content. + void reset(); + + void setDelegate(WebTestDelegate*); + void setDumpCallbacks(bool); + +private: + WebTestDelegate* m_delegate; + bool m_dumpCallbacks; + + bool m_imagesAllowed; + bool m_scriptsAllowed; + bool m_storageAllowed; + bool m_pluginsAllowed; + bool m_displayingInsecureContentAllowed; + bool m_runningInsecureContentAllowed; +}; + +} + +#endif diff --git a/content/shell/renderer/test_runner/WebScopedPtr.h b/content/shell/renderer/test_runner/WebScopedPtr.h new file mode 100644 index 0000000..94af5b60 --- /dev/null +++ b/content/shell/renderer/test_runner/WebScopedPtr.h @@ -0,0 +1,123 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebScopedPtr_h +#define WebScopedPtr_h + +#include <stddef.h> + +namespace WebTestRunner { + +template<typename Deallocator, typename T> +class WebScopedPtrBase { +public: + // Default constructor. Constructs an empty scoped pointer. + inline WebScopedPtrBase() : m_ptr(0) { } + + // Constructs a scoped pointer from a plain one. + explicit inline WebScopedPtrBase(T* ptr) : m_ptr(ptr) { } + + // Copy constructor removes the pointer from the original to avoid double + // freeing. + inline WebScopedPtrBase(const WebScopedPtrBase<Deallocator, T>& rhs) + : m_ptr(rhs.m_ptr) + { + const_cast<WebScopedPtrBase<Deallocator, T>&>(rhs).m_ptr = 0; + } + + // When the destructor of the scoped pointer is executed the plain pointer + // is deleted using DeleteArray. This implies that you must allocate with + // NewArray. + inline ~WebScopedPtrBase() + { + if (m_ptr) + Deallocator::Delete(m_ptr); + } + + inline T* operator->() const { return m_ptr; } + + // You can get the underlying pointer out with the * operator. + inline T* operator*() { return m_ptr; } + + // You can use [n] to index as if it was a plain pointer. + inline T& operator[](size_t i) + { + return m_ptr[i]; + } + + // You can use [n] to index as if it was a plain pointer. + const inline T& operator[](size_t i) const + { + return m_ptr[i]; + } + + // We don't have implicit conversion to a T* since that hinders migration: + // You would not be able to change a method from returning a T* to + // returning an WebScopedArrayPtr<T> and then get errors wherever it is used. + inline T* get() const { return m_ptr; } + + inline void reset(T* newValue = 0) + { + if (m_ptr) + Deallocator::Delete(m_ptr); + m_ptr = newValue; + } + + // Assignment requires an empty (0) WebScopedArrayPtr as the receiver. Like + // the copy constructor it removes the pointer in the original to avoid + // double freeing. + inline WebScopedPtrBase<Deallocator, T>& operator=(const WebScopedPtrBase<Deallocator, T>& rhs) + { + reset(rhs.m_ptr); + const_cast<WebScopedPtrBase<Deallocator, T>&>(rhs).m_ptr = 0; + return *this; + } + + inline bool isEmpty() { return !m_ptr; } + +private: + T* m_ptr; +}; + +// A 'scoped array pointer' that calls DeleteArray on its pointer when the +// destructor is called. +template<typename T> +struct ArrayDeallocator { + static void Delete(T* array) + { + DeleteArray(array); + } +}; + +template<typename T> +class WebScopedArrayPtr: public WebScopedPtrBase<ArrayDeallocator<T>, T> { +public: + inline WebScopedArrayPtr() { } + explicit inline WebScopedArrayPtr(T* ptr) + : WebScopedPtrBase<ArrayDeallocator<T>, T>(ptr) { } + inline WebScopedArrayPtr(const WebScopedArrayPtr<T>& rhs) + : WebScopedPtrBase<ArrayDeallocator<T>, T>(rhs) { } +}; + +template<typename T> +struct ObjectDeallocator { + static void Delete(T* object) + { + delete object; + } +}; + +template<typename T> +class WebScopedPtr: public WebScopedPtrBase<ObjectDeallocator<T>, T> { +public: + inline WebScopedPtr() { } + explicit inline WebScopedPtr(T* ptr) + : WebScopedPtrBase<ObjectDeallocator<T>, T>(ptr) { } + inline WebScopedPtr(const WebScopedPtr<T>& rhs) + : WebScopedPtrBase<ObjectDeallocator<T>, T>(rhs) { } +}; + +} // namespace WebTestRunner + +#endif // WebScopedPtr_h diff --git a/content/shell/renderer/test_runner/WebTask.cpp b/content/shell/renderer/test_runner/WebTask.cpp new file mode 100644 index 0000000..0a21971 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTask.cpp @@ -0,0 +1,53 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebTask.h" + +#include <algorithm> +#include "third_party/WebKit/public/web/WebKit.h" + +using namespace std; + +namespace WebTestRunner { + +WebTask::WebTask(WebTaskList* list) + : m_taskList(list) +{ + m_taskList->registerTask(this); +} + +WebTask::~WebTask() +{ + if (m_taskList) + m_taskList->unregisterTask(this); +} + +WebTaskList::WebTaskList() +{ +} + +WebTaskList::~WebTaskList() +{ + revokeAll(); +} + +void WebTaskList::registerTask(WebTask* task) +{ + m_tasks.push_back(task); +} + +void WebTaskList::unregisterTask(WebTask* task) +{ + vector<WebTask*>::iterator iter = find(m_tasks.begin(), m_tasks.end(), task); + if (iter != m_tasks.end()) + m_tasks.erase(iter); +} + +void WebTaskList::revokeAll() +{ + while (!m_tasks.empty()) + m_tasks[0]->cancel(); +} + +} diff --git a/content/shell/renderer/test_runner/WebTask.h b/content/shell/renderer/test_runner/WebTask.h new file mode 100644 index 0000000..3f8a234 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTask.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTask_h +#define WebTask_h + +#include <vector> + +namespace WebTestRunner { + +class WebTaskList; + +// WebTask represents a task which can run by WebTestDelegate::postTask() or +// WebTestDelegate::postDelayedTask(). +class WebTask { +public: + explicit WebTask(WebTaskList*); + virtual ~WebTask(); + + // The main code of this task. + // An implementation of run() should return immediately if cancel() was called. + virtual void run() = 0; + virtual void cancel() = 0; + +protected: + WebTaskList* m_taskList; +}; + +class WebTaskList { +public: + WebTaskList(); + ~WebTaskList(); + void registerTask(WebTask*); + void unregisterTask(WebTask*); + void revokeAll(); + +private: + std::vector<WebTask*> m_tasks; +}; + +// A task containing an object pointer of class T. Derived classes should +// override runIfValid() which in turn can safely invoke methods on the +// m_object. The Class T must have "WebTaskList* taskList()". +template<class T> +class WebMethodTask : public WebTask { +public: + explicit WebMethodTask(T* object) + : WebTask(object->taskList()) + , m_object(object) + { + } + + virtual ~WebMethodTask() { } + + virtual void run() + { + if (m_object) + runIfValid(); + } + + virtual void cancel() + { + m_object = 0; + m_taskList->unregisterTask(this); + m_taskList = 0; + } + + virtual void runIfValid() = 0; + +protected: + T* m_object; +}; + +} + +#endif // WebTask_h diff --git a/content/shell/renderer/test_runner/WebTestDelegate.h b/content/shell/renderer/test_runner/WebTestDelegate.h new file mode 100644 index 0000000..75d7cd9 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestDelegate.h @@ -0,0 +1,142 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTestDelegate_h +#define WebTestDelegate_h + +#include <string> + +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +#define WEBTESTRUNNER_NEW_HISTORY_CAPTURE + +namespace blink { +class WebDeviceMotionData; +class WebDeviceOrientationData; +class WebFrame; +class WebGamepads; +class WebHistoryItem; +struct WebRect; +struct WebSize; +struct WebURLError; +} + +namespace WebTestRunner { + +struct WebPreferences; +class WebTask; +class WebTestProxyBase; + +class WebTestDelegate { +public: + // Set and clear the edit command to execute on the next call to + // WebViewClient::handleCurrentKeyboardEvent(). + virtual void clearEditCommand() = 0; + virtual void setEditCommand(const std::string& name, const std::string& value) = 0; + + // Set the gamepads to return from Platform::sampleGamepads(). + virtual void setGamepadData(const blink::WebGamepads&) = 0; + + // Set data to return when registering via Platform::setDeviceMotionListener(). + virtual void setDeviceMotionData(const blink::WebDeviceMotionData&) = 0; + // Set data to return when registering via Platform::setDeviceOrientationListener(). + virtual void setDeviceOrientationData(const blink::WebDeviceOrientationData&) = 0; + + // Add a message to the text dump for the layout test. + virtual void printMessage(const std::string& message) = 0; + + // The delegate takes ownership of the WebTask objects and is responsible + // for deleting them. + virtual void postTask(WebTask*) = 0; + virtual void postDelayedTask(WebTask*, long long ms) = 0; + + // Register a new isolated filesystem with the given files, and return the + // new filesystem id. + virtual blink::WebString registerIsolatedFileSystem(const blink::WebVector<blink::WebString>& absoluteFilenames) = 0; + + // Gets the current time in milliseconds since the UNIX epoch. + virtual long long getCurrentTimeInMillisecond() = 0; + + // Convert the provided relative path into an absolute path. + virtual blink::WebString getAbsoluteWebStringFromUTF8Path(const std::string& path) = 0; + + // Reads in the given file and returns its contents as data URL. + virtual blink::WebURL localFileToDataURL(const blink::WebURL&) = 0; + + // Replaces file:///tmp/LayoutTests/ with the actual path to the + // LayoutTests directory. + virtual blink::WebURL rewriteLayoutTestsURL(const std::string& utf8URL) = 0; + + // Manages the settings to used for layout tests. + virtual WebPreferences* preferences() = 0; + virtual void applyPreferences() = 0; + + // Enables or disables synchronous resize mode. When enabled, all window-sizing machinery is + // short-circuited inside the renderer. This mode is necessary for some tests that were written + // before browsers had multi-process architecture and rely on window resizes to happen synchronously. + // The function has "unfortunate" it its name because we must strive to remove all tests + // that rely on this... well, unfortunate behavior. See http://crbug.com/309760 for the plan. + virtual void useUnfortunateSynchronousResizeMode(bool) = 0; + + // Controls auto resize mode. + virtual void enableAutoResizeMode(const blink::WebSize& minSize, const blink::WebSize& maxSize) = 0; + virtual void disableAutoResizeMode(const blink::WebSize&) = 0; + + // Opens and closes the inspector. + virtual void showDevTools() = 0; + virtual void closeDevTools() = 0; + + // Evaluate the given script in the DevTools agent. + virtual void evaluateInWebInspector(long callID, const std::string& script) = 0; + + // Controls WebSQL databases. + virtual void clearAllDatabases() = 0; + virtual void setDatabaseQuota(int) = 0; + + // Controls the device scale factor of the main WebView for hidpi tests. + virtual void setDeviceScaleFactor(float) = 0; + + // Controls which WebView should be focused. + virtual void setFocus(WebTestProxyBase*, bool) = 0; + + // Controls whether all cookies should be accepted or writing cookies in a + // third-party context is blocked. + virtual void setAcceptAllCookies(bool) = 0; + + // The same as rewriteLayoutTestsURL unless the resource is a path starting + // with /tmp/, then return a file URL to a temporary file. + virtual std::string pathToLocalResource(const std::string& resource) = 0; + + // Sets the POSIX locale of the current process. + virtual void setLocale(const std::string&) = 0; + + // Invoked when the test finished. + virtual void testFinished() = 0; + + // Invoked when the embedder should close all but the main WebView. + virtual void closeRemainingWindows() = 0; + + virtual void deleteAllCookies() = 0; + + // Returns the length of the back/forward history of the main WebView. + virtual int navigationEntryCount() = 0; + + // The following trigger navigations on the main WebViwe. + virtual void goToOffset(int offset) = 0; + virtual void reload() = 0; + virtual void loadURLForFrame(const blink::WebURL&, const std::string& frameName) = 0; + + // Returns true if resource requests to external URLs should be permitted. + virtual bool allowExternalPages() = 0; + + // Returns the back/forward history for the WebView associated with the + // given WebTestProxyBase as well as the index of the current entry. + virtual void captureHistoryForWindow(WebTestProxyBase*, blink::WebVector<blink::WebHistoryItem>*, size_t* currentEntryIndex) = 0; +}; + +} + +#endif // WebTestDelegate_h diff --git a/content/shell/renderer/test_runner/WebTestInterfaces.cpp b/content/shell/renderer/test_runner/WebTestInterfaces.cpp new file mode 100644 index 0000000..692c858 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestInterfaces.cpp @@ -0,0 +1,92 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebTestInterfaces.h" + +#include "content/shell/renderer/test_runner/MockWebAudioDevice.h" +#include "content/shell/renderer/test_runner/MockWebMIDIAccessor.h" +#include "content/shell/renderer/test_runner/MockWebMediaStreamCenter.h" +#include "content/shell/renderer/test_runner/MockWebRTCPeerConnectionHandler.h" +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/TestRunner.h" + +using namespace blink; + +namespace WebTestRunner { + +WebTestInterfaces::WebTestInterfaces() + : m_interfaces(new TestInterfaces()) +{ +} + +WebTestInterfaces::~WebTestInterfaces() +{ +} + +void WebTestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) +{ + m_interfaces->setWebView(webView, proxy); +} + +void WebTestInterfaces::setDelegate(WebTestDelegate* delegate) +{ + m_interfaces->setDelegate(delegate); +} + +void WebTestInterfaces::bindTo(WebFrame* frame) +{ + m_interfaces->bindTo(frame); +} + +void WebTestInterfaces::resetAll() +{ + m_interfaces->resetAll(); +} + +void WebTestInterfaces::setTestIsRunning(bool running) +{ + m_interfaces->setTestIsRunning(running); +} + +void WebTestInterfaces::configureForTestWithURL(const WebURL& testURL, bool generatePixels) +{ + m_interfaces->configureForTestWithURL(testURL, generatePixels); +} + +WebTestRunner* WebTestInterfaces::testRunner() +{ + return m_interfaces->testRunner(); +} + +WebThemeEngine* WebTestInterfaces::themeEngine() +{ + return m_interfaces->themeEngine(); +} + +TestInterfaces* WebTestInterfaces::testInterfaces() +{ + return m_interfaces.get(); +} + +WebMediaStreamCenter* WebTestInterfaces::createMediaStreamCenter(WebMediaStreamCenterClient* client) +{ + return new MockWebMediaStreamCenter(client); +} + +WebRTCPeerConnectionHandler* WebTestInterfaces::createWebRTCPeerConnectionHandler(WebRTCPeerConnectionHandlerClient* client) +{ + return new MockWebRTCPeerConnectionHandler(client, m_interfaces.get()); +} + +WebMIDIAccessor* WebTestInterfaces::createMIDIAccessor(WebMIDIAccessorClient* client) +{ + return new MockWebMIDIAccessor(client, m_interfaces.get()); +} + +WebAudioDevice* WebTestInterfaces::createAudioDevice(double sampleRate) +{ + return new MockWebAudioDevice(sampleRate); +} + +} diff --git a/content/shell/renderer/test_runner/WebTestInterfaces.h b/content/shell/renderer/test_runner/WebTestInterfaces.h new file mode 100644 index 0000000..cd59fd0 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestInterfaces.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTestInterfaces_h +#define WebTestInterfaces_h + +#include "content/shell/renderer/test_runner/WebScopedPtr.h" + +namespace blink { +class WebAudioDevice; +class WebFrame; +class WebMediaStreamCenter; +class WebMediaStreamCenterClient; +class WebMIDIAccessor; +class WebMIDIAccessorClient; +class WebRTCPeerConnectionHandler; +class WebRTCPeerConnectionHandlerClient; +class WebThemeEngine; +class WebURL; +class WebView; +} + +namespace WebTestRunner { + +class TestInterfaces; +class WebTestDelegate; +class WebTestProxyBase; +class WebTestRunner; + +class WebTestInterfaces { +public: + WebTestInterfaces(); + ~WebTestInterfaces(); + + void setWebView(blink::WebView*, WebTestProxyBase*); + void setDelegate(WebTestDelegate*); + void bindTo(blink::WebFrame*); + void resetAll(); + void setTestIsRunning(bool); + void configureForTestWithURL(const blink::WebURL&, bool generatePixels); + + WebTestRunner* testRunner(); + blink::WebThemeEngine* themeEngine(); + + blink::WebMediaStreamCenter* createMediaStreamCenter(blink::WebMediaStreamCenterClient*); + blink::WebRTCPeerConnectionHandler* createWebRTCPeerConnectionHandler(blink::WebRTCPeerConnectionHandlerClient*); + + blink::WebMIDIAccessor* createMIDIAccessor(blink::WebMIDIAccessorClient*); + + blink::WebAudioDevice* createAudioDevice(double sampleRate); + + TestInterfaces* testInterfaces(); + +private: + WebScopedPtr<TestInterfaces> m_interfaces; +}; + +} + +#endif // WebTestInterfaces_h diff --git a/content/shell/renderer/test_runner/WebTestProxy.cpp b/content/shell/renderer/test_runner/WebTestProxy.cpp new file mode 100644 index 0000000..541ec74 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestProxy.cpp @@ -0,0 +1,1367 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebTestProxy.h" + +#include <cctype> + +#include "content/shell/renderer/test_runner/AccessibilityController.h" +#include "content/shell/renderer/test_runner/EventSender.h" +#include "content/shell/renderer/test_runner/MockColorChooser.h" +#include "content/shell/renderer/test_runner/MockWebSpeechInputController.h" +#include "content/shell/renderer/test_runner/MockWebSpeechRecognizer.h" +#include "content/shell/renderer/test_runner/SpellCheckClient.h" +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/TestInterfaces.h" +#include "content/shell/renderer/test_runner/TestPlugin.h" +#include "content/shell/renderer/test_runner/TestRunner.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/WebTestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" +#include "content/shell/renderer/test_runner/WebUserMediaClientMock.h" +// FIXME: Including platform_canvas.h here is a layering violation. +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/WebURLError.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/platform/WebURLResponse.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" +#include "third_party/WebKit/public/web/WebAXObject.h" +#include "third_party/WebKit/public/web/WebCachedURLRequest.h" +#include "third_party/WebKit/public/web/WebConsoleMessage.h" +#include "third_party/WebKit/public/web/WebDataSource.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebGeolocationClientMock.h" +#include "third_party/WebKit/public/web/WebHistoryItem.h" +#include "third_party/WebKit/public/web/WebMIDIClientMock.h" +#include "third_party/WebKit/public/web/WebNode.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" +#include "third_party/WebKit/public/web/WebPrintParams.h" +#include "third_party/WebKit/public/web/WebRange.h" +#include "third_party/WebKit/public/web/WebScriptController.h" +#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" +#include "third_party/WebKit/public/web/WebView.h" + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +class HostMethodTask : public WebMethodTask<WebTestProxyBase> { +public: + typedef void (WebTestProxyBase::*CallbackMethodType)(); + HostMethodTask(WebTestProxyBase* object, CallbackMethodType callback) + : WebMethodTask<WebTestProxyBase>(object) + , m_callback(callback) + { } + + virtual void runIfValid() { (m_object->*m_callback)(); } + +private: + CallbackMethodType m_callback; +}; + +void printFrameDescription(WebTestDelegate* delegate, WebFrame* frame) +{ + string name8 = frame->uniqueName().utf8(); + if (frame == frame->view()->mainFrame()) { + if (!name8.length()) { + delegate->printMessage("main frame"); + return; + } + delegate->printMessage(string("main frame \"") + name8 + "\""); + return; + } + if (!name8.length()) { + delegate->printMessage("frame (anonymous)"); + return; + } + delegate->printMessage(string("frame \"") + name8 + "\""); +} + +void printFrameUserGestureStatus(WebTestDelegate* delegate, WebFrame* frame, const char* msg) +{ + bool isUserGesture = WebUserGestureIndicator::isProcessingUserGesture(); + delegate->printMessage(string("Frame with user gesture \"") + (isUserGesture ? "true" : "false") + "\"" + msg); +} + +// Used to write a platform neutral file:/// URL by taking the +// filename and its directory. (e.g., converts +// "file:///tmp/foo/bar.txt" to just "bar.txt"). +string descriptionSuitableForTestResult(const string& url) +{ + if (url.empty() || string::npos == url.find("file://")) + return url; + + size_t pos = url.rfind('/'); + if (pos == string::npos || !pos) + return "ERROR:" + url; + pos = url.rfind('/', pos - 1); + if (pos == string::npos) + return "ERROR:" + url; + + return url.substr(pos + 1); +} + +void printResponseDescription(WebTestDelegate* delegate, const WebURLResponse& response) +{ + if (response.isNull()) { + delegate->printMessage("(null)"); + return; + } + string url = response.url().spec(); + char data[100]; + snprintf(data, sizeof(data), "%d", response. httpStatusCode()); + delegate->printMessage(string("<NSURLResponse ") + descriptionSuitableForTestResult(url) + ", http status code " + data + ">"); +} + +string URLDescription(const GURL& url) +{ + if (url.SchemeIs("file")) + return url.ExtractFileName(); + return url.possibly_invalid_spec(); +} + +string PriorityDescription(const WebURLRequest::Priority& priority) +{ + switch (priority) { + case WebURLRequest::PriorityVeryLow: + return "VeryLow"; + case WebURLRequest::PriorityLow: + return "Low"; + case WebURLRequest::PriorityMedium: + return "Medium"; + case WebURLRequest::PriorityHigh: + return "High"; + case WebURLRequest::PriorityVeryHigh: + return "VeryHigh"; + case WebURLRequest::PriorityUnresolved: + default: + return "Unresolved"; + } +} + +void blockRequest(WebURLRequest& request) +{ + request.setURL(GURL("255.255.255.255")); +} + +bool isLocalhost(const string& host) +{ + return host == "127.0.0.1" || host == "localhost"; +} + +bool hostIsUsedBySomeTestsToGenerateError(const string& host) +{ + return host == "255.255.255.255"; +} + +// Used to write a platform neutral file:/// URL by only taking the filename +// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt"). +string urlSuitableForTestResult(const string& url) +{ + if (url.empty() || string::npos == url.find("file://")) + return url; + + size_t pos = url.rfind('/'); + if (pos == string::npos) { +#ifdef WIN32 + pos = url.rfind('\\'); + if (pos == string::npos) + pos = 0; +#else + pos = 0; +#endif + } + string filename = url.substr(pos + 1); + if (filename.empty()) + return "file:"; // A WebKit test has this in its expected output. + return filename; +} + +// WebNavigationType debugging strings taken from PolicyDelegate.mm. +const char* linkClickedString = "link clicked"; +const char* formSubmittedString = "form submitted"; +const char* backForwardString = "back/forward"; +const char* reloadString = "reload"; +const char* formResubmittedString = "form resubmitted"; +const char* otherString = "other"; +const char* illegalString = "illegal value"; + +// Get a debugging string from a WebNavigationType. +const char* webNavigationTypeToString(WebNavigationType type) +{ + switch (type) { + case blink::WebNavigationTypeLinkClicked: + return linkClickedString; + case blink::WebNavigationTypeFormSubmitted: + return formSubmittedString; + case blink::WebNavigationTypeBackForward: + return backForwardString; + case blink::WebNavigationTypeReload: + return reloadString; + case blink::WebNavigationTypeFormResubmitted: + return formResubmittedString; + case blink::WebNavigationTypeOther: + return otherString; + } + return illegalString; +} + +string dumpDocumentText(WebFrame* frame) +{ + // We use the document element's text instead of the body text here because + // not all documents have a body, such as XML documents. + WebElement documentElement = frame->document().documentElement(); + if (documentElement.isNull()) + return string(); + return documentElement.innerText().utf8(); +} + +string dumpFramesAsText(WebFrame* frame, bool recursive) +{ + string result; + + // Add header for all but the main frame. Skip empty frames. + if (frame->parent() && !frame->document().documentElement().isNull()) { + result.append("\n--------\nFrame: '"); + result.append(frame->uniqueName().utf8().data()); + result.append("'\n--------\n"); + } + + result.append(dumpDocumentText(frame)); + result.append("\n"); + + if (recursive) { + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + result.append(dumpFramesAsText(child, recursive)); + } + + return result; +} + +string dumpFramesAsPrintedText(WebFrame* frame, bool recursive) +{ + string result; + + // Cannot do printed format for anything other than HTML + if (!frame->document().isHTMLDocument()) + return string(); + + // Add header for all but the main frame. Skip empty frames. + if (frame->parent() && !frame->document().documentElement().isNull()) { + result.append("\n--------\nFrame: '"); + result.append(frame->uniqueName().utf8().data()); + result.append("'\n--------\n"); + } + + result.append(frame->renderTreeAsText(WebFrame::RenderAsTextPrinting).utf8()); + result.append("\n"); + + if (recursive) { + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + result.append(dumpFramesAsPrintedText(child, recursive)); + } + + return result; +} + +string dumpFrameScrollPosition(WebFrame* frame, bool recursive) +{ + string result; + WebSize offset = frame->scrollOffset(); + if (offset.width > 0 || offset.height > 0) { + if (frame->parent()) + result = string("frame '") + frame->uniqueName().utf8().data() + "' "; + char data[100]; + snprintf(data, sizeof(data), "scrolled to %d,%d\n", offset.width, offset.height); + result += data; + } + + if (!recursive) + return result; + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + result += dumpFrameScrollPosition(child, recursive); + return result; +} + +struct ToLower { + char16 operator()(char16 c) { return tolower(c); } +}; + +// Returns True if item1 < item2. +bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2) +{ + string16 target1 = item1.target(); + string16 target2 = item2.target(); + std::transform(target1.begin(), target1.end(), target1.begin(), ToLower()); + std::transform(target2.begin(), target2.end(), target2.begin(), ToLower()); + return target1 < target2; +} + +string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent) +{ + string result; + + if (isCurrent) { + result.append("curr->"); + result.append(indent - 6, ' '); // 6 == "curr->".length() + } else + result.append(indent, ' '); + + string url = normalizeLayoutTestURL(item.urlString().utf8()); + result.append(url); + if (!item.target().isEmpty()) { + result.append(" (in frame \""); + result.append(item.target().utf8()); + result.append("\")"); + } + result.append("\n"); + + const WebVector<WebHistoryItem>& children = item.children(); + if (!children.isEmpty()) { + // Must sort to eliminate arbitrary result ordering which defeats + // reproducible testing. + // FIXME: WebVector should probably just be a std::vector!! + std::vector<WebHistoryItem> sortedChildren; + for (size_t i = 0; i < children.size(); ++i) + sortedChildren.push_back(children[i]); + std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess); + for (size_t i = 0; i < sortedChildren.size(); ++i) + result += dumpHistoryItem(sortedChildren[i], indent + 4, false); + } + + return result; +} + +void dumpBackForwardList(const WebVector<WebHistoryItem>& history, size_t currentEntryIndex, string& result) +{ + result.append("\n============== Back Forward List ==============\n"); + for (size_t index = 0; index < history.size(); ++index) + result.append(dumpHistoryItem(history[index], 8, index == currentEntryIndex)); + result.append("===============================================\n"); +} + +string dumpAllBackForwardLists(TestInterfaces* interfaces, WebTestDelegate* delegate) +{ + string result; + const vector<WebTestProxyBase*>& windowList = interfaces->windowList(); + for (unsigned i = 0; i < windowList.size(); ++i) { + size_t currentEntryIndex = 0; + WebVector<WebHistoryItem> history; + delegate->captureHistoryForWindow(windowList.at(i), &history, ¤tEntryIndex); + dumpBackForwardList(history, currentEntryIndex, result); + } + return result; +} + +} + +WebTestProxyBase::WebTestProxyBase() + : m_testInterfaces(0) + , m_delegate(0) + , m_webWidget(0) + , m_spellcheck(new SpellCheckClient(this)) + , m_chooserCount(0) +{ + reset(); +} + +WebTestProxyBase::~WebTestProxyBase() +{ + m_testInterfaces->windowClosed(this); +} + +void WebTestProxyBase::setInterfaces(WebTestInterfaces* interfaces) +{ + m_testInterfaces = interfaces->testInterfaces(); + m_testInterfaces->windowOpened(this); +} + +void WebTestProxyBase::setDelegate(WebTestDelegate* delegate) +{ + m_delegate = delegate; + m_spellcheck->setDelegate(delegate); +#if ENABLE_INPUT_SPEECH + if (m_speechInputController.get()) + m_speechInputController->setDelegate(delegate); +#endif + if (m_speechRecognizer.get()) + m_speechRecognizer->setDelegate(delegate); +} + +void WebTestProxyBase::setWidget(WebWidget* widget) +{ + m_webWidget = widget; +} + +WebWidget* WebTestProxyBase::webWidget() +{ + return m_webWidget; +} + +WebView* WebTestProxyBase::webView() +{ + BLINK_ASSERT(m_webWidget); + // TestRunner does not support popup widgets. So m_webWidget is always a WebView. + return static_cast<WebView*>(m_webWidget); +} + +void WebTestProxyBase::didForceResize() +{ + invalidateAll(); + discardBackingStore(); +} + +void WebTestProxyBase::reset() +{ + m_paintRect = WebRect(); + m_canvas.reset(); + m_isPainting = false; + m_animateScheduled = false; + m_resourceIdentifierMap.clear(); + m_logConsoleOutput = true; + if (m_geolocationClient.get()) + m_geolocationClient->resetMock(); + if (m_midiClient.get()) + m_midiClient->resetMock(); +#if ENABLE_INPUT_SPEECH + if (m_speechInputController.get()) + m_speechInputController->clearResults(); +#endif +} + +WebSpellCheckClient* WebTestProxyBase::spellCheckClient() const +{ + return m_spellcheck.get(); +} + +WebColorChooser* WebTestProxyBase::createColorChooser(WebColorChooserClient* client, const blink::WebColor& color) +{ + // This instance is deleted by WebCore::ColorInputType + return new MockColorChooser(client, m_delegate, this); +} + +WebColorChooser* WebTestProxyBase::createColorChooser(WebColorChooserClient* client, const blink::WebColor& color, const blink::WebVector<blink::WebColorSuggestion>& suggestions) +{ + // This instance is deleted by WebCore::ColorInputType + return new MockColorChooser(client, m_delegate, this); +} + +bool WebTestProxyBase::runFileChooser(const blink::WebFileChooserParams&, blink::WebFileChooserCompletion*) +{ + m_delegate->printMessage("Mock: Opening a file chooser.\n"); + // FIXME: Add ability to set file names to a file upload control. + return false; +} + +void WebTestProxyBase::showValidationMessage(const WebRect&, const WebString& message, const WebString& subMessage, WebTextDirection) +{ + m_delegate->printMessage(std::string("ValidationMessageClient: main-message=") + std::string(message.utf8()) + " sub-message=" + std::string(subMessage.utf8()) + "\n"); +} + +void WebTestProxyBase::hideValidationMessage() +{ +} + +void WebTestProxyBase::moveValidationMessage(const WebRect&) +{ +} + +string WebTestProxyBase::captureTree(bool debugRenderTree) +{ + WebScriptController::flushConsoleMessages(); + + bool shouldDumpAsText = m_testInterfaces->testRunner()->shouldDumpAsText(); + bool shouldDumpAsMarkup = m_testInterfaces->testRunner()->shouldDumpAsMarkup(); + bool shouldDumpAsPrinted = m_testInterfaces->testRunner()->isPrinting(); + WebFrame* frame = webView()->mainFrame(); + string dataUtf8; + if (shouldDumpAsText) { + bool recursive = m_testInterfaces->testRunner()->shouldDumpChildFramesAsText(); + dataUtf8 = shouldDumpAsPrinted ? dumpFramesAsPrintedText(frame, recursive) : dumpFramesAsText(frame, recursive); + } else if (shouldDumpAsMarkup) { + // Append a newline for the test driver. + dataUtf8 = frame->contentAsMarkup().utf8() + "\n"; + } else { + bool recursive = m_testInterfaces->testRunner()->shouldDumpChildFrameScrollPositions(); + WebFrame::RenderAsTextControls renderTextBehavior = WebFrame::RenderAsTextNormal; + if (shouldDumpAsPrinted) + renderTextBehavior |= WebFrame::RenderAsTextPrinting; + if (debugRenderTree) + renderTextBehavior |= WebFrame::RenderAsTextDebug; + dataUtf8 = frame->renderTreeAsText(renderTextBehavior).utf8(); + dataUtf8 += dumpFrameScrollPosition(frame, recursive); + } + + if (m_testInterfaces->testRunner()->shouldDumpBackForwardList()) + dataUtf8 += dumpAllBackForwardLists(m_testInterfaces, m_delegate); + + return dataUtf8; +} + +SkCanvas* WebTestProxyBase::capturePixels() +{ + webWidget()->layout(); + if (m_testInterfaces->testRunner()->testRepaint()) { + WebSize viewSize = webWidget()->size(); + int width = viewSize.width; + int height = viewSize.height; + if (m_testInterfaces->testRunner()->sweepHorizontally()) { + for (WebRect column(0, 0, 1, height); column.x < width; column.x++) + paintRect(column); + } else { + for (WebRect line(0, 0, width, 1); line.y < height; line.y++) + paintRect(line); + } + } else if (m_testInterfaces->testRunner()->isPrinting()) + paintPagesWithBoundaries(); + else + paintInvalidatedRegion(); + + // See if we need to draw the selection bounds rect. Selection bounds + // rect is the rect enclosing the (possibly transformed) selection. + // The rect should be drawn after everything is laid out and painted. + if (m_testInterfaces->testRunner()->shouldDumpSelectionRect()) { + // If there is a selection rect - draw a red 1px border enclosing rect + WebRect wr = webView()->mainFrame()->selectionBoundsRect(); + if (!wr.isEmpty()) { + // Render a red rectangle bounding selection rect + SkPaint paint; + paint.setColor(0xFFFF0000); // Fully opaque red + paint.setStyle(SkPaint::kStroke_Style); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setStrokeWidth(1.0f); + SkIRect rect; // Bounding rect + rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height); + canvas()->drawIRect(rect, paint); + } + } + + return canvas(); +} + +void WebTestProxyBase::setLogConsoleOutput(bool enabled) +{ + m_logConsoleOutput = enabled; +} + +void WebTestProxyBase::paintRect(const WebRect& rect) +{ + BLINK_ASSERT(!m_isPainting); + BLINK_ASSERT(canvas()); + m_isPainting = true; + float deviceScaleFactor = webView()->deviceScaleFactor(); + int scaledX = static_cast<int>(static_cast<float>(rect.x) * deviceScaleFactor); + int scaledY = static_cast<int>(static_cast<float>(rect.y) * deviceScaleFactor); + int scaledWidth = static_cast<int>(ceil(static_cast<float>(rect.width) * deviceScaleFactor)); + int scaledHeight = static_cast<int>(ceil(static_cast<float>(rect.height) * deviceScaleFactor)); + WebRect deviceRect(scaledX, scaledY, scaledWidth, scaledHeight); + webWidget()->paint(canvas(), deviceRect); + m_isPainting = false; +} + +void WebTestProxyBase::paintInvalidatedRegion() +{ + webWidget()->animate(0.0); + webWidget()->layout(); + WebSize widgetSize = webWidget()->size(); + WebRect clientRect(0, 0, widgetSize.width, widgetSize.height); + + // Paint the canvas if necessary. Allow painting to generate extra rects + // for the first two calls. This is necessary because some WebCore rendering + // objects update their layout only when painted. + // Store the total area painted in total_paint. Then tell the gdk window + // to update that area after we're done painting it. + for (int i = 0; i < 3; ++i) { + // rect = intersect(m_paintRect , clientRect) + WebRect damageRect = m_paintRect; + int left = max(damageRect.x, clientRect.x); + int top = max(damageRect.y, clientRect.y); + int right = min(damageRect.x + damageRect.width, clientRect.x + clientRect.width); + int bottom = min(damageRect.y + damageRect.height, clientRect.y + clientRect.height); + WebRect rect; + if (left < right && top < bottom) + rect = WebRect(left, top, right - left, bottom - top); + + m_paintRect = WebRect(); + if (rect.isEmpty()) + continue; + paintRect(rect); + } + BLINK_ASSERT(m_paintRect.isEmpty()); +} + +void WebTestProxyBase::paintPagesWithBoundaries() +{ + BLINK_ASSERT(!m_isPainting); + BLINK_ASSERT(canvas()); + m_isPainting = true; + + WebSize pageSizeInPixels = webWidget()->size(); + WebFrame* webFrame = webView()->mainFrame(); + + int pageCount = webFrame->printBegin(pageSizeInPixels); + int totalHeight = pageCount * (pageSizeInPixels.height + 1) - 1; + + SkCanvas* testCanvas = skia::TryCreateBitmapCanvas(pageSizeInPixels.width, totalHeight, true); + if (testCanvas) { + discardBackingStore(); + m_canvas.reset(testCanvas); + } else { + webFrame->printEnd(); + return; + } + + webFrame->printPagesWithBoundaries(canvas(), pageSizeInPixels); + webFrame->printEnd(); + + m_isPainting = false; +} + +SkCanvas* WebTestProxyBase::canvas() +{ + if (m_canvas.get()) + return m_canvas.get(); + WebSize widgetSize = webWidget()->size(); + float deviceScaleFactor = webView()->deviceScaleFactor(); + int scaledWidth = static_cast<int>(ceil(static_cast<float>(widgetSize.width) * deviceScaleFactor)); + int scaledHeight = static_cast<int>(ceil(static_cast<float>(widgetSize.height) * deviceScaleFactor)); + m_canvas.reset(skia::CreateBitmapCanvas(scaledWidth, scaledHeight, true)); + return m_canvas.get(); +} + +// Paints the entire canvas a semi-transparent black (grayish). This is used +// by the layout tests in fast/repaint. The alpha value matches upstream. +void WebTestProxyBase::displayRepaintMask() +{ + canvas()->drawARGB(167, 0, 0, 0); +} + +void WebTestProxyBase::display() +{ + const blink::WebSize& size = webWidget()->size(); + WebRect rect(0, 0, size.width, size.height); + m_paintRect = rect; + paintInvalidatedRegion(); + displayRepaintMask(); +} + +void WebTestProxyBase::displayInvalidatedRegion() +{ + paintInvalidatedRegion(); + displayRepaintMask(); +} + +void WebTestProxyBase::discardBackingStore() +{ + m_canvas.reset(); +} + +WebGeolocationClientMock* WebTestProxyBase::geolocationClientMock() +{ + if (!m_geolocationClient.get()) + m_geolocationClient.reset(WebGeolocationClientMock::create()); + return m_geolocationClient.get(); +} + +WebMIDIClientMock* WebTestProxyBase::midiClientMock() +{ + if (!m_midiClient.get()) + m_midiClient.reset(new WebMIDIClientMock); + return m_midiClient.get(); +} + +#if ENABLE_INPUT_SPEECH +MockWebSpeechInputController* WebTestProxyBase::speechInputControllerMock() +{ + BLINK_ASSERT(m_speechInputController.get()); + return m_speechInputController.get(); +} +#endif + +MockWebSpeechRecognizer* WebTestProxyBase::speechRecognizerMock() +{ + if (!m_speechRecognizer.get()) { + m_speechRecognizer.reset(new MockWebSpeechRecognizer()); + m_speechRecognizer->setDelegate(m_delegate); + } + return m_speechRecognizer.get(); +} + +void WebTestProxyBase::didInvalidateRect(const WebRect& rect) +{ + // m_paintRect = m_paintRect U rect + if (rect.isEmpty()) + return; + if (m_paintRect.isEmpty()) { + m_paintRect = rect; + return; + } + int left = min(m_paintRect.x, rect.x); + int top = min(m_paintRect.y, rect.y); + int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width); + int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height); + m_paintRect = WebRect(left, top, right - left, bottom - top); +} + +void WebTestProxyBase::didScrollRect(int, int, const WebRect& clipRect) +{ + didInvalidateRect(clipRect); +} + +void WebTestProxyBase::invalidateAll() +{ + m_paintRect = WebRect(0, 0, INT_MAX, INT_MAX); +} + +void WebTestProxyBase::scheduleComposite() +{ + invalidateAll(); +} + +void WebTestProxyBase::scheduleAnimation() +{ + if (!m_testInterfaces->testRunner()->testIsRunning()) + return; + + if (!m_animateScheduled) { + m_animateScheduled = true; + m_delegate->postDelayedTask(new HostMethodTask(this, &WebTestProxyBase::animateNow), 1); + } +} + +void WebTestProxyBase::animateNow() +{ + if (m_animateScheduled) { + m_animateScheduled = false; + webWidget()->animate(0.0); + } +} + +void WebTestProxyBase::show(WebNavigationPolicy) +{ + invalidateAll(); +} + +void WebTestProxyBase::setWindowRect(const WebRect& rect) +{ + invalidateAll(); + discardBackingStore(); +} + +void WebTestProxyBase::didAutoResize(const WebSize&) +{ + invalidateAll(); +} + +void WebTestProxyBase::postAccessibilityEvent(const blink::WebAXObject& obj, blink::WebAXEvent event) +{ + if (event == blink::WebAXEventFocus) + m_testInterfaces->accessibilityController()->setFocusedElement(obj); + + const char* eventName = 0; + switch (event) { + case blink::WebAXEventActiveDescendantChanged: + eventName = "ActiveDescendantChanged"; + break; + case blink::WebAXEventAlert: + eventName = "Alert"; + break; + case blink::WebAXEventAriaAttributeChanged: + eventName = "AriaAttributeChanged"; + break; + case blink::WebAXEventAutocorrectionOccured: + eventName = "AutocorrectionOccured"; + break; + case blink::WebAXEventBlur: + eventName = "Blur"; + break; + case blink::WebAXEventCheckedStateChanged: + eventName = "CheckedStateChanged"; + break; + case blink::WebAXEventChildrenChanged: + eventName = "ChildrenChanged"; + break; + case blink::WebAXEventFocus: + eventName = "Focus"; + break; + case blink::WebAXEventHide: + eventName = "Hide"; + break; + case blink::WebAXEventInvalidStatusChanged: + eventName = "InvalidStatusChanged"; + break; + case blink::WebAXEventLayoutComplete: + eventName = "LayoutComplete"; + break; + case blink::WebAXEventLiveRegionChanged: + eventName = "LiveRegionChanged"; + break; + case blink::WebAXEventLoadComplete: + eventName = "LoadComplete"; + break; + case blink::WebAXEventLocationChanged: + eventName = "LocationChanged"; + break; + case blink::WebAXEventMenuListItemSelected: + eventName = "MenuListItemSelected"; + break; + case blink::WebAXEventMenuListValueChanged: + eventName = "MenuListValueChanged"; + break; + case blink::WebAXEventRowCollapsed: + eventName = "RowCollapsed"; + break; + case blink::WebAXEventRowCountChanged: + eventName = "RowCountChanged"; + break; + case blink::WebAXEventRowExpanded: + eventName = "RowExpanded"; + break; + case blink::WebAXEventScrolledToAnchor: + eventName = "ScrolledToAnchor"; + break; + case blink::WebAXEventSelectedChildrenChanged: + eventName = "SelectedChildrenChanged"; + break; + case blink::WebAXEventSelectedTextChanged: + eventName = "SelectedTextChanged"; + break; + case blink::WebAXEventShow: + eventName = "Show"; + break; + case blink::WebAXEventTextChanged: + eventName = "TextChanged"; + break; + case blink::WebAXEventTextInserted: + eventName = "TextInserted"; + break; + case blink::WebAXEventTextRemoved: + eventName = "TextRemoved"; + break; + case blink::WebAXEventValueChanged: + eventName = "ValueChanged"; + break; + } + + m_testInterfaces->accessibilityController()->notificationReceived(obj, eventName); + + if (m_testInterfaces->accessibilityController()->shouldLogAccessibilityEvents()) { + string message("AccessibilityNotification - "); + message += eventName; + + blink::WebNode node = obj.node(); + if (!node.isNull() && node.isElementNode()) { + blink::WebElement element = node.to<blink::WebElement>(); + if (element.hasAttribute("id")) { + message += " - id:"; + message += element.getAttribute("id").utf8().data(); + } + } + + m_delegate->printMessage(message + "\n"); + } +} + +void WebTestProxyBase::startDragging(WebFrame*, const WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&) +{ + // When running a test, we need to fake a drag drop operation otherwise + // Windows waits for real mouse events to know when the drag is over. + m_testInterfaces->eventSender()->doDragDrop(data, mask); +} + +// The output from these methods in layout test mode should match that +// expected by the layout tests. See EditingDelegate.m in DumpRenderTree. + +void WebTestProxyBase::didChangeSelection(bool isEmptySelection) +{ + if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) + m_delegate->printMessage("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n"); +} + +void WebTestProxyBase::didChangeContents() +{ + if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) + m_delegate->printMessage("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n"); +} + +bool WebTestProxyBase::createView(WebFrame*, const WebURLRequest& request, const WebWindowFeatures&, const WebString&, WebNavigationPolicy, bool) +{ + if (!m_testInterfaces->testRunner()->canOpenWindows()) + return false; + if (m_testInterfaces->testRunner()->shouldDumpCreateView()) + m_delegate->printMessage(string("createView(") + URLDescription(request.url()) + ")\n"); + return true; +} + +WebPlugin* WebTestProxyBase::createPlugin(WebFrame* frame, const WebPluginParams& params) +{ + if (params.mimeType == TestPlugin::mimeType()) + return TestPlugin::create(frame, params, m_delegate); + return 0; +} + +void WebTestProxyBase::setStatusText(const WebString& text) +{ + if (!m_testInterfaces->testRunner()->shouldDumpStatusCallbacks()) + return; + m_delegate->printMessage(string("UI DELEGATE STATUS CALLBACK: setStatusText:") + text.utf8().data() + "\n"); +} + +void WebTestProxyBase::didStopLoading() +{ + if (m_testInterfaces->testRunner()->shouldDumpProgressFinishedCallback()) + m_delegate->printMessage("postProgressFinishedNotification\n"); +} + +void WebTestProxyBase::showContextMenu(WebFrame*, const WebContextMenuData& contextMenuData) +{ + m_testInterfaces->eventSender()->setContextMenuData(contextMenuData); +} + +WebUserMediaClient* WebTestProxyBase::userMediaClient() +{ + if (!m_userMediaClient.get()) + m_userMediaClient.reset(new WebUserMediaClientMock(m_delegate)); + return m_userMediaClient.get(); +} + +// Simulate a print by going into print mode and then exit straight away. +void WebTestProxyBase::printPage(WebFrame* frame) +{ + WebSize pageSizeInPixels = webWidget()->size(); + if (pageSizeInPixels.isEmpty()) + return; + WebPrintParams printParams(pageSizeInPixels); + frame->printBegin(printParams); + frame->printEnd(); +} + +WebNotificationPresenter* WebTestProxyBase::notificationPresenter() +{ + return m_testInterfaces->testRunner()->notificationPresenter(); +} + +WebGeolocationClient* WebTestProxyBase::geolocationClient() +{ + return geolocationClientMock(); +} + +WebMIDIClient* WebTestProxyBase::webMIDIClient() +{ + return midiClientMock(); +} + +WebSpeechInputController* WebTestProxyBase::speechInputController(WebSpeechInputListener* listener) +{ +#if ENABLE_INPUT_SPEECH + if (!m_speechInputController.get()) { + m_speechInputController.reset(new MockWebSpeechInputController(listener)); + m_speechInputController->setDelegate(m_delegate); + } + return m_speechInputController.get(); +#else + BLINK_ASSERT(listener); + return 0; +#endif +} + +WebSpeechRecognizer* WebTestProxyBase::speechRecognizer() +{ + return speechRecognizerMock(); +} + +bool WebTestProxyBase::requestPointerLock() +{ + return m_testInterfaces->testRunner()->requestPointerLock(); +} + +void WebTestProxyBase::requestPointerUnlock() +{ + m_testInterfaces->testRunner()->requestPointerUnlock(); +} + +bool WebTestProxyBase::isPointerLocked() +{ + return m_testInterfaces->testRunner()->isPointerLocked(); +} + +void WebTestProxyBase::didFocus() +{ + m_delegate->setFocus(this, true); +} + +void WebTestProxyBase::didBlur() +{ + m_delegate->setFocus(this, false); +} + +void WebTestProxyBase::setToolTipText(const WebString& text, WebTextDirection) +{ + m_testInterfaces->testRunner()->setToolTipText(text); +} + +void WebTestProxyBase::didOpenChooser() +{ + m_chooserCount++; +} + +void WebTestProxyBase::didCloseChooser() +{ + m_chooserCount--; +} + +bool WebTestProxyBase::isChooserShown() +{ + return 0 < m_chooserCount; +} + +void WebTestProxyBase::didStartProvisionalLoad(WebFrame* frame) +{ + if (!m_testInterfaces->testRunner()->topLoadingFrame()) + m_testInterfaces->testRunner()->setTopLoadingFrame(frame, false); + + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didStartProvisionalLoadForFrame\n"); + } + + if (m_testInterfaces->testRunner()->shouldDumpUserGestureInFrameLoadCallbacks()) + printFrameUserGestureStatus(m_delegate, frame, " - in didStartProvisionalLoadForFrame\n"); +} + +void WebTestProxyBase::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didReceiveServerRedirectForProvisionalLoadForFrame\n"); + } +} + +bool WebTestProxyBase::didFailProvisionalLoad(WebFrame* frame, const WebURLError&) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didFailProvisionalLoadWithError\n"); + } + locationChangeDone(frame); + return !frame->provisionalDataSource(); +} + +void WebTestProxyBase::didCommitProvisionalLoad(WebFrame* frame, bool) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didCommitLoadForFrame\n"); + } +} + +void WebTestProxyBase::didReceiveTitle(WebFrame* frame, const WebString& title, WebTextDirection direction) +{ + WebCString title8 = title.utf8(); + + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(string(" - didReceiveTitle: ") + title8.data() + "\n"); + } + + if (m_testInterfaces->testRunner()->shouldDumpTitleChanges()) + m_delegate->printMessage(string("TITLE CHANGED: '") + title8.data() + "'\n"); +} + +void WebTestProxyBase::didChangeIcon(WebFrame* frame, WebIconURL::Type) +{ + if (m_testInterfaces->testRunner()->shouldDumpIconChanges()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(string(" - didChangeIcons\n")); + } +} + +void WebTestProxyBase::didFinishDocumentLoad(WebFrame* frame) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didFinishDocumentLoadForFrame\n"); + } else { + unsigned pendingUnloadEvents = frame->unloadListenerCount(); + if (pendingUnloadEvents) { + printFrameDescription(m_delegate, frame); + char buffer[100]; + snprintf(buffer, sizeof(buffer), " - has %u onunload handler(s)\n", pendingUnloadEvents); + m_delegate->printMessage(buffer); + } + } +} + +void WebTestProxyBase::didHandleOnloadEvents(WebFrame* frame) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didHandleOnloadEventsForFrame\n"); + } +} + +void WebTestProxyBase::didFailLoad(WebFrame* frame, const WebURLError&) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didFailLoadWithError\n"); + } + locationChangeDone(frame); +} + +void WebTestProxyBase::didFinishLoad(WebFrame* frame) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(" - didFinishLoadForFrame\n"); + } + locationChangeDone(frame); +} + +void WebTestProxyBase::didDetectXSS(WebFrame*, const WebURL&, bool) +{ + if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) + m_delegate->printMessage("didDetectXSS\n"); +} + +void WebTestProxyBase::didDispatchPingLoader(WebFrame*, const WebURL& url) +{ + if (m_testInterfaces->testRunner()->shouldDumpPingLoaderCallbacks()) + m_delegate->printMessage(string("PingLoader dispatched to '") + URLDescription(url).c_str() + "'.\n"); +} + +void WebTestProxyBase::willRequestResource(WebFrame* frame, const blink::WebCachedURLRequest& request) +{ + if (m_testInterfaces->testRunner()->shouldDumpResourceRequestCallbacks()) { + printFrameDescription(m_delegate, frame); + m_delegate->printMessage(string(" - ") + request.initiatorName().utf8().data()); + m_delegate->printMessage(string(" requested '") + URLDescription(request.urlRequest().url()).c_str() + "'\n"); + } +} + +void WebTestProxyBase::willSendRequest(WebFrame*, unsigned identifier, blink::WebURLRequest& request, const blink::WebURLResponse& redirectResponse) +{ + // Need to use GURL for host() and SchemeIs() + GURL url = request.url(); + string requestURL = url.possibly_invalid_spec(); + + GURL mainDocumentURL = request.firstPartyForCookies(); + + if (redirectResponse.isNull() && (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks() || m_testInterfaces->testRunner()->shouldDumpResourcePriorities())) { + BLINK_ASSERT(m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end()); + m_resourceIdentifierMap[identifier] = descriptionSuitableForTestResult(requestURL); + } + + if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end()) + m_delegate->printMessage("<unknown>"); + else + m_delegate->printMessage(m_resourceIdentifierMap[identifier]); + m_delegate->printMessage(" - willSendRequest <NSURLRequest URL "); + m_delegate->printMessage(descriptionSuitableForTestResult(requestURL).c_str()); + m_delegate->printMessage(", main document URL "); + m_delegate->printMessage(URLDescription(mainDocumentURL).c_str()); + m_delegate->printMessage(", http method "); + m_delegate->printMessage(request.httpMethod().utf8().data()); + m_delegate->printMessage("> redirectResponse "); + printResponseDescription(m_delegate, redirectResponse); + m_delegate->printMessage("\n"); + } + + if (m_testInterfaces->testRunner()->shouldDumpResourcePriorities()) { + m_delegate->printMessage(descriptionSuitableForTestResult(requestURL).c_str()); + m_delegate->printMessage(" has priority "); + m_delegate->printMessage(PriorityDescription(request.priority())); + m_delegate->printMessage("\n"); + } + + if (m_testInterfaces->testRunner()->httpHeadersToClear()) { + const set<string> *clearHeaders = m_testInterfaces->testRunner()->httpHeadersToClear(); + for (set<string>::const_iterator header = clearHeaders->begin(); header != clearHeaders->end(); ++header) + request.clearHTTPHeaderField(WebString::fromUTF8(*header)); + } + + string host = url.host(); + if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https"))) { + if (!isLocalhost(host) && !hostIsUsedBySomeTestsToGenerateError(host) + && ((!mainDocumentURL.SchemeIs("http") && !mainDocumentURL.SchemeIs("https")) || isLocalhost(mainDocumentURL.host())) + && !m_delegate->allowExternalPages()) { + m_delegate->printMessage(string("Blocked access to external URL ") + requestURL + "\n"); + blockRequest(request); + return; + } + } + + // Set the new substituted URL. + request.setURL(m_delegate->rewriteLayoutTestsURL(request.url().spec())); +} + +void WebTestProxyBase::didReceiveResponse(WebFrame*, unsigned identifier, const blink::WebURLResponse& response) +{ + if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end()) + m_delegate->printMessage("<unknown>"); + else + m_delegate->printMessage(m_resourceIdentifierMap[identifier]); + m_delegate->printMessage(" - didReceiveResponse "); + printResponseDescription(m_delegate, response); + m_delegate->printMessage("\n"); + } + if (m_testInterfaces->testRunner()->shouldDumpResourceResponseMIMETypes()) { + GURL url = response.url(); + WebString mimeType = response.mimeType(); + m_delegate->printMessage(url.ExtractFileName()); + m_delegate->printMessage(" has MIME type "); + // Simulate NSURLResponse's mapping of empty/unknown MIME types to application/octet-stream + m_delegate->printMessage(mimeType.isEmpty() ? "application/octet-stream" : mimeType.utf8().data()); + m_delegate->printMessage("\n"); + } +} + +void WebTestProxyBase::didChangeResourcePriority(WebFrame*, unsigned identifier, const blink::WebURLRequest::Priority& priority) +{ + if (m_testInterfaces->testRunner()->shouldDumpResourcePriorities()) { + if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end()) + m_delegate->printMessage("<unknown>"); + else + m_delegate->printMessage(m_resourceIdentifierMap[identifier]); + m_delegate->printMessage(" changed priority to "); + m_delegate->printMessage(PriorityDescription(priority)); + m_delegate->printMessage("\n"); + } +} + +void WebTestProxyBase::didFinishResourceLoad(WebFrame*, unsigned identifier) +{ + if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end()) + m_delegate->printMessage("<unknown>"); + else + m_delegate->printMessage(m_resourceIdentifierMap[identifier]); + m_delegate->printMessage(" - didFinishLoading\n"); + } + m_resourceIdentifierMap.erase(identifier); +} + +void WebTestProxyBase::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine) +{ + // This matches win DumpRenderTree's UIDelegate.cpp. + if (!m_logConsoleOutput) + return; + string level; + switch (message.level) { + case WebConsoleMessage::LevelDebug: + level = "DEBUG"; + break; + case WebConsoleMessage::LevelLog: + level = "MESSAGE"; + break; + case WebConsoleMessage::LevelInfo: + level = "INFO"; + break; + case WebConsoleMessage::LevelWarning: + level = "WARNING"; + break; + case WebConsoleMessage::LevelError: + level = "ERROR"; + break; + } + m_delegate->printMessage(string("CONSOLE ") + level + ": "); + if (sourceLine) { + char buffer[40]; + snprintf(buffer, sizeof(buffer), "line %d: ", sourceLine); + m_delegate->printMessage(buffer); + } + if (!message.text.isEmpty()) { + string newMessage; + newMessage = message.text.utf8(); + size_t fileProtocol = newMessage.find("file://"); + if (fileProtocol != string::npos) { + newMessage = newMessage.substr(0, fileProtocol) + + urlSuitableForTestResult(newMessage.substr(fileProtocol)); + } + m_delegate->printMessage(newMessage); + } + m_delegate->printMessage(string("\n")); +} + +void WebTestProxyBase::runModalAlertDialog(WebFrame*, const WebString& message) +{ + m_delegate->printMessage(string("ALERT: ") + message.utf8().data() + "\n"); +} + +bool WebTestProxyBase::runModalConfirmDialog(WebFrame*, const WebString& message) +{ + m_delegate->printMessage(string("CONFIRM: ") + message.utf8().data() + "\n"); + return true; +} + +bool WebTestProxyBase::runModalPromptDialog(WebFrame* frame, const WebString& message, const WebString& defaultValue, WebString*) +{ + m_delegate->printMessage(string("PROMPT: ") + message.utf8().data() + ", default text: " + defaultValue.utf8().data() + "\n"); + return true; +} + +bool WebTestProxyBase::runModalBeforeUnloadDialog(WebFrame*, const WebString& message) +{ + m_delegate->printMessage(string("CONFIRM NAVIGATION: ") + message.utf8().data() + "\n"); + return !m_testInterfaces->testRunner()->shouldStayOnPageAfterHandlingBeforeUnload(); +} + +void WebTestProxyBase::locationChangeDone(WebFrame* frame) +{ + if (frame != m_testInterfaces->testRunner()->topLoadingFrame()) + return; + m_testInterfaces->testRunner()->setTopLoadingFrame(frame, true); +} + +WebNavigationPolicy WebTestProxyBase::decidePolicyForNavigation(WebFrame*, WebDataSource::ExtraData*, const WebURLRequest& request, WebNavigationType type, WebNavigationPolicy defaultPolicy, bool isRedirect) +{ + WebNavigationPolicy result; + if (!m_testInterfaces->testRunner()->policyDelegateEnabled()) + return defaultPolicy; + + m_delegate->printMessage(string("Policy delegate: attempt to load ") + URLDescription(request.url()) + " with navigation type '" + webNavigationTypeToString(type) + "'\n"); + if (m_testInterfaces->testRunner()->policyDelegateIsPermissive()) + result = blink::WebNavigationPolicyCurrentTab; + else + result = blink::WebNavigationPolicyIgnore; + + if (m_testInterfaces->testRunner()->policyDelegateShouldNotifyDone()) + m_testInterfaces->testRunner()->policyDelegateDone(); + return result; +} + +bool WebTestProxyBase::willCheckAndDispatchMessageEvent(WebFrame*, WebFrame*, WebSecurityOrigin, WebDOMMessageEvent) +{ + if (m_testInterfaces->testRunner()->shouldInterceptPostMessage()) { + m_delegate->printMessage("intercepted postMessage\n"); + return true; + } + + return false; +} + +void WebTestProxyBase::postSpellCheckEvent(const WebString& eventName) +{ + if (m_testInterfaces->testRunner()->shouldDumpSpellCheckCallbacks()) { + m_delegate->printMessage(string("SpellCheckEvent: ") + eventName.utf8().data() + "\n"); + } +} + +void WebTestProxyBase::resetInputMethod() +{ + // If a composition text exists, then we need to let the browser process + // to cancel the input method's ongoing composition session. + if (m_webWidget) + m_webWidget->confirmComposition(); +} + +} diff --git a/content/shell/renderer/test_runner/WebTestProxy.h b/content/shell/renderer/test_runner/WebTestProxy.h new file mode 100644 index 0000000..4f406fbd --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestProxy.h @@ -0,0 +1,539 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTestProxy_h +#define WebTestProxy_h + +#include <map> +#include <string> + +#include "content/shell/renderer/test_runner/WebScopedPtr.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/WebURLError.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" +#include "third_party/WebKit/public/web/WebDOMMessageEvent.h" +#include "third_party/WebKit/public/web/WebDataSource.h" +#include "third_party/WebKit/public/web/WebDragOperation.h" +#include "third_party/WebKit/public/web/WebIconURL.h" +#include "third_party/WebKit/public/web/WebNavigationPolicy.h" +#include "third_party/WebKit/public/web/WebNavigationType.h" +#include "third_party/WebKit/public/web/WebSecurityOrigin.h" +#include "third_party/WebKit/public/web/WebTextAffinity.h" +#include "third_party/WebKit/public/web/WebTextDirection.h" + +namespace blink { +class WebAXObject; +class WebAudioDevice; +class WebCachedURLRequest; +class WebColorChooser; +class WebColorChooserClient; +class WebDataSource; +class WebDragData; +class WebFileChooserCompletion; +class WebFrame; +class WebGeolocationClient; +class WebGeolocationClientMock; +class WebImage; +class WebMIDIAccessor; +class WebMIDIAccessorClient; +class WebMIDIClient; +class WebMIDIClientMock; +class WebNode; +class WebNotificationPresenter; +class WebPlugin; +class WebRange; +class WebSerializedScriptValue; +class WebSpeechInputController; +class WebSpeechInputListener; +class WebSpeechRecognizer; +class WebSpellCheckClient; +class WebString; +class WebURL; +class WebURLResponse; +class WebUserMediaClient; +class WebView; +class WebWidget; +struct WebColorSuggestion; +struct WebConsoleMessage; +struct WebContextMenuData; +struct WebFileChooserParams; +struct WebPluginParams; +struct WebPoint; +struct WebSize; +struct WebWindowFeatures; +typedef unsigned WebColor; +} + +class SkCanvas; + +namespace WebTestRunner { + +class MockWebSpeechInputController; +class MockWebSpeechRecognizer; +class SpellCheckClient; +class TestInterfaces; +class WebTestDelegate; +class WebTestInterfaces; +class WebTestRunner; +class WebUserMediaClientMock; + +class WebTestProxyBase { +public: + void setInterfaces(WebTestInterfaces*); + void setDelegate(WebTestDelegate*); + void setWidget(blink::WebWidget*); + + void reset(); + + blink::WebSpellCheckClient *spellCheckClient() const; + blink::WebColorChooser* createColorChooser(blink::WebColorChooserClient*, const blink::WebColor&); + blink::WebColorChooser* createColorChooser(blink::WebColorChooserClient*, const blink::WebColor&, const blink::WebVector<blink::WebColorSuggestion>& suggestions); + bool runFileChooser(const blink::WebFileChooserParams&, blink::WebFileChooserCompletion*); + void showValidationMessage(const blink::WebRect& anchorInRootView, const blink::WebString& mainText, const blink::WebString& supplementalText, blink::WebTextDirection); + void hideValidationMessage(); + void moveValidationMessage(const blink::WebRect& anchorInRootView); + + std::string captureTree(bool debugRenderTree); + SkCanvas* capturePixels(); + + void setLogConsoleOutput(bool enabled); + + // FIXME: Make this private again. + void scheduleComposite(); + + void didOpenChooser(); + void didCloseChooser(); + bool isChooserShown(); + + void display(); + void displayInvalidatedRegion(); + void discardBackingStore(); + + blink::WebGeolocationClientMock* geolocationClientMock(); + blink::WebMIDIClientMock* midiClientMock(); + MockWebSpeechInputController* speechInputControllerMock(); + MockWebSpeechRecognizer* speechRecognizerMock(); + + WebTaskList* taskList() { return &m_taskList; } + + blink::WebView* webView(); + + void didForceResize(); + + void postSpellCheckEvent(const blink::WebString& eventName); + +protected: + WebTestProxyBase(); + ~WebTestProxyBase(); + + void didInvalidateRect(const blink::WebRect&); + void didScrollRect(int, int, const blink::WebRect&); + void scheduleAnimation(); + // FIXME: Remove once we switch to use didForceResize. + void setWindowRect(const blink::WebRect&); + void show(blink::WebNavigationPolicy); + void didAutoResize(const blink::WebSize&); + void postAccessibilityEvent(const blink::WebAXObject&, blink::WebAXEvent); + void startDragging(blink::WebFrame*, const blink::WebDragData&, blink::WebDragOperationsMask, const blink::WebImage&, const blink::WebPoint&); + void didChangeSelection(bool isEmptySelection); + void didChangeContents(); + void didEndEditing(); + bool createView(blink::WebFrame* creator, const blink::WebURLRequest&, const blink::WebWindowFeatures&, const blink::WebString& frameName, blink::WebNavigationPolicy, bool suppressOpener); + blink::WebPlugin* createPlugin(blink::WebFrame*, const blink::WebPluginParams&); + void setStatusText(const blink::WebString&); + void didStopLoading(); + void showContextMenu(blink::WebFrame*, const blink::WebContextMenuData&); + blink::WebUserMediaClient* userMediaClient(); + void printPage(blink::WebFrame*); + blink::WebNotificationPresenter* notificationPresenter(); + blink::WebGeolocationClient* geolocationClient(); + blink::WebMIDIClient* webMIDIClient(); + blink::WebSpeechInputController* speechInputController(blink::WebSpeechInputListener*); + blink::WebSpeechRecognizer* speechRecognizer(); + bool requestPointerLock(); + void requestPointerUnlock(); + bool isPointerLocked(); + void didFocus(); + void didBlur(); + void setToolTipText(const blink::WebString&, blink::WebTextDirection); + void didAddMessageToConsole(const blink::WebConsoleMessage&, const blink::WebString& sourceName, unsigned sourceLine); + void runModalAlertDialog(blink::WebFrame*, const blink::WebString&); + bool runModalConfirmDialog(blink::WebFrame*, const blink::WebString&); + bool runModalPromptDialog(blink::WebFrame*, const blink::WebString& message, const blink::WebString& defaultValue, blink::WebString* actualValue); + bool runModalBeforeUnloadDialog(blink::WebFrame*, const blink::WebString&); + + void didStartProvisionalLoad(blink::WebFrame*); + void didReceiveServerRedirectForProvisionalLoad(blink::WebFrame*); + bool didFailProvisionalLoad(blink::WebFrame*, const blink::WebURLError&); + void didCommitProvisionalLoad(blink::WebFrame*, bool isNewNavigation); + void didReceiveTitle(blink::WebFrame*, const blink::WebString& title, blink::WebTextDirection); + void didChangeIcon(blink::WebFrame*, blink::WebIconURL::Type); + void didFinishDocumentLoad(blink::WebFrame*); + void didHandleOnloadEvents(blink::WebFrame*); + void didFailLoad(blink::WebFrame*, const blink::WebURLError&); + void didFinishLoad(blink::WebFrame*); + void didChangeLocationWithinPage(blink::WebFrame*); + void didDetectXSS(blink::WebFrame*, const blink::WebURL& insecureURL, bool didBlockEntirePage); + void didDispatchPingLoader(blink::WebFrame*, const blink::WebURL&); + void willRequestResource(blink::WebFrame*, const blink::WebCachedURLRequest&); + void willSendRequest(blink::WebFrame*, unsigned identifier, blink::WebURLRequest&, const blink::WebURLResponse& redirectResponse); + void didReceiveResponse(blink::WebFrame*, unsigned identifier, const blink::WebURLResponse&); + void didChangeResourcePriority(blink::WebFrame*, unsigned identifier, const blink::WebURLRequest::Priority&); + void didFinishResourceLoad(blink::WebFrame*, unsigned identifier); + blink::WebNavigationPolicy decidePolicyForNavigation(blink::WebFrame*, blink::WebDataSource::ExtraData*, const blink::WebURLRequest&, blink::WebNavigationType, blink::WebNavigationPolicy defaultPolicy, bool isRedirect); + bool willCheckAndDispatchMessageEvent(blink::WebFrame* sourceFrame, blink::WebFrame* targetFrame, blink::WebSecurityOrigin target, blink::WebDOMMessageEvent); + void resetInputMethod(); + +private: + template<class, typename, typename> friend class WebFrameTestProxy; + void locationChangeDone(blink::WebFrame*); + void paintRect(const blink::WebRect&); + void paintInvalidatedRegion(); + void paintPagesWithBoundaries(); + SkCanvas* canvas(); + void displayRepaintMask(); + void invalidateAll(); + void animateNow(); + + blink::WebWidget* webWidget(); + + TestInterfaces* m_testInterfaces; + WebTestDelegate* m_delegate; + blink::WebWidget* m_webWidget; + + WebTaskList m_taskList; + + WebScopedPtr<SpellCheckClient> m_spellcheck; + WebScopedPtr<WebUserMediaClientMock> m_userMediaClient; + + // Painting. + WebScopedPtr<SkCanvas> m_canvas; + blink::WebRect m_paintRect; + bool m_isPainting; + bool m_animateScheduled; + std::map<unsigned, std::string> m_resourceIdentifierMap; + std::map<unsigned, blink::WebURLRequest> m_requestMap; + + bool m_logConsoleOutput; + int m_chooserCount; + + WebScopedPtr<blink::WebGeolocationClientMock> m_geolocationClient; + WebScopedPtr<blink::WebMIDIClientMock> m_midiClient; + WebScopedPtr<MockWebSpeechRecognizer> m_speechRecognizer; + WebScopedPtr<MockWebSpeechInputController> m_speechInputController; + + // FIXME:: We want to move away from this pattern and mark classes + // as Noncopyable, but this class is marked as WEBTESTRUNNER_EXPORT + // while WebNonCopyable is not, so we cannot inherit from WebNonCopyable. + // To overcome the problem, for now not inheriting from WebNonCopyable + // but plan to fix it when we make the change of making WebNonCopyable + // a macro rather than class. We will have a single way to mark all classes + // as Noncopyable. + // Tracked under: http://code.google.com/p/chromium/issues/detail?id=229178 +private: + WebTestProxyBase(WebTestProxyBase&); + WebTestProxyBase& operator=(const WebTestProxyBase&); +}; + +// Use this template to inject methods into your WebViewClient/WebFrameClient +// implementation required for the running layout tests. +template<class Base, typename T> +class WebTestProxy : public Base, public WebTestProxyBase, public blink::WebNonCopyable { +public: + explicit WebTestProxy(T t) + : Base(t) + { + } + + virtual ~WebTestProxy() { } + + // WebViewClient implementation. + virtual void didInvalidateRect(const blink::WebRect& rect) + { + WebTestProxyBase::didInvalidateRect(rect); + } + virtual void didScrollRect(int dx, int dy, const blink::WebRect& clipRect) + { + WebTestProxyBase::didScrollRect(dx, dy, clipRect); + } + virtual void scheduleComposite() + { + WebTestProxyBase::scheduleComposite(); + } + virtual void scheduleAnimation() + { + WebTestProxyBase::scheduleAnimation(); + } + virtual void setWindowRect(const blink::WebRect& rect) + { + WebTestProxyBase::setWindowRect(rect); + Base::setWindowRect(rect); + } + virtual void show(blink::WebNavigationPolicy policy) + { + WebTestProxyBase::show(policy); + Base::show(policy); + } + virtual void didAutoResize(const blink::WebSize& newSize) + { + WebTestProxyBase::didAutoResize(newSize); + Base::didAutoResize(newSize); + } + virtual void postAccessibilityEvent(const blink::WebAXObject& object, blink::WebAXEvent event) + { + WebTestProxyBase::postAccessibilityEvent(object, event); + Base::postAccessibilityEvent(object, event); + } + virtual void startDragging(blink::WebFrame* frame, const blink::WebDragData& data, blink::WebDragOperationsMask mask, const blink::WebImage& image, const blink::WebPoint& point) + { + WebTestProxyBase::startDragging(frame, data, mask, image, point); + // Don't forward this call to Base because we don't want to do a real drag-and-drop. + } + virtual void didChangeSelection(bool isEmptySelection) + { + WebTestProxyBase::didChangeSelection(isEmptySelection); + Base::didChangeSelection(isEmptySelection); + } + virtual void didChangeContents() + { + WebTestProxyBase::didChangeContents(); + Base::didChangeContents(); + } + virtual blink::WebView* createView(blink::WebFrame* creator, const blink::WebURLRequest& request, const blink::WebWindowFeatures& features, const blink::WebString& frameName, blink::WebNavigationPolicy policy, bool suppressOpener) + { + if (!WebTestProxyBase::createView(creator, request, features, frameName, policy, suppressOpener)) + return 0; + return Base::createView(creator, request, features, frameName, policy, suppressOpener); + } + virtual void setStatusText(const blink::WebString& text) + { + WebTestProxyBase::setStatusText(text); + Base::setStatusText(text); + } + virtual void didStopLoading() + { + WebTestProxyBase::didStopLoading(); + Base::didStopLoading(); + } + virtual void showContextMenu(blink::WebFrame* frame, const blink::WebContextMenuData& contextMenuData) + { + WebTestProxyBase::showContextMenu(frame, contextMenuData); + Base::showContextMenu(frame, contextMenuData); + } + virtual blink::WebUserMediaClient* userMediaClient() + { + return WebTestProxyBase::userMediaClient(); + } + virtual void printPage(blink::WebFrame* frame) + { + WebTestProxyBase::printPage(frame); + } + virtual blink::WebNotificationPresenter* notificationPresenter() + { + return WebTestProxyBase::notificationPresenter(); + } + virtual blink::WebGeolocationClient* geolocationClient() + { + return WebTestProxyBase::geolocationClient(); + } + virtual blink::WebMIDIClient* webMIDIClient() + { + return WebTestProxyBase::webMIDIClient(); + } + virtual blink::WebSpeechInputController* speechInputController(blink::WebSpeechInputListener* listener) + { + return WebTestProxyBase::speechInputController(listener); + } + virtual blink::WebSpeechRecognizer* speechRecognizer() + { + return WebTestProxyBase::speechRecognizer(); + } + virtual bool requestPointerLock() + { + return WebTestProxyBase::requestPointerLock(); + } + virtual void requestPointerUnlock() + { + WebTestProxyBase::requestPointerUnlock(); + } + virtual bool isPointerLocked() + { + return WebTestProxyBase::isPointerLocked(); + } + virtual void didFocus() + { + WebTestProxyBase::didFocus(); + Base::didFocus(); + } + virtual void didBlur() + { + WebTestProxyBase::didBlur(); + Base::didBlur(); + } + virtual void setToolTipText(const blink::WebString& text, blink::WebTextDirection hint) + { + WebTestProxyBase::setToolTipText(text, hint); + Base::setToolTipText(text, hint); + } + virtual void resetInputMethod() + { + WebTestProxyBase::resetInputMethod(); + } + + virtual void didStartProvisionalLoad(blink::WebFrame* frame) + { + WebTestProxyBase::didStartProvisionalLoad(frame); + Base::didStartProvisionalLoad(frame); + } + virtual void didReceiveServerRedirectForProvisionalLoad(blink::WebFrame* frame) + { + WebTestProxyBase::didReceiveServerRedirectForProvisionalLoad(frame); + Base::didReceiveServerRedirectForProvisionalLoad(frame); + } + virtual void didFailProvisionalLoad(blink::WebFrame* frame, const blink::WebURLError& error) + { + // If the test finished, don't notify the embedder of the failed load, + // as we already destroyed the document loader. + if (WebTestProxyBase::didFailProvisionalLoad(frame, error)) + return; + Base::didFailProvisionalLoad(frame, error); + } + virtual void didCommitProvisionalLoad(blink::WebFrame* frame, bool isNewNavigation) + { + WebTestProxyBase::didCommitProvisionalLoad(frame, isNewNavigation); + Base::didCommitProvisionalLoad(frame, isNewNavigation); + } + virtual void didReceiveTitle(blink::WebFrame* frame, const blink::WebString& title, blink::WebTextDirection direction) + { + WebTestProxyBase::didReceiveTitle(frame, title, direction); + Base::didReceiveTitle(frame, title, direction); + } + virtual void didChangeIcon(blink::WebFrame* frame, blink::WebIconURL::Type iconType) + { + WebTestProxyBase::didChangeIcon(frame, iconType); + Base::didChangeIcon(frame, iconType); + } + virtual void didFinishDocumentLoad(blink::WebFrame* frame) + { + WebTestProxyBase::didFinishDocumentLoad(frame); + Base::didFinishDocumentLoad(frame); + } + virtual void didHandleOnloadEvents(blink::WebFrame* frame) + { + WebTestProxyBase::didHandleOnloadEvents(frame); + Base::didHandleOnloadEvents(frame); + } + virtual void didFailLoad(blink::WebFrame* frame, const blink::WebURLError& error) + { + WebTestProxyBase::didFailLoad(frame, error); + Base::didFailLoad(frame, error); + } + virtual void didFinishLoad(blink::WebFrame* frame) + { + WebTestProxyBase::didFinishLoad(frame); + Base::didFinishLoad(frame); + } + virtual void didDetectXSS(blink::WebFrame* frame, const blink::WebURL& insecureURL, bool didBlockEntirePage) + { + WebTestProxyBase::didDetectXSS(frame, insecureURL, didBlockEntirePage); + Base::didDetectXSS(frame, insecureURL, didBlockEntirePage); + } + virtual void willRequestResource(blink::WebFrame* frame, const blink::WebCachedURLRequest& request) + { + WebTestProxyBase::willRequestResource(frame, request); + Base::willRequestResource(frame, request); + } + virtual void willSendRequest(blink::WebFrame* frame, unsigned identifier, blink::WebURLRequest& request, const blink::WebURLResponse& redirectResponse) + { + WebTestProxyBase::willSendRequest(frame, identifier, request, redirectResponse); + Base::willSendRequest(frame, identifier, request, redirectResponse); + } + virtual void didReceiveResponse(blink::WebFrame* frame, unsigned identifier, const blink::WebURLResponse& response) + { + WebTestProxyBase::didReceiveResponse(frame, identifier, response); + Base::didReceiveResponse(frame, identifier, response); + } + virtual void didChangeResourcePriority(blink::WebFrame* frame, unsigned identifier, const blink::WebURLRequest::Priority& priority) + { + WebTestProxyBase::didChangeResourcePriority(frame, identifier, priority); + Base::didChangeResourcePriority(frame, identifier, priority); + } + virtual void didFinishResourceLoad(blink::WebFrame* frame, unsigned identifier) + { + WebTestProxyBase::didFinishResourceLoad(frame, identifier); + Base::didFinishResourceLoad(frame, identifier); + } + virtual void didAddMessageToConsole(const blink::WebConsoleMessage& message, const blink::WebString& sourceName, unsigned sourceLine, const blink::WebString& stackTrace) + { + WebTestProxyBase::didAddMessageToConsole(message, sourceName, sourceLine); + Base::didAddMessageToConsole(message, sourceName, sourceLine, stackTrace); + } + virtual void runModalAlertDialog(blink::WebFrame* frame, const blink::WebString& message) + { + WebTestProxyBase::runModalAlertDialog(frame, message); + Base::runModalAlertDialog(frame, message); + } + virtual bool runModalConfirmDialog(blink::WebFrame* frame, const blink::WebString& message) + { + WebTestProxyBase::runModalConfirmDialog(frame, message); + return Base::runModalConfirmDialog(frame, message); + } + virtual bool runModalPromptDialog(blink::WebFrame* frame, const blink::WebString& message, const blink::WebString& defaultValue, blink::WebString* actualValue) + { + WebTestProxyBase::runModalPromptDialog(frame, message, defaultValue, actualValue); + return Base::runModalPromptDialog(frame, message, defaultValue, actualValue); + } + virtual bool runModalBeforeUnloadDialog(blink::WebFrame* frame, const blink::WebString& message) + { + return WebTestProxyBase::runModalBeforeUnloadDialog(frame, message); + } + virtual blink::WebNavigationPolicy decidePolicyForNavigation(blink::WebFrame* frame, blink::WebDataSource::ExtraData* extraData, const blink::WebURLRequest& request, blink::WebNavigationType type, blink::WebNavigationPolicy defaultPolicy, bool isRedirect) + { + blink::WebNavigationPolicy policy = WebTestProxyBase::decidePolicyForNavigation(frame, extraData, request, type, defaultPolicy, isRedirect); + if (policy == blink::WebNavigationPolicyIgnore) + return policy; + return Base::decidePolicyForNavigation(frame, extraData, request, type, defaultPolicy, isRedirect); + } + virtual bool willCheckAndDispatchMessageEvent(blink::WebFrame* sourceFrame, blink::WebFrame* targetFrame, blink::WebSecurityOrigin target, blink::WebDOMMessageEvent event) + { + if (WebTestProxyBase::willCheckAndDispatchMessageEvent(sourceFrame, targetFrame, target, event)) + return true; + return Base::willCheckAndDispatchMessageEvent(sourceFrame, targetFrame, target, event); + } + virtual blink::WebColorChooser* createColorChooser(blink::WebColorChooserClient* client, const blink::WebColor& color) + { + return WebTestProxyBase::createColorChooser(client, color); + } + virtual blink::WebColorChooser* createColorChooser(blink::WebColorChooserClient* client, const blink::WebColor& color, const blink::WebVector<blink::WebColorSuggestion>& suggestions) + { + return WebTestProxyBase::createColorChooser(client, color, suggestions); + } + virtual bool runFileChooser(const blink::WebFileChooserParams& params, blink::WebFileChooserCompletion* completion) + { + return WebTestProxyBase::runFileChooser(params, completion); + } + virtual void showValidationMessage(const blink::WebRect& anchorInRootView, const blink::WebString& mainText, const blink::WebString& supplementalText, blink::WebTextDirection hint) + { + WebTestProxyBase::showValidationMessage(anchorInRootView, mainText, supplementalText, hint); + } + virtual void hideValidationMessage() + { + WebTestProxyBase::hideValidationMessage(); + } + virtual void moveValidationMessage(const blink::WebRect& anchorInRootView) + { + WebTestProxyBase::moveValidationMessage(anchorInRootView); + } + virtual void postSpellCheckEvent(const blink::WebString& eventName) + { + WebTestProxyBase::postSpellCheckEvent(eventName); + } +}; + +} + +#endif // WebTestProxy_h diff --git a/content/shell/renderer/test_runner/WebTestRunner.h b/content/shell/renderer/test_runner/WebTestRunner.h new file mode 100644 index 0000000..1ae5c30 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestRunner.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTestRunner_h +#define WebTestRunner_h + +namespace blink { +class WebArrayBufferView; +class WebPermissionClient; +} + +namespace WebTestRunner { + +class WebTestRunner { +public: + // Returns a mock WebPermissionClient that is used for layout tests. An + // embedder should use this for all WebViews it creates. + virtual blink::WebPermissionClient* webPermissions() const = 0; + + // After WebTestDelegate::testFinished was invoked, the following methods + // can be used to determine what kind of dump the main WebTestProxy can + // provide. + + // If true, WebTestDelegate::audioData returns an audio dump and no text + // or pixel results are available. + virtual bool shouldDumpAsAudio() const = 0; + virtual const blink::WebArrayBufferView* audioData() const = 0; + + // Returns true if the call to WebTestProxy::captureTree will invoke + // WebTestDelegate::captureHistoryForWindow. + virtual bool shouldDumpBackForwardList() const = 0; + + // Returns true if WebTestProxy::capturePixels should be invoked after + // capturing text results. + virtual bool shouldGeneratePixelResults() = 0; +}; + +} + +#endif // WebTestRunner_h diff --git a/content/shell/renderer/test_runner/WebTestThemeControlWin.cpp b/content/shell/renderer/test_runner/WebTestThemeControlWin.cpp new file mode 100644 index 0000000..28f089a --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeControlWin.cpp @@ -0,0 +1,465 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file implements a simple generic version of the WebThemeEngine, +// which is used to draw all the native controls on a web page. We use this +// file when running in layout test mode in order to remove any +// platform-specific rendering differences due to themes, colors, etc. +// + +#include "content/shell/renderer/test_runner/WebTestThemeControlWin.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "skia/ext/skia_utils_win.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" + +#include <algorithm> + +using namespace blink; +using namespace std; + +namespace WebTestRunner { + +namespace { + +const SkColor edgeColor = SK_ColorBLACK; +const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6); +const SkColor fgColor = SK_ColorBLACK; + +// These are indexed by WebTestThemeControlWin::State, *not* WebThemeEngine::State. +const SkColor bgColors[] = { + SK_ColorBLACK, // Unknown (not used) + SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled + SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly + SkColorSetRGB(0x89, 0xc4, 0xff), // Normal + SkColorSetRGB(0x43, 0xf9, 0xff), // Hot + SkColorSetRGB(0x20, 0xf6, 0xcc), // Hover + SkColorSetRGB(0x00, 0xf3, 0xac), // Focused + SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed + SkColorSetRGB(0xcc, 0xcc, 0xcc) // Indeterminate (not used) +}; + +SkIRect validate(const SkIRect& rect, WebTestThemeControlWin::Type ctype) +{ + switch (ctype) { + case WebTestThemeControlWin::UncheckedBoxType: + case WebTestThemeControlWin::CheckedBoxType: + case WebTestThemeControlWin::UncheckedRadioType: + case WebTestThemeControlWin::CheckedRadioType: { + SkIRect retval = rect; + + // The maximum width and height is 13. + // Center the square in the passed rectangle. + const int maxControlSize = 13; + int controlSize = std::min(rect.width(), rect.height()); + controlSize = std::min(controlSize, maxControlSize); + + retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2); + retval.fRight = retval.fLeft + controlSize - 1; + retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2); + retval.fBottom = retval.fTop + controlSize - 1; + + return retval; + } + + default: + return rect; + } +} + +} + +WebTestThemeControlWin::WebTestThemeControlWin(SkCanvas* canvas, const SkIRect& irect, Type ctype, State cstate) + : m_canvas(canvas) + , m_irect(validate(irect, ctype)) + , m_type(ctype) + , m_state(cstate) + , m_left(m_irect.fLeft) + , m_right(m_irect.fRight) + , m_top(m_irect.fTop) + , m_bottom(m_irect.fBottom) + , m_height(m_irect.height()) + , m_width(m_irect.width()) + , m_edgeColor(edgeColor) + , m_bgColor(bgColors[cstate]) + , m_fgColor(fgColor) +{ +} + +WebTestThemeControlWin::~WebTestThemeControlWin() +{ +} + +void WebTestThemeControlWin::box(const SkIRect& rect, SkColor fillColor) +{ + SkPaint paint; + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(fillColor); + m_canvas->drawIRect(rect, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawIRect(rect, paint); +} + +void WebTestThemeControlWin::line(int x0, int y0, int x1, int y1, SkColor color) +{ + SkPaint paint; + paint.setColor(color); + m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), SkIntToScalar(x1), SkIntToScalar(y1), paint); +} + +void WebTestThemeControlWin::triangle(int x0, int y0, int x1, int y1, int x2, int y2, SkColor color) +{ + SkPath path; + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + path.incReserve(4); + path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0)); + path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1)); + path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2)); + path.close(); + m_canvas->drawPath(path, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawPath(path, paint); +} + +void WebTestThemeControlWin::roundRect(SkColor color) +{ + SkRect rect; + SkScalar radius = SkIntToScalar(5); + SkPaint paint; + + rect.set(m_irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawRoundRect(rect, radius, radius, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawRoundRect(rect, radius, radius, paint); +} + +void WebTestThemeControlWin::oval(SkColor color) +{ + SkRect rect; + SkPaint paint; + + rect.set(m_irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawOval(rect, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawOval(rect, paint); +} + +void WebTestThemeControlWin::circle(SkScalar radius, SkColor color) +{ + SkScalar cy = SkIntToScalar(m_top + m_height / 2); + SkScalar cx = SkIntToScalar(m_left + m_width / 2); + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawCircle(cx, cy, radius, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawCircle(cx, cy, radius, paint); +} + +void WebTestThemeControlWin::nestedBoxes(int indentLeft, int indentTop, int indentRight, int indentBottom, SkColor outerColor, SkColor innerColor) +{ + SkIRect lirect; + box(m_irect, outerColor); + lirect.set(m_irect.fLeft + indentLeft, m_irect.fTop + indentTop, m_irect.fRight - indentRight, m_irect.fBottom - indentBottom); + box(lirect, innerColor); +} + +void WebTestThemeControlWin::markState() +{ + // The horizontal lines in a read only control are spaced by this amount. + const int readOnlyLineOffset = 5; + + // The length of a triangle side for the corner marks. + const int triangleSize = 5; + + switch (m_state) { + case UnknownState: + case DisabledState: + case NormalState: + case IndeterminateState: + // Don't visually mark these states (color is enough). + break; + case ReadOnlyState: + // Drawing lines across the control. + for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset) + line(m_left + 1, i, m_right - 1, i, readOnlyColor); + break; + + case HotState: + // Draw a triangle in the upper left corner of the control. + triangle(m_left, m_top, m_left + triangleSize, m_top, m_left, m_top + triangleSize, m_edgeColor); + break; + + case HoverState: + // Draw a triangle in the upper right corner of the control. + triangle(m_right, m_top, m_right, m_top + triangleSize, m_right - triangleSize, m_top, m_edgeColor); + break; + + case FocusedState: + // Draw a triangle in the bottom right corner of the control. + triangle(m_right, m_bottom, m_right - triangleSize, m_bottom, m_right, m_bottom - triangleSize, m_edgeColor); + break; + + case PressedState: + // Draw a triangle in the bottom left corner of the control. + triangle(m_left, m_bottom, m_left, m_bottom - triangleSize, m_left + triangleSize, m_bottom, m_edgeColor); + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } +} + +void WebTestThemeControlWin::draw() +{ + int halfWidth = m_width / 2; + int halfHeight = m_height / 2; + int quarterWidth = m_width / 4; + int quarterHeight = m_height / 4; + + // Indent amounts for the check in a checkbox or radio button. + const int checkIndent = 3; + + // Indent amounts for short and long sides of the scrollbar notches. + const int notchLongOffset = 1; + const int notchShortOffset = 4; + const int noOffset = 0; + + // Indent amounts for the short and long sides of a scroll thumb box. + const int thumbLongIndent = 0; + const int thumbShortIndent = 2; + + // Indents for the crosshatch on a scroll grip. + const int gripLongIndent = 3; + const int gripShortIndent = 5; + + // Indents for the the slider track. + const int sliderIndent = 2; + + switch (m_type) { + case UnknownType: + BLINK_ASSERT_NOT_REACHED(); + break; + + case TextFieldType: + // We render this by hand outside of this function. + BLINK_ASSERT_NOT_REACHED(); + break; + + case PushButtonType: + // push buttons render as a rounded rectangle + roundRect(m_bgColor); + break; + + case UncheckedBoxType: + // Unchecked boxes are simply plain boxes. + box(m_irect, m_bgColor); + break; + + case CheckedBoxType: + nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor); + break; + + case IndeterminateCheckboxType: + // Indeterminate checkbox is a box containing '-'. + nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor); + break; + + case UncheckedRadioType: + circle(SkIntToScalar(halfHeight), m_bgColor); + break; + + case CheckedRadioType: + circle(SkIntToScalar(halfHeight), m_bgColor); + circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor); + break; + + case HorizontalScrollTrackBackType: { + // Draw a box with a notch at the left. + int longOffset = halfHeight - notchLongOffset; + int shortOffset = m_width - notchShortOffset; + nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor); + break; + } + + case HorizontalScrollTrackForwardType: { + // Draw a box with a notch at the right. + int longOffset = halfHeight - notchLongOffset; + int shortOffset = m_width - notchShortOffset; + nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor); + break; + } + + case VerticalScrollTrackBackType: { + // Draw a box with a notch at the top. + int longOffset = halfWidth - notchLongOffset; + int shortOffset = m_height - notchShortOffset; + nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor); + break; + } + + case VerticalScrollTrackForwardType: { + // Draw a box with a notch at the bottom. + int longOffset = halfWidth - notchLongOffset; + int shortOffset = m_height - notchShortOffset; + nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor); + break; + } + + case HorizontalScrollThumbType: + // Draw a narrower box on top of the outside box. + nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor); + break; + + case VerticalScrollThumbType: + // Draw a shorter box on top of the outside box. + nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor); + break; + + case HorizontalSliderThumbType: + case VerticalSliderThumbType: + // Slider thumbs are ovals. + oval(m_bgColor); + break; + + case HorizontalScrollGripType: { + // Draw a horizontal crosshatch for the grip. + int longOffset = halfWidth - gripLongIndent; + line(m_left + gripLongIndent, m_top + halfHeight, m_right - gripLongIndent, m_top + halfHeight, m_fgColor); + line(m_left + longOffset, m_top + gripShortIndent, m_left + longOffset, m_bottom - gripShortIndent, m_fgColor); + line(m_right - longOffset, m_top + gripShortIndent, m_right - longOffset, m_bottom - gripShortIndent, m_fgColor); + break; + } + + case VerticalScrollGripType: { + // Draw a vertical crosshatch for the grip. + int longOffset = halfHeight - gripLongIndent; + line(m_left + halfWidth, m_top + gripLongIndent, m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor); + line(m_left + gripShortIndent, m_top + longOffset, m_right - gripShortIndent, m_top + longOffset, m_fgColor); + line(m_left + gripShortIndent, m_bottom - longOffset, m_right - gripShortIndent, m_bottom - longOffset, m_fgColor); + break; + } + + case LeftArrowType: + // Draw a left arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_right - quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_left + quarterWidth, m_top + halfHeight, m_fgColor); + break; + + case RightArrowType: + // Draw a left arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + halfHeight, m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case UpArrowType: + // Draw an up arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_bottom - quarterHeight, m_left + halfWidth, m_top + quarterHeight, m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case DownArrowType: + // Draw a down arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top + quarterHeight, m_right - quarterWidth, m_top + quarterHeight, m_left + halfWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case HorizontalSliderTrackType: { + // Draw a narrow rect for the track plus box hatches on the ends. + SkIRect lirect; + lirect = m_irect; + lirect.inset(noOffset, halfHeight - sliderIndent); + box(lirect, m_bgColor); + line(m_left, m_top, m_left, m_bottom, m_edgeColor); + line(m_right, m_top, m_right, m_bottom, m_edgeColor); + break; + } + + case VerticalSliderTrackType: { + // Draw a narrow rect for the track plus box hatches on the ends. + SkIRect lirect; + lirect = m_irect; + lirect.inset(halfWidth - sliderIndent, noOffset); + box(lirect, m_bgColor); + line(m_left, m_top, m_right, m_top, m_edgeColor); + line(m_left, m_bottom, m_right, m_bottom, m_edgeColor); + break; + } + + case DropDownButtonType: + // Draw a box with a big down arrow on top. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top, m_right - quarterWidth, m_top, m_left + halfWidth, m_bottom, m_fgColor); + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + markState(); +} + +// Because rendering a text field is dependent on input +// parameters the other controls don't have, we render it directly +// rather than trying to overcomplicate draw() further. +void WebTestThemeControlWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color) +{ + SkPaint paint; + + if (fillContentArea) { + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(m_irect, paint); + } + if (drawEdges) { + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawIRect(m_irect, paint); + } + + markState(); +} + +void WebTestThemeControlWin::drawProgressBar(const SkIRect& fillRect) +{ + SkPaint paint; + + paint.setColor(m_bgColor); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(m_irect, paint); + + // Emulate clipping + SkIRect tofill; + tofill.intersect(m_irect, fillRect); + paint.setColor(m_fgColor); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(tofill, paint); + + markState(); +} + +} diff --git a/content/shell/renderer/test_runner/WebTestThemeControlWin.h b/content/shell/renderer/test_runner/WebTestThemeControlWin.h new file mode 100644 index 0000000..66b5ae8 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeControlWin.h @@ -0,0 +1,175 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WebTestThemeControlWin implements the generic rendering of controls +// needed by WebThemeEngineDRTWin. See the comments in that class +// header file for why this class is needed and used. +// +// This class implements a generic set of widgets using Skia. The widgets +// are optimized for testability, not a pleasing appearance. +// + +#ifndef WebTestThemeControlWin_h +#define WebTestThemeControlWin_h + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkRect.h" + +// Skia forward declarations +class SkCanvas; + +namespace WebTestRunner { + +class WebTestThemeControlWin : public blink::WebNonCopyable { +public: + // This list of states mostly mirrors the list in WebCore/platform/ThemeTypes.h + // but is maintained separately since that isn't public and also to minimize + // dependencies. + // Note that the WebKit ThemeTypes seem to imply that a control can be + // in multiple states simultaneously but WebThemeEngine only allows for + // a single state at a time. + // + // Some definitions for the various states: + // Disabled - indicates that a control can't be modified or selected + // (corresponds to HTML 'disabled' attribute) + // ReadOnly - indicates that a control can't be modified but can be + // selected + // Normal - the normal state of control on the page when it isn't + // focused or otherwise active + // Hot - when the mouse is hovering over a part of the control, + // all the other parts are considered "hot" + // Hover - when the mouse is directly over a control (the CSS + // :hover pseudo-class) + // Focused - when the control has the keyboard focus + // Pressed - when the control is being triggered (by a mousedown or + // a key event). + // Indeterminate - when set to indeterminate (only for progress bar) + enum State { + UnknownState = 0, + DisabledState, + ReadOnlyState, + NormalState, + HotState, + HoverState, + FocusedState, + PressedState, + IndeterminateState + }; + + // This list of types mostly mirrors the list in + // WebCore/platform/ThemeTypes.h but is maintained + // separately since that isn't public and also to minimize dependencies. + // + // Note that what the user might think of as a single control can be + // made up of multiple parts. For example, a single scroll bar contains + // six clickable parts - two arrows, the "thumb" indicating the current + // position on the bar, the other two parts of the bar (before and after + // the thumb) and the "gripper" on the thumb itself. + // + enum Type { + UnknownType = 0, + TextFieldType, + PushButtonType, + UncheckedBoxType, + CheckedBoxType, + IndeterminateCheckboxType, + UncheckedRadioType, + CheckedRadioType, + HorizontalScrollTrackBackType, + HorizontalScrollTrackForwardType, + HorizontalScrollThumbType, + HorizontalScrollGripType, + VerticalScrollTrackBackType, + VerticalScrollTrackForwardType, + VerticalScrollThumbType, + VerticalScrollGripType, + LeftArrowType, + RightArrowType, + UpArrowType, + DownArrowType, + HorizontalSliderTrackType, + HorizontalSliderThumbType, + VerticalSliderTrackType, + VerticalSliderThumbType, + DropDownButtonType, + ProgressBarType + }; + + // Constructs a control of the given size, type and state to draw + // on to the given canvas. + WebTestThemeControlWin(SkCanvas*, const SkIRect&, Type, State); + ~WebTestThemeControlWin(); + + // Draws the control. + void draw(); + + // Use this for TextField controls instead, because the logic + // for drawing them is dependent on what WebKit tells us to do. + // If drawEdges is true, draw an edge around the control. If + // fillContentArea is true, fill the content area with the given color. + void drawTextField(bool drawEdges, bool fillContentArea, SkColor); + + // Use this for drawing ProgressBar controls instead, since we + // need to know the rect to fill inside the bar. + void drawProgressBar(const SkIRect& fillRect); + +private: + // Draws a box of size specified by irect, filled with the given color. + // The box will have a border drawn in the default edge color. + void box(const SkIRect& irect, SkColor); + + + // Draws a triangle of size specified by the three pairs of coordinates, + // filled with the given color. The box will have an edge drawn in the + // default edge color. + void triangle(int x0, int y0, int x1, int y1, int x2, int y2, SkColor); + + // Draws a rectangle the size of the control with rounded corners, filled + // with the specified color (and with a border in the default edge color). + void roundRect(SkColor); + + // Draws an oval the size of the control, filled with the specified color + // and with a border in the default edge color. + void oval(SkColor); + + // Draws a circle centered in the control with the specified radius, + // filled with the specified color, and with a border draw in the + // default edge color. + void circle(SkScalar radius, SkColor); + + // Draws a box the size of the control, filled with the outerColor and + // with a border in the default edge color, and then draws another box + // indented on all four sides by the specified amounts, filled with the + // inner color and with a border in the default edge color. + void nestedBoxes(int indentLeft, int indentTop, int indentRight, int indentBottom, SkColor outerColor, SkColor innerColor); + + // Draws a line between the two points in the given color. + void line(int x0, int y0, int x1, int y1, SkColor); + + // Draws a distinctive mark on the control for each state, so that the + // state of the control can be determined without needing to know which + // color is which. + void markState(); + + SkCanvas* m_canvas; + const SkIRect m_irect; + const Type m_type; + const State m_state; + const SkColor m_edgeColor; + const SkColor m_bgColor; + const SkColor m_fgColor; + + // The following are convenience accessors for m_irect. + const int m_left; + const int m_right; + const int m_top; + const int m_bottom; + const int m_width; + const int m_height; +}; + +} + +#endif // WebTestThemeControlWin_h diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineMac.h b/content/shell/renderer/test_runner/WebTestThemeEngineMac.h new file mode 100644 index 0000000..f51d128 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineMac.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This implements the WebThemeEngine API in such a way that we match the Mac +// port rendering more than usual Chromium path, thus allowing us to share +// more pixel baselines. + +#ifndef WebTestThemeEngineMac_h +#define WebTestThemeEngineMac_h + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/mac/WebThemeEngine.h" + +namespace WebTestRunner { + +class WebTestThemeEngineMac : public blink::WebThemeEngine, public blink::WebNonCopyable { +public: + virtual ~WebTestThemeEngineMac() { } + + virtual void paintScrollbarThumb( + blink::WebCanvas*, + blink::WebThemeEngine::State, + blink::WebThemeEngine::Size, + const blink::WebRect&, + const blink::WebThemeEngine::ScrollbarInfo&); + +private: + virtual void paintHIThemeScrollbarThumb( + blink::WebCanvas*, + blink::WebThemeEngine::State, + blink::WebThemeEngine::Size, + const blink::WebRect&, + const blink::WebThemeEngine::ScrollbarInfo&); + virtual void paintNSScrollerScrollbarThumb( + blink::WebCanvas*, + blink::WebThemeEngine::State, + blink::WebThemeEngine::Size, + const blink::WebRect&, + const blink::WebThemeEngine::ScrollbarInfo&); +}; + +} + +#endif // WebTestThemeEngineMac_h diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineMac.mm b/content/shell/renderer/test_runner/WebTestThemeEngineMac.mm new file mode 100644 index 0000000..8a26302 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineMac.mm @@ -0,0 +1,173 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebTestThemeEngineMac.h" + +#import <AppKit/NSAffineTransform.h> +#import <AppKit/NSGraphicsContext.h> +#import <AppKit/NSScroller.h> +#import <AppKit/NSWindow.h> +#include <Carbon/Carbon.h> +#include "skia/ext/skia_utils_mac.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/platform/WebRect.h" + +using blink::WebCanvas; +using blink::WebRect; +using blink::WebThemeEngine; + +// We can't directly tell the NSScroller to draw itself as active or inactive, +// instead we have to make it a child of an (in)active window. This class lets +// us fake that parent window. +@interface FakeActiveWindow : NSWindow { +@private + BOOL hasActiveControls; +} ++ (NSWindow*)alwaysActiveWindow; ++ (NSWindow*)alwaysInactiveWindow; +- (id)initWithActiveControls:(BOOL)_hasActiveControls; +- (BOOL)_hasActiveControls; +@end + +@implementation FakeActiveWindow + +static NSWindow* alwaysActiveWindow = nil; +static NSWindow* alwaysInactiveWindow = nil; + ++ (NSWindow*)alwaysActiveWindow +{ + if (alwaysActiveWindow == nil) + alwaysActiveWindow = [[self alloc] initWithActiveControls:YES]; + return alwaysActiveWindow; +} + ++ (NSWindow*)alwaysInactiveWindow +{ + if (alwaysInactiveWindow == nil) + alwaysInactiveWindow = [[self alloc] initWithActiveControls:NO]; + return alwaysInactiveWindow; +} + +- (id)initWithActiveControls:(BOOL)_hasActiveControls +{ + self = [super init]; + hasActiveControls = _hasActiveControls; + return self; +} + +- (BOOL)_hasActiveControls +{ + return hasActiveControls; +} + +@end + +namespace WebTestRunner { + +namespace { + +ThemeTrackEnableState stateToHIEnableState(WebThemeEngine::State state) +{ + switch (state) { + case WebThemeEngine::StateDisabled: + return kThemeTrackDisabled; + case WebThemeEngine::StateInactive: + return kThemeTrackInactive; + default: + return kThemeTrackActive; + } +} + +} + +void WebTestThemeEngineMac::paintScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + // To match the Mac port, we still use HITheme for inner scrollbars. + if (scrollbarInfo.parent == WebThemeEngine::ScrollbarParentRenderLayer) + paintHIThemeScrollbarThumb(canvas, state, size, rect, scrollbarInfo); + else + paintNSScrollerScrollbarThumb(canvas, state, size, rect, scrollbarInfo); +} + +// Duplicated from webkit/glue/webthemeengine_impl_mac.cc in the downstream +// Chromium WebThemeEngine implementation. +void WebTestThemeEngineMac::paintHIThemeScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + HIThemeTrackDrawInfo trackInfo; + trackInfo.version = 0; + trackInfo.kind = size == WebThemeEngine::SizeRegular ? kThemeMediumScrollBar : kThemeSmallScrollBar; + trackInfo.bounds = CGRectMake(rect.x, rect.y, rect.width, rect.height); + trackInfo.min = 0; + trackInfo.max = scrollbarInfo.maxValue; + trackInfo.value = scrollbarInfo.currentValue; + trackInfo.trackInfo.scrollbar.viewsize = scrollbarInfo.visibleSize; + trackInfo.attributes = 0; + if (scrollbarInfo.orientation == WebThemeEngine::ScrollbarOrientationHorizontal) + trackInfo.attributes |= kThemeTrackHorizontal; + + trackInfo.enableState = stateToHIEnableState(state); + + trackInfo.trackInfo.scrollbar.pressState = + state == WebThemeEngine::StatePressed ? kThemeThumbPressed : 0; + trackInfo.attributes |= (kThemeTrackShowThumb | kThemeTrackHideTrack); + gfx::SkiaBitLocker bitLocker(canvas); + CGContextRef cgContext = bitLocker.cgContext(); + HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal); +} + +void WebTestThemeEngineMac::paintNSScrollerScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + [NSGraphicsContext saveGraphicsState]; + NSScroller* scroller = [[NSScroller alloc] initWithFrame:NSMakeRect(rect.x, rect.y, rect.width, rect.height)]; + [scroller setEnabled:state != WebThemeEngine::StateDisabled]; + if (state == WebThemeEngine::StateInactive) + [[[FakeActiveWindow alwaysInactiveWindow] contentView] addSubview:scroller]; + else + [[[FakeActiveWindow alwaysActiveWindow] contentView] addSubview:scroller]; + + [scroller setControlSize:size == WebThemeEngine::SizeRegular ? NSRegularControlSize : NSSmallControlSize]; + + double value = double(scrollbarInfo.currentValue) / double(scrollbarInfo.maxValue); + [scroller setDoubleValue: value]; + + float knobProportion = float(scrollbarInfo.visibleSize) / float(scrollbarInfo.totalSize); + [scroller setKnobProportion: knobProportion]; + + gfx::SkiaBitLocker bitLocker(canvas); + CGContextRef cgContext = bitLocker.cgContext(); + NSGraphicsContext* nsGraphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; + [NSGraphicsContext setCurrentContext:nsGraphicsContext]; + + // Despite passing in frameRect() to the scroller, it always draws at (0, 0). + // Force it to draw in the right location by translating the whole graphics + // context. + CGContextSaveGState(cgContext); + NSAffineTransform *transform = [NSAffineTransform transform]; + [transform translateXBy:rect.x yBy:rect.y]; + [transform concat]; + + [scroller drawKnob]; + CGContextRestoreGState(cgContext); + + [scroller release]; + + [NSGraphicsContext restoreGraphicsState]; +} + +} diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineMock.cpp b/content/shell/renderer/test_runner/WebTestThemeEngineMock.cpp new file mode 100644 index 0000000..125f134 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineMock.cpp @@ -0,0 +1,601 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FIXME: This code is largely cloned from WebTestThemeEngineWin.cpp +// and WebTestThemeControlWin.cpp. We should delete that code once the +// cutover to Aura is final. + +#include "content/shell/renderer/test_runner/WebTestThemeEngineMock.h" + +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/skia/include/core/SkRect.h" + +using blink::WebCanvas; +using blink::WebColor; +using blink::WebRect; +using blink::WebThemeEngine; + +namespace WebTestRunner { + +static const SkColor edgeColor = SK_ColorBLACK; +static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6); +static const SkColor bgColors[] = { + SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled + SkColorSetRGB(0x43, 0xf9, 0xff), // Hover (Win's "Hot") + SkColorSetRGB(0x89, 0xc4, 0xff), // Normal + SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed + SkColorSetRGB(0x00, 0xf3, 0xac), // Focused + SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly +}; + + +blink::WebSize WebTestThemeEngineMock::getSize(WebThemeEngine::Part part) +{ + // FIXME: We use this constant to indicate we are being asked for the size of + // a part that we don't expect to be asked about. We return a garbage value + // rather than just asserting because this code doesn't have access to either + // WTF or base to raise an assertion or do any logging :(. + const blink::WebSize invalidPartSize = blink::WebSize(100, 100); + + switch (part) { + case WebThemeEngine::PartScrollbarLeftArrow: + return blink::WebSize(17, 15); + case WebThemeEngine::PartScrollbarRightArrow: + return invalidPartSize; + case WebThemeEngine::PartScrollbarUpArrow: + return blink::WebSize(15, 17); + case WebThemeEngine::PartScrollbarDownArrow: + return invalidPartSize; + case WebThemeEngine::PartScrollbarHorizontalThumb: + return blink::WebSize(15, 15); + case WebThemeEngine::PartScrollbarVerticalThumb: + return blink::WebSize(15, 15); + case WebThemeEngine::PartScrollbarHorizontalTrack: + return blink::WebSize(0, 15); + case WebThemeEngine::PartScrollbarVerticalTrack: + return blink::WebSize(15, 0); + case WebThemeEngine::PartCheckbox: + case WebThemeEngine::PartRadio: + return blink::WebSize(13, 13); + case WebThemeEngine::PartSliderThumb: + return blink::WebSize(11, 21); + case WebThemeEngine::PartInnerSpinButton: + return blink::WebSize(15, 8); + default: + return invalidPartSize; + } +} + +static SkIRect webRectToSkIRect(const WebRect& webRect) +{ + SkIRect irect; + irect.set(webRect.x, webRect.y, + webRect.x + webRect.width - 1, webRect.y + webRect.height - 1); + return irect; +} + +static SkIRect validate(const SkIRect& rect, WebThemeEngine::Part part) +{ + switch (part) { + case WebThemeEngine::PartCheckbox: + case WebThemeEngine::PartRadio: { + SkIRect retval = rect; + + // The maximum width and height is 13. + // Center the square in the passed rectangle. + const int maxControlSize = 13; + int controlSize = std::min(rect.width(), rect.height()); + controlSize = std::min(controlSize, maxControlSize); + + retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2); + retval.fRight = retval.fLeft + controlSize - 1; + retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2); + retval.fBottom = retval.fTop + controlSize - 1; + + return retval; + } + default: + return rect; + } +} + + +void box(SkCanvas *canvas, const SkIRect& rect, SkColor fillColor) +{ + SkPaint paint; + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(fillColor); + canvas->drawIRect(rect, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawIRect(rect, paint); +} + +void line(SkCanvas *canvas, int x0, int y0, int x1, int y1, SkColor color) +{ + SkPaint paint; + paint.setColor(color); + canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), + SkIntToScalar(x1), SkIntToScalar(y1), paint); +} + +void triangle(SkCanvas *canvas, + int x0, int y0, + int x1, int y1, + int x2, int y2, + SkColor color) +{ + SkPath path; + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + path.incReserve(4); + path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0)); + path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1)); + path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2)); + path.close(); + canvas->drawPath(path, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(path, paint); +} + +void roundRect(SkCanvas *canvas, SkIRect irect, SkColor color) +{ + SkRect rect; + SkScalar radius = SkIntToScalar(5); + SkPaint paint; + + rect.set(irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawRoundRect(rect, radius, radius, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawRoundRect(rect, radius, radius, paint); +} + +void oval(SkCanvas* canvas, SkIRect irect, SkColor color) +{ + SkRect rect; + SkPaint paint; + + rect.set(irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawOval(rect, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawOval(rect, paint); +} + +void circle(SkCanvas *canvas, SkIRect irect, SkScalar radius, SkColor color) +{ + int left = irect.fLeft; + int width = irect.width(); + int height = irect.height(); + int top = irect.fTop; + + SkScalar cy = SkIntToScalar(top + height / 2); + SkScalar cx = SkIntToScalar(left + width / 2); + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawCircle(cx, cy, radius, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawCircle(cx, cy, radius, paint); +} + +void nestedBoxes(SkCanvas *canvas, + SkIRect irect, + int indentLeft, + int indentTop, + int indentRight, + int indentBottom, + SkColor outerColor, + SkColor innerColor) +{ + SkIRect lirect; + box(canvas, irect, outerColor); + lirect.set(irect.fLeft + indentLeft, + irect.fTop + indentTop, + irect.fRight - indentRight, + irect.fBottom - indentBottom); + box(canvas, lirect, innerColor); +} + +void markState(SkCanvas *canvas, SkIRect irect, WebThemeEngine::State state) +{ + int left = irect.fLeft; + int right = irect.fRight; + int top = irect.fTop; + int bottom = irect.fBottom; + + // The length of a triangle side for the corner marks. + const int triangleSize = 5; + + switch (state) { + case WebThemeEngine::StateDisabled: + case WebThemeEngine::StateNormal: + // Don't visually mark these states (color is enough). + break; + + case WebThemeEngine::StateReadonly: { + // The horizontal lines in a read only control are spaced by this amount. + const int readOnlyLineOffset = 5; + + // Drawing lines across the control. + for (int i = top + readOnlyLineOffset; i < bottom; i += readOnlyLineOffset) + line(canvas, left + 1, i, right - 1, i, readOnlyColor); + break; + } + case WebThemeEngine::StateHover: + // Draw a triangle in the upper left corner of the control. (Win's "hot") + triangle(canvas, + left, top, + left + triangleSize, top, + left, top + triangleSize, + edgeColor); + break; + + case WebThemeEngine::StateFocused: + // Draw a triangle in the bottom right corner of the control. + triangle(canvas, + right, bottom, + right - triangleSize, bottom, + right, bottom - triangleSize, + edgeColor); + break; + + case WebThemeEngine::StatePressed: + // Draw a triangle in the bottom left corner of the control. + triangle(canvas, + left, bottom, + left, bottom - triangleSize, + left + triangleSize, bottom, + edgeColor); + break; + + default: + // FIXME: Should we do something here to indicate that we got an invalid state? + // Unfortunately, we can't assert because we don't have access to WTF or base. + break; + } +} + +void WebTestThemeEngineMock::paint( + blink::WebCanvas* canvas, + WebThemeEngine::Part part, + WebThemeEngine::State state, + const blink::WebRect& rect, + const WebThemeEngine::ExtraParams* extraParams) +{ + SkIRect irect = webRectToSkIRect(rect); + SkPaint paint; + + // Indent amounts for the check in a checkbox or radio button. + const int checkIndent = 3; + + // Indent amounts for short and long sides of the scrollbar notches. + const int notchLongOffset = 1; + const int notchShortOffset = 4; + const int noOffset = 0; + + // Indent amounts for the short and long sides of a scroll thumb box. + const int thumbLongIndent = 0; + const int thumbShortIndent = 2; + + // Indents for the crosshatch on a scroll grip. + const int gripLongIndent = 3; + const int gripShortIndent = 5; + + // Indents for the the slider track. + const int sliderIndent = 2; + + int halfHeight = irect.height() / 2; + int halfWidth = irect.width() / 2; + int quarterHeight = irect.height() / 4; + int quarterWidth = irect.width() / 4; + int left = irect.fLeft; + int right = irect.fRight; + int top = irect.fTop; + int bottom = irect.fBottom; + + switch (part) { + case WebThemeEngine::PartScrollbarDownArrow: + box(canvas, irect, bgColors[state]); + triangle(canvas, + left + quarterWidth, top + quarterHeight, + right - quarterWidth, top + quarterHeight, + left + halfWidth, bottom - quarterHeight, + edgeColor); + markState(canvas, irect, state); + break; + + case WebThemeEngine::PartScrollbarLeftArrow: + box(canvas, irect, bgColors[state]); + triangle(canvas, + right - quarterWidth, top + quarterHeight, + right - quarterWidth, bottom - quarterHeight, + left + quarterWidth, top + halfHeight, + edgeColor); + break; + + case WebThemeEngine::PartScrollbarRightArrow: + box(canvas, irect, bgColors[state]); + triangle(canvas, + left + quarterWidth, top + quarterHeight, + right - quarterWidth, top + halfHeight, + left + quarterWidth, bottom - quarterHeight, + edgeColor); + break; + + case WebThemeEngine::PartScrollbarUpArrow: + box(canvas, irect, bgColors[state]); + triangle(canvas, + left + quarterWidth, bottom - quarterHeight, + left + halfWidth, top + quarterHeight, + right - quarterWidth, bottom - quarterHeight, + edgeColor); + markState(canvas, irect, state); + break; + + case WebThemeEngine::PartScrollbarHorizontalThumb: { + // Draw a narrower box on top of the outside box. + nestedBoxes(canvas, irect, thumbLongIndent, thumbShortIndent, + thumbLongIndent, thumbShortIndent, + bgColors[state], bgColors[state]); + // Draw a horizontal crosshatch for the grip. + int longOffset = halfWidth - gripLongIndent; + line(canvas, + left + gripLongIndent, top + halfHeight, + right - gripLongIndent, top + halfHeight, + edgeColor); + line(canvas, + left + longOffset, top + gripShortIndent, + left + longOffset, bottom - gripShortIndent, + edgeColor); + line(canvas, + right - longOffset, top + gripShortIndent, + right - longOffset, bottom - gripShortIndent, + edgeColor); + markState(canvas, irect, state); + break; + } + + case WebThemeEngine::PartScrollbarVerticalThumb: { + // Draw a shorter box on top of the outside box. + nestedBoxes(canvas, irect, thumbShortIndent, thumbLongIndent, + thumbShortIndent, thumbLongIndent, + bgColors[state], bgColors[state]); + // Draw a vertical crosshatch for the grip. + int longOffset = halfHeight - gripLongIndent; + line(canvas, + left + halfWidth, top + gripLongIndent, + left + halfWidth, bottom - gripLongIndent, + edgeColor); + line(canvas, + left + gripShortIndent, top + longOffset, + right - gripShortIndent, top + longOffset, + edgeColor); + line(canvas, + left + gripShortIndent, bottom - longOffset, + right - gripShortIndent, bottom - longOffset, + edgeColor); + markState(canvas, irect, state); + break; + } + + case WebThemeEngine::PartScrollbarHorizontalTrack: { + int longOffset = halfHeight - notchLongOffset; + int shortOffset = irect.width() - notchShortOffset; + if (extraParams->scrollbarTrack.isBack) { + // back, notch on left + nestedBoxes(canvas, irect, noOffset, longOffset, shortOffset, + longOffset, bgColors[state], edgeColor); + } else { + // forward, notch on right + nestedBoxes(canvas, irect, shortOffset, longOffset, noOffset, + longOffset, bgColors[state], edgeColor); + } + + markState(canvas, irect, state); + break; + } + + case WebThemeEngine::PartScrollbarVerticalTrack: { + int longOffset = halfWidth - notchLongOffset; + int shortOffset = irect.height() - notchShortOffset; + if (extraParams->scrollbarTrack.isBack) { + // back, notch at top + nestedBoxes(canvas, irect, longOffset, noOffset, longOffset, + shortOffset, bgColors[state], edgeColor); + } else { + // forward, notch at bottom + nestedBoxes(canvas, irect, longOffset, shortOffset, longOffset, + noOffset, bgColors[state], edgeColor); + } + + markState(canvas, irect, state); + break; + } + + case WebThemeEngine::PartCheckbox: + if (extraParams->button.indeterminate) { + nestedBoxes(canvas, irect, + checkIndent, halfHeight, + checkIndent, halfHeight, + bgColors[state], edgeColor); + } else if (extraParams->button.checked) { + irect = validate(irect, part); + nestedBoxes(canvas, irect, + checkIndent, checkIndent, + checkIndent, checkIndent, + bgColors[state], edgeColor); + } else { + irect = validate(irect, part); + box(canvas, irect, bgColors[state]); + } + break; + + case WebThemeEngine::PartRadio: + irect = validate(irect, part); + halfHeight = irect.height() / 2; + if (extraParams->button.checked) { + circle(canvas, irect, SkIntToScalar(halfHeight), bgColors[state]); + circle(canvas, irect, SkIntToScalar(halfHeight - checkIndent), edgeColor); + } else { + circle(canvas, irect, SkIntToScalar(halfHeight), bgColors[state]); + } + break; + + case WebThemeEngine::PartButton: + roundRect(canvas, irect, bgColors[state]); + markState(canvas, irect, state); + break; + + case WebThemeEngine::PartTextField: + paint.setColor(extraParams->textField.backgroundColor); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawIRect(irect, paint); + + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawIRect(irect, paint); + + markState(canvas, irect, state); + break; + + case WebThemeEngine::PartMenuList: + if (extraParams->menuList.fillContentArea) { + box(canvas, irect, extraParams->menuList.backgroundColor); + } else { + SkPaint paint; + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawIRect(irect, paint); + } + + // clip the drop-down arrow to be inside the select box + if (extraParams->menuList.arrowX - 4 > irect.fLeft) + irect.fLeft = extraParams->menuList.arrowX - 4; + if (extraParams->menuList.arrowX + 12 < irect.fRight) + irect.fRight = extraParams->menuList.arrowX + 12; + + irect.fTop = extraParams->menuList.arrowY - (extraParams->menuList.arrowHeight) / 2; + irect.fBottom = extraParams->menuList.arrowY + (extraParams->menuList.arrowHeight - 1) / 2; + halfWidth = irect.width() / 2; + quarterWidth = irect.width() / 4; + + if (state == WebThemeEngine::StateFocused) // FIXME: draw differenty? + state = WebThemeEngine::StateNormal; + box(canvas, irect, bgColors[state]); + triangle(canvas, + irect.fLeft + quarterWidth, irect.fTop, + irect.fRight - quarterWidth, irect.fTop, + irect.fLeft + halfWidth, irect.fBottom, + edgeColor); + + break; + + case WebThemeEngine::PartSliderTrack: { + SkIRect lirect = irect; + + // Draw a narrow rect for the track plus box hatches on the ends. + if (state == WebThemeEngine::StateFocused) // FIXME: draw differently? + state = WebThemeEngine::StateNormal; + if (extraParams->slider.vertical) { + lirect.inset(halfWidth - sliderIndent, noOffset); + box(canvas, lirect, bgColors[state]); + line(canvas, left, top, right, top, edgeColor); + line(canvas, left, bottom, right, bottom, edgeColor); + } else { + lirect.inset(noOffset, halfHeight - sliderIndent); + box(canvas, lirect, bgColors[state]); + line(canvas, left, top, left, bottom, edgeColor); + line(canvas, right, top, right, bottom, edgeColor); + } + break; + } + + case WebThemeEngine::PartSliderThumb: + if (state == WebThemeEngine::StateFocused) // FIXME: draw differently? + state = WebThemeEngine::StateNormal; + oval(canvas, irect, bgColors[state]); + break; + + case WebThemeEngine::PartInnerSpinButton: { + // stack half-height up and down arrows on top of each other + SkIRect lirect; + int halfHeight = rect.height / 2; + if (extraParams->innerSpin.readOnly) + state = blink::WebThemeEngine::StateDisabled; + + lirect.set(rect.x, rect.y, rect.x + rect.width - 1, rect.y + halfHeight - 1); + box(canvas, lirect, bgColors[state]); + bottom = lirect.fBottom; + quarterHeight = lirect.height() / 4; + triangle(canvas, + left + quarterWidth, bottom - quarterHeight, + right - quarterWidth, bottom - quarterHeight, + left + halfWidth, top + quarterHeight, + edgeColor); + + lirect.set(rect.x, rect.y + halfHeight, rect.x + rect.width - 1, + rect.y + 2 * halfHeight - 1); + top = lirect.fTop; + bottom = lirect.fBottom; + quarterHeight = lirect.height() / 4; + box(canvas, lirect, bgColors[state]); + triangle(canvas, + left + quarterWidth, top + quarterHeight, + right - quarterWidth, top + quarterHeight, + left + halfWidth, bottom - quarterHeight, + edgeColor); + markState(canvas, irect, state); + break; + } + case WebThemeEngine::PartProgressBar: { + paint.setColor(bgColors[state]); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawIRect(irect, paint); + + // Emulate clipping + SkIRect tofill = irect; + if (extraParams->progressBar.determinate) { + tofill.set(extraParams->progressBar.valueRectX, + extraParams->progressBar.valueRectY, + extraParams->progressBar.valueRectX + + extraParams->progressBar.valueRectWidth - 1, + extraParams->progressBar.valueRectY + + extraParams->progressBar.valueRectHeight); + } + + tofill.intersect(irect, tofill); + paint.setColor(edgeColor); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawIRect(tofill, paint); + + markState(canvas, irect, state); + break; + } + default: + // FIXME: Should we do something here to indicate that we got an invalid part? + // Unfortunately, we can't assert because we don't have access to WTF or base. + break; + } +} + +} // namespace WebTestRunner diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineMock.h b/content/shell/renderer/test_runner/WebTestThemeEngineMock.h new file mode 100644 index 0000000..88ac8e1 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineMock.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebTestThemeEngineMock_h +#define WebTestThemeEngineMock_h + +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/default/WebThemeEngine.h" + +using blink::WebRect; +using blink::WebThemeEngine; + +namespace WebTestRunner { + +class WebTestThemeEngineMock : public blink::WebThemeEngine { +public: + virtual ~WebTestThemeEngineMock() { } + + // WebThemeEngine methods: + virtual blink::WebSize getSize(WebThemeEngine::Part); + + virtual void paint(blink::WebCanvas*, + WebThemeEngine::Part, + WebThemeEngine::State, + const blink::WebRect&, + const WebThemeEngine::ExtraParams*); +}; + +} // namespace WebTestRunner + +#endif // WebTestThemeEngineMock_h diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineWin.cpp b/content/shell/renderer/test_runner/WebTestThemeEngineWin.cpp new file mode 100644 index 0000000..9a191fd --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineWin.cpp @@ -0,0 +1,716 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebTestThemeEngineWin.h" + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTestThemeControlWin.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/skia/include/core/SkRect.h" + +// Although all this code is generic, we include these headers +// to pull in the Windows #defines for the parts and states of +// the controls. +#include <windows.h> +#include <vsstyle.h> + +using namespace blink; + +namespace WebTestRunner { + +namespace { + +// We define this for clarity, although there really should be a DFCS_NORMAL in winuser.h. +const int dfcsNormal = 0x0000; + +SkIRect webRectToSkIRect(const WebRect& webRect) +{ + SkIRect irect; + irect.set(webRect.x, webRect.y, webRect.x + webRect.width - 1, webRect.y + webRect.height - 1); + return irect; +} + +void drawControl(WebCanvas* canvas, const WebRect& rect, WebTestThemeControlWin::Type ctype, WebTestThemeControlWin::State cstate) +{ + WebTestThemeControlWin control(canvas, webRectToSkIRect(rect), ctype, cstate); + control.draw(); +} + +void drawTextField(WebCanvas* canvas, const WebRect& rect, WebTestThemeControlWin::Type ctype, WebTestThemeControlWin::State cstate, bool drawEdges, bool fillContentArea, WebColor color) +{ + WebTestThemeControlWin control(canvas, webRectToSkIRect(rect), ctype, cstate); + control.drawTextField(drawEdges, fillContentArea, color); +} + +void drawProgressBar(WebCanvas* canvas, WebTestThemeControlWin::Type ctype, WebTestThemeControlWin::State cstate, const WebRect& barRect, const WebRect& fillRect) +{ + WebTestThemeControlWin control(canvas, webRectToSkIRect(barRect), ctype, cstate); + control.drawProgressBar(webRectToSkIRect(fillRect)); +} + +} + +void WebTestThemeEngineWin::paintButton(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + if (part == BP_CHECKBOX) { + switch (state) { + case CBS_UNCHECKEDNORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + ctype = WebTestThemeControlWin::UncheckedBoxType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case CBS_UNCHECKEDHOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_HOT)); + ctype = WebTestThemeControlWin::UncheckedBoxType; + cstate = WebTestThemeControlWin::HotState; + break; + + case CBS_UNCHECKEDPRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::UncheckedBoxType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case CBS_UNCHECKEDDISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::UncheckedBoxType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case CBS_CHECKEDNORMAL: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED)); + ctype = WebTestThemeControlWin::CheckedBoxType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case CBS_CHECKEDHOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_HOT)); + ctype = WebTestThemeControlWin::CheckedBoxType; + cstate = WebTestThemeControlWin::HotState; + break; + + case CBS_CHECKEDPRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::CheckedBoxType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case CBS_CHECKEDDISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::CheckedBoxType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case CBS_MIXEDNORMAL: + // Classic theme can't represent mixed state checkbox. We assume + // it's equivalent to unchecked. + BLINK_ASSERT(classicState == DFCS_BUTTONCHECK); + ctype = WebTestThemeControlWin::IndeterminateCheckboxType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case CBS_MIXEDHOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_HOT)); + ctype = WebTestThemeControlWin::IndeterminateCheckboxType; + cstate = WebTestThemeControlWin::HotState; + break; + + case CBS_MIXEDPRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::IndeterminateCheckboxType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case CBS_MIXEDDISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::IndeterminateCheckboxType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else if (BP_RADIOBUTTON == part) { + switch (state) { + case RBS_UNCHECKEDNORMAL: + BLINK_ASSERT(classicState == DFCS_BUTTONRADIO); + ctype = WebTestThemeControlWin::UncheckedRadioType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case RBS_UNCHECKEDHOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_HOT)); + ctype = WebTestThemeControlWin::UncheckedRadioType; + cstate = WebTestThemeControlWin::HotState; + break; + + case RBS_UNCHECKEDPRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::UncheckedRadioType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case RBS_UNCHECKEDDISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::UncheckedRadioType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case RBS_CHECKEDNORMAL: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED)); + ctype = WebTestThemeControlWin::CheckedRadioType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case RBS_CHECKEDHOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_HOT)); + ctype = WebTestThemeControlWin::CheckedRadioType; + cstate = WebTestThemeControlWin::HotState; + break; + + case RBS_CHECKEDPRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::CheckedRadioType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case RBS_CHECKEDDISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::CheckedRadioType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else if (BP_PUSHBUTTON == part) { + switch (state) { + case PBS_NORMAL: + BLINK_ASSERT(classicState == DFCS_BUTTONPUSH); + ctype = WebTestThemeControlWin::PushButtonType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case PBS_HOT: + BLINK_ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_HOT)); + ctype = WebTestThemeControlWin::PushButtonType; + cstate = WebTestThemeControlWin::HotState; + break; + + case PBS_PRESSED: + BLINK_ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_PUSHED)); + ctype = WebTestThemeControlWin::PushButtonType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case PBS_DISABLED: + BLINK_ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::PushButtonType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case PBS_DEFAULTED: + BLINK_ASSERT(classicState == DFCS_BUTTONPUSH); + ctype = WebTestThemeControlWin::PushButtonType; + cstate = WebTestThemeControlWin::FocusedState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else + BLINK_ASSERT_NOT_REACHED(); + + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintMenuList(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + if (CP_DROPDOWNBUTTON == part) { + ctype = WebTestThemeControlWin::DropDownButtonType; + switch (state) { + case CBXS_NORMAL: + BLINK_ASSERT(classicState == DFCS_MENUARROW); + cstate = WebTestThemeControlWin::NormalState; + break; + + case CBXS_HOT: + BLINK_ASSERT(classicState == (DFCS_MENUARROW | DFCS_HOT)); + cstate = WebTestThemeControlWin::HoverState; + break; + + case CBXS_PRESSED: + BLINK_ASSERT(classicState == (DFCS_MENUARROW | DFCS_PUSHED)); + cstate = WebTestThemeControlWin::PressedState; + break; + + case CBXS_DISABLED: + BLINK_ASSERT(classicState == (DFCS_MENUARROW | DFCS_INACTIVE)); + cstate = WebTestThemeControlWin::DisabledState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else + BLINK_ASSERT_NOT_REACHED(); + + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintScrollbarArrow(WebCanvas* canvas, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + switch (state) { + case ABS_UPNORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLUP); + ctype = WebTestThemeControlWin::UpArrowType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case ABS_DOWNNORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLDOWN); + ctype = WebTestThemeControlWin::DownArrowType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case ABS_LEFTNORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLLEFT); + ctype = WebTestThemeControlWin::LeftArrowType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case ABS_RIGHTNORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLRIGHT); + ctype = WebTestThemeControlWin::RightArrowType; + cstate = WebTestThemeControlWin::NormalState; + break; + + case ABS_UPHOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_HOT)); + ctype = WebTestThemeControlWin::UpArrowType; + cstate = WebTestThemeControlWin::HotState; + break; + + case ABS_DOWNHOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_HOT)); + ctype = WebTestThemeControlWin::DownArrowType; + cstate = WebTestThemeControlWin::HotState; + break; + + case ABS_LEFTHOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_HOT)); + ctype = WebTestThemeControlWin::LeftArrowType; + cstate = WebTestThemeControlWin::HotState; + break; + + case ABS_RIGHTHOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_HOT)); + ctype = WebTestThemeControlWin::RightArrowType; + cstate = WebTestThemeControlWin::HotState; + break; + + case ABS_UPHOVER: + BLINK_ASSERT(classicState == DFCS_SCROLLUP); + ctype = WebTestThemeControlWin::UpArrowType; + cstate = WebTestThemeControlWin::HoverState; + break; + + case ABS_DOWNHOVER: + BLINK_ASSERT(classicState == DFCS_SCROLLDOWN); + ctype = WebTestThemeControlWin::DownArrowType; + cstate = WebTestThemeControlWin::HoverState; + break; + + case ABS_LEFTHOVER: + BLINK_ASSERT(classicState == DFCS_SCROLLLEFT); + ctype = WebTestThemeControlWin::LeftArrowType; + cstate = WebTestThemeControlWin::HoverState; + break; + + case ABS_RIGHTHOVER: + BLINK_ASSERT(classicState == DFCS_SCROLLRIGHT); + ctype = WebTestThemeControlWin::RightArrowType; + cstate = WebTestThemeControlWin::HoverState; + break; + + case ABS_UPPRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebTestThemeControlWin::UpArrowType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case ABS_DOWNPRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebTestThemeControlWin::DownArrowType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case ABS_LEFTPRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebTestThemeControlWin::LeftArrowType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case ABS_RIGHTPRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebTestThemeControlWin::RightArrowType; + cstate = WebTestThemeControlWin::PressedState; + break; + + case ABS_UPDISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::UpArrowType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case ABS_DOWNDISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::DownArrowType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case ABS_LEFTDISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::LeftArrowType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + case ABS_RIGHTDISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_INACTIVE)); + ctype = WebTestThemeControlWin::RightArrowType; + cstate = WebTestThemeControlWin::DisabledState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintScrollbarThumb(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + switch (part) { + case SBP_THUMBBTNHORZ: + ctype = WebTestThemeControlWin::HorizontalScrollThumbType; + break; + + case SBP_THUMBBTNVERT: + ctype = WebTestThemeControlWin::VerticalScrollThumbType; + break; + + case SBP_GRIPPERHORZ: + ctype = WebTestThemeControlWin::HorizontalScrollGripType; + break; + + case SBP_GRIPPERVERT: + ctype = WebTestThemeControlWin::VerticalScrollGripType; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + switch (state) { + case SCRBS_NORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + break; + + case SCRBS_HOT: + BLINK_ASSERT(classicState == DFCS_HOT); + cstate = WebTestThemeControlWin::HotState; + break; + + case SCRBS_HOVER: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::HoverState; + break; + + case SCRBS_PRESSED: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::PressedState; + break; + + case SCRBS_DISABLED: + BLINK_ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintScrollbarTrack(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect, const WebRect& alignRect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + switch (part) { + case SBP_UPPERTRACKHORZ: + ctype = WebTestThemeControlWin::HorizontalScrollTrackBackType; + break; + + case SBP_LOWERTRACKHORZ: + ctype = WebTestThemeControlWin::HorizontalScrollTrackForwardType; + break; + + case SBP_UPPERTRACKVERT: + ctype = WebTestThemeControlWin::VerticalScrollTrackBackType; + break; + + case SBP_LOWERTRACKVERT: + ctype = WebTestThemeControlWin::VerticalScrollTrackForwardType; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + switch (state) { + case SCRBS_NORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + break; + + case SCRBS_HOT: + BLINK_ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + case SCRBS_HOVER: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::HoverState; + break; + + case SCRBS_PRESSED: + BLINK_ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + case SCRBS_DISABLED: + BLINK_ASSERT(classicState == DFCS_INACTIVE); + cstate = WebTestThemeControlWin::DisabledState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintSpinButton(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + if (part == SPNP_UP) { + ctype = WebTestThemeControlWin::UpArrowType; + switch (state) { + case UPS_NORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLUP); + cstate = WebTestThemeControlWin::NormalState; + break; + case UPS_DISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_INACTIVE)); + cstate = WebTestThemeControlWin::DisabledState; + break; + case UPS_PRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_PUSHED)); + cstate = WebTestThemeControlWin::PressedState; + break; + case UPS_HOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLUP | DFCS_HOT)); + cstate = WebTestThemeControlWin::HoverState; + break; + default: + BLINK_ASSERT_NOT_REACHED(); + } + } else if (part == SPNP_DOWN) { + ctype = WebTestThemeControlWin::DownArrowType; + switch (state) { + case DNS_NORMAL: + BLINK_ASSERT(classicState == DFCS_SCROLLDOWN); + cstate = WebTestThemeControlWin::NormalState; + break; + case DNS_DISABLED: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_INACTIVE)); + cstate = WebTestThemeControlWin::DisabledState; + break; + case DNS_PRESSED: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_PUSHED)); + cstate = WebTestThemeControlWin::PressedState; + break; + case DNS_HOT: + BLINK_ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_HOT)); + cstate = WebTestThemeControlWin::HoverState; + break; + default: + BLINK_ASSERT_NOT_REACHED(); + } + } else + BLINK_ASSERT_NOT_REACHED(); + drawControl(canvas, rect, ctype, cstate); +} + +void WebTestThemeEngineWin::paintTextField(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect, WebColor color, bool fillContentArea, bool drawEdges) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + BLINK_ASSERT(EP_EDITTEXT == part); + ctype = WebTestThemeControlWin::TextFieldType; + + switch (state) { + case ETS_NORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + break; + + case ETS_HOT: + BLINK_ASSERT(classicState == DFCS_HOT); + cstate = WebTestThemeControlWin::HotState; + break; + + case ETS_DISABLED: + BLINK_ASSERT(classicState == DFCS_INACTIVE); + cstate = WebTestThemeControlWin::DisabledState; + break; + + case ETS_SELECTED: + BLINK_ASSERT(classicState == DFCS_PUSHED); + cstate = WebTestThemeControlWin::PressedState; + break; + + case ETS_FOCUSED: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::FocusedState; + break; + + case ETS_READONLY: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::ReadOnlyState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + + drawTextField(canvas, rect, ctype, cstate, drawEdges, fillContentArea, color); +} + +void WebTestThemeEngineWin::paintTrackbar(WebCanvas* canvas, int part, int state, int classicState, const WebRect& rect) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::UnknownType; + WebTestThemeControlWin::State cstate = WebTestThemeControlWin::UnknownState; + + if (TKP_THUMBBOTTOM == part) { + ctype = WebTestThemeControlWin::HorizontalSliderThumbType; + switch (state) { + case TUS_NORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + break; + + case TUS_HOT: + BLINK_ASSERT(classicState == DFCS_HOT); + cstate = WebTestThemeControlWin::HotState; + break; + + case TUS_DISABLED: + BLINK_ASSERT(classicState == DFCS_INACTIVE); + cstate = WebTestThemeControlWin::DisabledState; + break; + + case TUS_PRESSED: + BLINK_ASSERT(classicState == DFCS_PUSHED); + cstate = WebTestThemeControlWin::PressedState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else if (TKP_THUMBVERT == part) { + ctype = WebTestThemeControlWin::VerticalSliderThumbType; + switch (state) { + case TUS_NORMAL: + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + break; + + case TUS_HOT: + BLINK_ASSERT(classicState == DFCS_HOT); + cstate = WebTestThemeControlWin::HotState; + break; + + case TUS_DISABLED: + BLINK_ASSERT(classicState == DFCS_INACTIVE); + cstate = WebTestThemeControlWin::DisabledState; + break; + + case TUS_PRESSED: + BLINK_ASSERT(classicState == DFCS_PUSHED); + cstate = WebTestThemeControlWin::PressedState; + break; + + default: + BLINK_ASSERT_NOT_REACHED(); + break; + } + } else if (TKP_TRACK == part) { + ctype = WebTestThemeControlWin::HorizontalSliderTrackType; + BLINK_ASSERT(state == TRS_NORMAL); + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + } else if (TKP_TRACKVERT == part) { + ctype = WebTestThemeControlWin::VerticalSliderTrackType; + BLINK_ASSERT(state == TRVS_NORMAL); + BLINK_ASSERT(classicState == dfcsNormal); + cstate = WebTestThemeControlWin::NormalState; + } else + BLINK_ASSERT_NOT_REACHED(); + + drawControl(canvas, rect, ctype, cstate); +} + + +void WebTestThemeEngineWin::paintProgressBar(blink::WebCanvas* canvas, const blink::WebRect& barRect, const blink::WebRect& valueRect, bool determinate, double) +{ + WebTestThemeControlWin::Type ctype = WebTestThemeControlWin::ProgressBarType; + WebTestThemeControlWin::State cstate = determinate ? WebTestThemeControlWin::NormalState : WebTestThemeControlWin::IndeterminateState; + drawProgressBar(canvas, ctype, cstate, barRect, valueRect); +} + + +blink::WebSize WebTestThemeEngineWin::getSize(int part) +{ + return blink::WebSize(); +} + +} diff --git a/content/shell/renderer/test_runner/WebTestThemeEngineWin.h b/content/shell/renderer/test_runner/WebTestThemeEngineWin.h new file mode 100644 index 0000000..808af68 --- /dev/null +++ b/content/shell/renderer/test_runner/WebTestThemeEngineWin.h @@ -0,0 +1,78 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This implements the WebThemeEngine API used by the Windows version of +// Chromium to render native form controls like checkboxes, radio buttons, +// and scroll bars. +// The normal implementation (native_theme) renders the controls using either +// the UXTheme theming engine present in XP, Vista, and Win 7, or the "classic" +// theme used if that theme is selected in the Desktop settings. +// Unfortunately, both of these themes render controls differently on the +// different versions of Windows. +// +// In order to ensure maximum consistency of baselines across the different +// Windows versions, we provide a simple implementation for DRT here +// instead. These controls are actually platform-independent (they're rendered +// using Skia) and could be used on Linux and the Mac as well, should we +// choose to do so at some point. +// + +#ifndef WebTestThemeEngineWin_h +#define WebTestThemeEngineWin_h + +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/win/WebThemeEngine.h" + +namespace WebTestRunner { + +class WebTestThemeEngineWin : public blink::WebThemeEngine, public blink::WebNonCopyable { +public: + WebTestThemeEngineWin() { } + virtual ~WebTestThemeEngineWin() { } + + // WebThemeEngine methods: + virtual void paintButton( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&); + + virtual void paintMenuList( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&); + + virtual void paintScrollbarArrow( + blink::WebCanvas*, int state, int classicState, + const blink::WebRect&); + + virtual void paintScrollbarThumb( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&); + + virtual void paintScrollbarTrack( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&, const blink::WebRect& alignRect); + + virtual void paintSpinButton( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&); + + virtual void paintTextField( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&, blink::WebColor, bool fillContentArea, + bool drawEdges); + + virtual void paintTrackbar( + blink::WebCanvas*, int part, int state, int classicState, + const blink::WebRect&); + + virtual void paintProgressBar( + blink::WebCanvas*, const blink::WebRect& barRect, + const blink::WebRect& valueRect, + bool determinate, double time); + + virtual blink::WebSize getSize(int part); +}; + +} + +#endif // WebTestThemeEngineWin_h diff --git a/content/shell/renderer/test_runner/WebUserMediaClientMock.cpp b/content/shell/renderer/test_runner/WebUserMediaClientMock.cpp new file mode 100644 index 0000000..36950a0 --- /dev/null +++ b/content/shell/renderer/test_runner/WebUserMediaClientMock.cpp @@ -0,0 +1,141 @@ +// Copyright 2013 The Chromium 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 "content/shell/renderer/test_runner/WebUserMediaClientMock.h" + +#include "content/shell/renderer/test_runner/MockConstraints.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "third_party/WebKit/public/platform/WebMediaConstraints.h" +#include "third_party/WebKit/public/platform/WebMediaStream.h" +#include "third_party/WebKit/public/platform/WebMediaStreamSource.h" +#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" +#include "third_party/WebKit/public/web/WebUserMediaRequest.h" + +using namespace blink; + +namespace WebTestRunner { + +class UserMediaRequestTask : public WebMethodTask<WebUserMediaClientMock> { +public: + UserMediaRequestTask(WebUserMediaClientMock* object, const WebUserMediaRequest& request, const WebMediaStream result) + : WebMethodTask<WebUserMediaClientMock>(object) + , m_request(request) + , m_result(result) + { + BLINK_ASSERT(!m_result.isNull()); + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestSucceeded(m_result); + } + +private: + WebUserMediaRequest m_request; + WebMediaStream m_result; +}; + +class UserMediaRequestConstraintFailedTask : public WebMethodTask<WebUserMediaClientMock> { +public: + UserMediaRequestConstraintFailedTask(WebUserMediaClientMock* object, const WebUserMediaRequest& request, const WebString& constraint) + : WebMethodTask<WebUserMediaClientMock>(object) + , m_request(request) + , m_constraint(constraint) + { + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestFailedConstraint(m_constraint); + } + +private: + WebUserMediaRequest m_request; + WebString m_constraint; +}; + +class UserMediaRequestPermissionDeniedTask : public WebMethodTask<WebUserMediaClientMock> { +public: + UserMediaRequestPermissionDeniedTask(WebUserMediaClientMock* object, const WebUserMediaRequest& request) + : WebMethodTask<WebUserMediaClientMock>(object) + , m_request(request) + { + } + + virtual void runIfValid() OVERRIDE + { + m_request.requestFailed(); + } + +private: + WebUserMediaRequest m_request; +}; + +//////////////////////////////// + +class MockExtraData : public WebMediaStream::ExtraData { +public: + int foo; +}; + +WebUserMediaClientMock::WebUserMediaClientMock(WebTestDelegate* delegate) + : m_delegate(delegate) +{ +} + +void WebUserMediaClientMock::requestUserMedia(const WebUserMediaRequest& streamRequest) +{ + BLINK_ASSERT(!streamRequest.isNull()); + WebUserMediaRequest request = streamRequest; + + if (request.ownerDocument().isNull() || !request.ownerDocument().frame()) { + m_delegate->postTask(new UserMediaRequestPermissionDeniedTask(this, request)); + return; + } + + WebMediaConstraints constraints = request.audioConstraints(); + WebString failedConstraint; + if (!constraints.isNull() && !MockConstraints::verifyConstraints(constraints, &failedConstraint)) { + m_delegate->postTask(new UserMediaRequestConstraintFailedTask(this, request, failedConstraint)); + return; + } + constraints = request.videoConstraints(); + if (!constraints.isNull() && !MockConstraints::verifyConstraints(constraints, &failedConstraint)) { + m_delegate->postTask(new UserMediaRequestConstraintFailedTask(this, request, failedConstraint)); + return; + } + + const size_t zero = 0; + const size_t one = 1; + WebVector<WebMediaStreamTrack> audioTracks(request.audio() ? one : zero); + WebVector<WebMediaStreamTrack> videoTracks(request.video() ? one : zero); + + if (request.audio()) { + WebMediaStreamSource source; + source.initialize("MockAudioDevice#1", WebMediaStreamSource::TypeAudio, "Mock audio device"); + audioTracks[0].initialize(source); + } + + if (request.video()) { + WebMediaStreamSource source; + source.initialize("MockVideoDevice#1", WebMediaStreamSource::TypeVideo, "Mock video device"); + videoTracks[0].initialize(source); + } + + WebMediaStream stream; + stream.initialize(audioTracks, videoTracks); + + stream.setExtraData(new MockExtraData()); + + m_delegate->postTask(new UserMediaRequestTask(this, request, stream)); +} + +void WebUserMediaClientMock::cancelUserMediaRequest(const WebUserMediaRequest&) +{ +} + +} diff --git a/content/shell/renderer/test_runner/WebUserMediaClientMock.h b/content/shell/renderer/test_runner/WebUserMediaClientMock.h new file mode 100644 index 0000000..37a4bd9 --- /dev/null +++ b/content/shell/renderer/test_runner/WebUserMediaClientMock.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebUserMediaClientMock_h +#define WebUserMediaClientMock_h + +#include "content/shell/renderer/test_runner/TestCommon.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "third_party/WebKit/public/platform/WebCommon.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebUserMediaClient.h" + +namespace WebTestRunner { + +class WebTestDelegate; + +class WebUserMediaClientMock : public blink::WebUserMediaClient, public blink::WebNonCopyable { +public: + explicit WebUserMediaClientMock(WebTestDelegate*); + ~WebUserMediaClientMock() { } + + virtual void requestUserMedia(const blink::WebUserMediaRequest&) OVERRIDE; + virtual void cancelUserMediaRequest(const blink::WebUserMediaRequest&) OVERRIDE; + + // Task related methods + WebTaskList* taskList() { return &m_taskList; } + +private: + WebTaskList m_taskList; + WebTestDelegate* m_delegate; +}; + +} + +#endif // WebUserMediaClientMock_h diff --git a/content/shell/renderer/webkit_test_runner.cc b/content/shell/renderer/webkit_test_runner.cc index e9b96ae..666cd53 100644 --- a/content/shell/renderer/webkit_test_runner.cc +++ b/content/shell/renderer/webkit_test_runner.cc @@ -29,6 +29,10 @@ #include "content/shell/common/shell_messages.h" #include "content/shell/common/webkit_test_helpers.h" #include "content/shell/renderer/shell_render_process_observer.h" +#include "content/shell/renderer/test_runner/WebTask.h" +#include "content/shell/renderer/test_runner/WebTestInterfaces.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" +#include "content/shell/renderer/test_runner/WebTestRunner.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "skia/ext/platform_canvas.h" @@ -42,10 +46,6 @@ #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" -#include "third_party/WebKit/public/testing/WebTask.h" -#include "third_party/WebKit/public/testing/WebTestInterfaces.h" -#include "third_party/WebKit/public/testing/WebTestProxy.h" -#include "third_party/WebKit/public/testing/WebTestRunner.h" #include "third_party/WebKit/public/web/WebArrayBufferView.h" #include "third_party/WebKit/public/web/WebContextMenuData.h" #include "third_party/WebKit/public/web/WebDataSource.h" diff --git a/content/shell/renderer/webkit_test_runner.h b/content/shell/renderer/webkit_test_runner.h index 4b12643..4b419ee 100644 --- a/content/shell/renderer/webkit_test_runner.h +++ b/content/shell/renderer/webkit_test_runner.h @@ -13,8 +13,8 @@ #include "content/public/renderer/render_view_observer.h" #include "content/public/renderer/render_view_observer_tracker.h" #include "content/shell/common/shell_test_configuration.h" -#include "third_party/WebKit/public/testing/WebPreferences.h" -#include "third_party/WebKit/public/testing/WebTestDelegate.h" +#include "content/shell/common/test_runner/WebPreferences.h" +#include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "v8/include/v8.h" class SkCanvas; diff --git a/content/test/DEPS b/content/test/DEPS index 6e063e8..fb2a8f8 100644 --- a/content/test/DEPS +++ b/content/test/DEPS @@ -3,7 +3,6 @@ include_rules = [ "+content", "+media/audio", # For AudioParameters in WebRTC tests. "+media/base", # For ChannelLayout in WebRTC tests. - "+third_party/WebKit/public/testing", "+ui/base/resource/data_pack.h", "+ui/base/resource/resource_bundle.h", "!v8/include/v8.h", diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc index 97eb7c4..125d2a2 100644 --- a/content/test/layouttest_support.cc +++ b/content/test/layouttest_support.cc @@ -11,12 +11,12 @@ #include "content/renderer/render_thread_impl.h" #include "content/renderer/render_view_impl.h" #include "content/renderer/renderer_webkitplatformsupport_impl.h" +#include "content/shell/renderer/test_runner/WebFrameTestProxy.h" +#include "content/shell/renderer/test_runner/WebTestProxy.h" #include "content/test/test_media_stream_client.h" #include "third_party/WebKit/public/platform/WebDeviceMotionData.h" #include "third_party/WebKit/public/platform/WebDeviceOrientationData.h" #include "third_party/WebKit/public/platform/WebGamepads.h" -#include "third_party/WebKit/public/testing/WebFrameTestProxy.h" -#include "third_party/WebKit/public/testing/WebTestProxy.h" #if defined(OS_WIN) && !defined(USE_AURA) #include "content/browser/web_contents/web_contents_drag_win.h" |