diff options
author | enne@chromium.org <enne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 22:53:46 +0000 |
---|---|---|
committer | enne@chromium.org <enne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-25 22:53:46 +0000 |
commit | f4009be83d39141213e67db11c185b6b4ea79890 (patch) | |
tree | 73d3dc56a3d845d8040b456e5004fb80146254fc | |
parent | eb9342fb26b9b957f46d88d783ad9ef9432dca0a (diff) | |
download | chromium_src-f4009be83d39141213e67db11c185b6b4ea79890.zip chromium_src-f4009be83d39141213e67db11c185b6b4ea79890.tar.gz chromium_src-f4009be83d39141213e67db11c185b6b4ea79890.tar.bz2 |
Revert of Reland "Move EventSender from CppBoundClass to gin::Wrappable." (https://codereview.chromium.org/210833003/)
Reason for revert:
Causes fast/dom/Window/property* tests to fail flakily.
http://test-results.appspot.com/dashboards/flakiness_dashboard.html#group=%40ToT%20Blink&showExpectations=true&tests=fast%2Fdom%2FWindow%2Fproperty-access-on-cached-properties-after-frame-removed-and-gced.html%2Cfast%2Fdom%2FWindow%2Fproperty-access-on-cached-properties-after-frame-navigated.html%2Cfast%2Fdom%2FWindow%2Fproperty-access-on-cached-window-after-frame-navigated.html%2Cfast%2Fdom%2FWindow%2Fproperty-access-on-cached-window-after-frame-removed.html%2Cfast%2Fdom%2FWindow%2Fproperty-access-on-cached-window-after-frame-removed-and-gced.html%2Cfast%2Fdom%2FWindow%2Fproperty-access-on-cached-properties-after-frame-removed.html
These tests now all fail with additional lines like:
FAIL window.cached_eventSender.dragMode should be false. Was true.
FAIL window.cached_eventSender.forceLayoutOnEvents should be false. Was true.
Original issue's description:
> Reland "Move EventSender from CppBoundClass to gin::Wrappable."
>
> (patch from issue 199793025 by tfarina@chromium.org)
>
> including the fix https://codereview.chromium.org/210773002/
>
> BUG=297480, 331301
> COLLABORATOR=tfarina@chromium.org
> R=jochen@chromium.org
> TBR=jochen
>
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=259226
TBR=jochen@chromium.org,tfarina@chromium.org,hajimehoshi@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=297480, 331301
Review URL: https://codereview.chromium.org/211863002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259377 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/content_shell.gypi | 8 | ||||
-rw-r--r-- | content/renderer/pepper/event_conversion.cc | 4 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/CppBoundClass.cpp | 367 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/CppBoundClass.h | 253 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/CppVariant.cpp | 296 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/CppVariant.h | 119 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/EventSender.cpp | 1467 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/EventSender.h | 188 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/TestInterfaces.cpp | 18 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/TestInterfaces.h | 6 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/WebTestProxy.cpp | 6 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/event_sender.cc | 2100 | ||||
-rw-r--r-- | content/shell/renderer/test_runner/event_sender.h | 276 |
13 files changed, 2713 insertions, 2395 deletions
diff --git a/content/content_shell.gypi b/content/content_shell.gypi index a665471..9290d49 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi @@ -173,6 +173,12 @@ '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/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/KeyCodeMapping.cpp', 'shell/renderer/test_runner/KeyCodeMapping.h', 'shell/renderer/test_runner/MockColorChooser.cpp', @@ -226,8 +232,6 @@ 'shell/renderer/test_runner/WebUserMediaClientMock.h', 'shell/renderer/test_runner/accessibility_controller.cc', 'shell/renderer/test_runner/accessibility_controller.h', - 'shell/renderer/test_runner/event_sender.cc', - 'shell/renderer/test_runner/event_sender.h', 'shell/renderer/test_runner/gamepad_controller.cc', 'shell/renderer/test_runner/gamepad_controller.h', 'shell/renderer/test_runner/notification_presenter.cc', diff --git a/content/renderer/pepper/event_conversion.cc b/content/renderer/pepper/event_conversion.cc index 0243434..52d382a 100644 --- a/content/renderer/pepper/event_conversion.cc +++ b/content/renderer/pepper/event_conversion.cc @@ -459,7 +459,7 @@ WebMouseWheelEvent* BuildMouseWheelEvent(const InputEventData& event) { #endif // Convert a character string to a Windows virtual key code. Adapted from -// src/content/shell/renderer/test_runner/event_sender.cc. This +// src/third_party/WebKit/Tools/DumpRenderTree/chromium/EventSender.cpp. This // is used by CreateSimulatedWebInputEvents to convert keyboard events. void GetKeyCode(const std::string& char_text, WebUChar* code, @@ -606,7 +606,7 @@ WebInputEvent* CreateWebInputEvent(const InputEventData& event) { } // Generate a coherent sequence of input events to simulate a user event. -// From src/content/shell/renderer/test_runner/event_sender.cc. +// From src/third_party/WebKit/Tools/DumpRenderTree/chromium/EventSender.cpp. std::vector<linked_ptr<WebInputEvent> > CreateSimulatedWebInputEvents( const ppapi::InputEventData& event, int plugin_x, diff --git a/content/shell/renderer/test_runner/CppBoundClass.cpp b/content/shell/renderer/test_runner/CppBoundClass.cpp new file mode 100644 index 0000000..943dd92 --- /dev/null +++ b/content/shell/renderer/test_runner/CppBoundClass.cpp @@ -0,0 +1,367 @@ +// Copyright 2014 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) OVERRIDE + { + value->set(*m_value); + return true; + } + + virtual bool setValue(const CppVariant& value) OVERRIDE + { + m_value->set(value); + return true; + } + +private: + CppVariant* m_value; +}; + +class GetterPropertyCallback : public CppBoundClass::PropertyCallback { +public: + GetterPropertyCallback(scoped_ptr<CppBoundClass::GetterCallback> callback) + : m_callback(callback.Pass()) + { + } + + virtual bool getValue(CppVariant* value) OVERRIDE + { + m_callback->run(value); + return true; + } + + virtual bool setValue(const CppVariant& value) OVERRIDE { return false; } + +private: + scoped_ptr<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() + : m_boundToFrame(false) +{ +} + +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, scoped_ptr<GetterCallback> callback) +{ + PropertyCallback* propertyCallback = callback.get() ? new GetterPropertyCallback(callback.Pass()) : 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..23109f4 --- /dev/null +++ b/content/shell/renderer/test_runner/CppBoundClass.h @@ -0,0 +1,253 @@ +// Copyright 2014 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 CONTENT_SHELL_RENDERER_TEST_RUNNER_CPPBOUNDCLASS_H_ +#define CONTENT_SHELL_RENDERER_TEST_RUNNER_CPPBOUNDCLASS_H_ + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/shell/renderer/test_runner/CppVariant.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: + 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(); + 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&, scoped_ptr<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*)) + { + scoped_ptr<GetterCallback> callback(new MemberGetterCallback<T>(static_cast<T*>(this), method)); + bindGetterCallback(name, callback.Pass()); + } + + // 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(scoped_ptr<Callback> fallbackCallback) + { + m_fallbackCallback = fallbackCallback.Pass(); + } + + // 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(scoped_ptr<Callback>(new MemberCallback<T>(static_cast<T*>(this), method)).Pass()); + else + bindFallbackCallback(scoped_ptr<Callback>().Pass()); + } + + // 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. + scoped_ptr<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; + + DISALLOW_COPY_AND_ASSIGN(CppBoundClass); +}; + +} + +#endif // CONTENT_SHELL_RENDERER_TEST_RUNNER_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..2b08e1e --- /dev/null +++ b/content/shell/renderer/test_runner/CppVariant.cpp @@ -0,0 +1,296 @@ +// Copyright 2014 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..572c060 --- /dev/null +++ b/content/shell/renderer/test_runner/CppVariant.h @@ -0,0 +1,119 @@ +// Copyright 2014 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 CONTENT_SHELL_RENDERER_TEST_RUNNER_CPPVARIANT_H_ +#define CONTENT_SHELL_RENDERER_TEST_RUNNER_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 // CONTENT_SHELL_RENDERER_TEST_RUNNER_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..369f31f --- /dev/null +++ b/content/shell/renderer/test_runner/EventSender.cpp @@ -0,0 +1,1467 @@ +// Copyright 2014 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. + int modifiers; + + SavedEvent() + : type(Unspecified) + , buttonType(WebMouseEvent::ButtonNone) + , milliseconds(0) + , modifiers(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, int modifiers) +{ + e->type = t; + e->button = b; + e->modifiers = modifiers; + e->x = pos.x; + e->y = pos.y; + e->globalX = pos.x; + e->globalY = pos.y; + e->timeStampSeconds = ts; + e->clickCount = clickCount; +} + +int getKeyModifier(const string& modifierName) +{ + const char* characters = modifierName.c_str(); + if (!strcmp(characters, "ctrlKey") +#ifndef __APPLE__ + || !strcmp(characters, "addSelectionKey") +#endif + ) { + return WebInputEvent::ControlKey; + } else if (!strcmp(characters, "shiftKey") || !strcmp(characters, "rangeSelectionKey")) { + return WebInputEvent::ShiftKey; + } else if (!strcmp(characters, "altKey")) { + return WebInputEvent::AltKey; +#ifdef __APPLE__ + } else if (!strcmp(characters, "metaKey") || !strcmp(characters, "addSelectionKey")) { + return WebInputEvent::MetaKey; +#else + } else if (!strcmp(characters, "metaKey")) { + return WebInputEvent::MetaKey; +#endif + } else if (!strcmp(characters, "autoRepeat")) { + return WebInputEvent::IsAutoRepeat; + } else if (!strcmp(characters, "copyKey")) { +#ifdef __APPLE__ + return WebInputEvent::AltKey; +#else + return WebInputEvent::ControlKey; +#endif + } + + return 0; +} + +int getKeyModifiers(const CppVariant* argument) +{ + int modifiers = 0; + if (argument->isObject()) { + vector<string> modifierNames = argument->toStringVector(); + for (vector<string>::const_iterator i = modifierNames.begin(); i != modifierNames.end(); ++i) + modifiers |= getKeyModifier(*i); + } else if (argument->isString()) { + modifiers |= getKeyModifier(argument->toString()); + } + return modifiers; +} + +// 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 = scoped_ptr<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), 0); + 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; + int modifiers = 0; + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + modifiers = getKeyModifiers(&(arguments[1])); + initMouseEvent(WebInputEvent::MouseDown, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate), modifiers); + 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); + + int modifiers = 0; + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + modifiers = getKeyModifiers(&(arguments[1])); + + if (isDragMode() && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseUp; + savedEvent.buttonType = buttonType; + savedEvent.modifiers = modifiers; + mouseEventQueue.push_back(savedEvent); + replaySavedEvents(); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseUp, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate), modifiers); + 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) { + // Specifically pass any keyboard modifiers to the drop + // method. This allows tests to control the drop type + // (i.e. copy or move). + webview()->dragTargetDrop(clientPoint, screenPoint, e.modifiers); + } 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()); + + int modifiers = 0; + if (arguments.size() >= 3 && (arguments[2].isObject() || arguments[2].isString())) + modifiers = getKeyModifiers(&(arguments[2])); + + if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseMove; + savedEvent.pos = mousePos; + savedEvent.modifiers = modifiers; + mouseEventQueue.push_back(savedEvent); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseMove, pressedButton, mousePos, &event, getCurrentEventTimeSec(m_delegate), modifiers); + 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())) { + eventDown.modifiers = getKeyModifiers(&(arguments[1])); +#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), 0); + 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), e.modifiers); + 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), e.modifiers); + 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), 0); + webview()->handleInputEvent(event); + +#ifdef WIN32 + initMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, lastMousePos, &event, getCurrentEventTimeSec(m_delegate), 0); + 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() OVERRIDE { 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() OVERRIDE { 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() OVERRIDE { 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.x = arguments[0].toInt32(); + touchPoint.position.y = 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()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateMoved; + touchPoint->position.x = arguments[1].toInt32(); + touchPoint->position.y = arguments[2].toInt32(); + touchPoint->screenPosition = touchPoint->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), 0); + 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), 0); + 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), 0); + 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), 0); + 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; + int modifiers = 0; + + if (arguments.size() > 2 && arguments[2].isBool()) + paged = arguments[2].toBoolean(); + + if (arguments.size() > 3 && arguments[3].isBool()) + hasPreciseScrollingDeltas = arguments[3].toBoolean(); + + if (arguments.size() > 4 && (arguments[4].isObject() || arguments[4].isString())) + modifiers = getKeyModifiers(&(arguments[4])); + + initMouseEvent(WebInputEvent::MouseWheel, pressedButton, lastMousePos, event, getCurrentEventTimeSec(m_delegate), modifiers); + 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), 0); + 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..75fad30 --- /dev/null +++ b/content/shell/renderer/test_runner/EventSender.h @@ -0,0 +1,188 @@ +// Copyright 2014 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 CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENTSENDER_H_ +#define CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENTSENDER_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/shell/renderer/test_runner/CppBoundClass.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*); + virtual ~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; + + scoped_ptr<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 // CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENTSENDER_H_ diff --git a/content/shell/renderer/test_runner/TestInterfaces.cpp b/content/shell/renderer/test_runner/TestInterfaces.cpp index c7c4d2d..5e08ac3 100644 --- a/content/shell/renderer/test_runner/TestInterfaces.cpp +++ b/content/shell/renderer/test_runner/TestInterfaces.cpp @@ -7,9 +7,9 @@ #include <string> #include "base/strings/stringprintf.h" +#include "content/shell/renderer/test_runner/EventSender.h" #include "content/shell/renderer/test_runner/WebTestProxy.h" #include "content/shell/renderer/test_runner/accessibility_controller.h" -#include "content/shell/renderer/test_runner/event_sender.h" #include "content/shell/renderer/test_runner/gamepad_controller.h" #include "content/shell/renderer/test_runner/text_input_controller.h" #include "content/shell/renderer/test_runner/test_runner.h" @@ -27,7 +27,7 @@ namespace WebTestRunner { TestInterfaces::TestInterfaces() : m_accessibilityController(new content::AccessibilityController()) - , m_eventSender(new content::EventSender(this)) + , m_eventSender(new EventSender(this)) , m_gamepadController(new content::GamepadController()) , m_textInputController(new content::TextInputController()) , m_testRunner(new content::TestRunner(this)) @@ -44,13 +44,13 @@ TestInterfaces::TestInterfaces() TestInterfaces::~TestInterfaces() { m_accessibilityController->SetWebView(0); - m_eventSender->SetWebView(0); + m_eventSender->setWebView(0); // m_gamepadController doesn't depend on WebView. m_textInputController->SetWebView(NULL); m_testRunner->SetWebView(0, 0); m_accessibilityController->SetDelegate(0); - m_eventSender->SetDelegate(0); + m_eventSender->setDelegate(0); m_gamepadController->SetDelegate(0); // m_textInputController doesn't depend on WebTestDelegate. m_testRunner->SetDelegate(0); @@ -60,7 +60,7 @@ void TestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) { m_proxy = proxy; m_accessibilityController->SetWebView(webView); - m_eventSender->SetWebView(webView); + m_eventSender->setWebView(webView); // m_gamepadController doesn't depend on WebView. m_textInputController->SetWebView(webView); m_testRunner->SetWebView(webView, proxy); @@ -69,7 +69,7 @@ void TestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) void TestInterfaces::setDelegate(WebTestDelegate* delegate) { m_accessibilityController->SetDelegate(delegate); - m_eventSender->SetDelegate(delegate); + m_eventSender->setDelegate(delegate); m_gamepadController->SetDelegate(delegate); // m_textInputController doesn't depend on WebTestDelegate. m_testRunner->SetDelegate(delegate); @@ -79,7 +79,7 @@ void TestInterfaces::setDelegate(WebTestDelegate* delegate) void TestInterfaces::bindTo(WebFrame* frame) { m_accessibilityController->Install(frame); - m_eventSender->Install(frame); + m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender")); m_gamepadController->Install(frame); m_textInputController->Install(frame); m_testRunner->Install(frame); @@ -88,7 +88,7 @@ void TestInterfaces::bindTo(WebFrame* frame) void TestInterfaces::resetTestHelperControllers() { m_accessibilityController->Reset(); - m_eventSender->Reset(); + m_eventSender->reset(); m_gamepadController->Reset(); // m_textInputController doesn't have any state to reset. WebCache::clear(); @@ -157,7 +157,7 @@ content::AccessibilityController* TestInterfaces::accessibilityController() return m_accessibilityController.get(); } -content::EventSender* TestInterfaces::eventSender() +EventSender* TestInterfaces::eventSender() { return m_eventSender.get(); } diff --git a/content/shell/renderer/test_runner/TestInterfaces.h b/content/shell/renderer/test_runner/TestInterfaces.h index 4bc50d9..5e6ba1f 100644 --- a/content/shell/renderer/test_runner/TestInterfaces.h +++ b/content/shell/renderer/test_runner/TestInterfaces.h @@ -26,7 +26,6 @@ class WebView; namespace content { class AccessibilityController; -class EventSender; class GamepadController; class TestRunner; class TextInputController; @@ -34,6 +33,7 @@ class TextInputController; namespace WebTestRunner { +class EventSender; class WebTestDelegate; class WebTestProxyBase; @@ -54,7 +54,7 @@ public: void windowClosed(WebTestProxyBase*); content::AccessibilityController* accessibilityController(); - content::EventSender* eventSender(); + EventSender* eventSender(); content::TestRunner* testRunner(); WebTestDelegate* delegate(); WebTestProxyBase* proxy(); @@ -63,7 +63,7 @@ public: private: scoped_ptr<content::AccessibilityController> m_accessibilityController; - scoped_ptr<content::EventSender> m_eventSender; + scoped_ptr<EventSender> m_eventSender; scoped_ptr<content::GamepadController> m_gamepadController; scoped_ptr<content::TextInputController> m_textInputController; scoped_ptr<content::TestRunner> m_testRunner; diff --git a/content/shell/renderer/test_runner/WebTestProxy.cpp b/content/shell/renderer/test_runner/WebTestProxy.cpp index 9d454a2..0b8ddd5 100644 --- a/content/shell/renderer/test_runner/WebTestProxy.cpp +++ b/content/shell/renderer/test_runner/WebTestProxy.cpp @@ -6,7 +6,7 @@ #include <cctype> -#include "content/shell/renderer/test_runner/event_sender.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" @@ -875,7 +875,7 @@ void WebTestProxyBase::startDragging(WebFrame*, const WebDragData& data, WebDrag { // 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); + m_testInterfaces->eventSender()->doDragDrop(data, mask); } // The output from these methods in layout test mode should match that @@ -924,7 +924,7 @@ void WebTestProxyBase::didStopLoading() void WebTestProxyBase::showContextMenu(WebFrame*, const WebContextMenuData& contextMenuData) { - m_testInterfaces->eventSender()->SetContextMenuData(contextMenuData); + m_testInterfaces->eventSender()->setContextMenuData(contextMenuData); } WebUserMediaClient* WebTestProxyBase::userMediaClient() diff --git a/content/shell/renderer/test_runner/event_sender.cc b/content/shell/renderer/test_runner/event_sender.cc deleted file mode 100644 index 2964cd4..0000000 --- a/content/shell/renderer/test_runner/event_sender.cc +++ /dev/null @@ -1,2100 +0,0 @@ -// Copyright 2014 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/event_sender.h" - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "content/shell/renderer/test_runner/KeyCodeMapping.h" -#include "content/shell/renderer/test_runner/MockSpellCheck.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 "gin/handle.h" -#include "gin/object_template_builder.h" -#include "gin/wrappable.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/WebFrame.h" -#include "third_party/WebKit/public/web/WebKit.h" -#include "third_party/WebKit/public/web/WebView.h" -#include "v8/include/v8.h" - -#if defined(OS_WIN) -#include "third_party/WebKit/public/web/win/WebInputEventFactory.h" -#elif defined(OS_MACOSX) -#include "third_party/WebKit/public/web/mac/WebInputEventFactory.h" -#elif defined(OS_ANDROID) -#include "third_party/WebKit/public/web/android/WebInputEventFactory.h" -#elif defined(TOOLKIT_GTK) -#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" -#endif - -using blink::WebContextMenuData; -using blink::WebDragData; -using blink::WebDragOperationsMask; -using blink::WebFloatPoint; -using blink::WebFrame; -using blink::WebGestureEvent; -using blink::WebInputEvent; -using blink::WebKeyboardEvent; -using blink::WebMouseEvent; -using blink::WebMouseWheelEvent; -using blink::WebPoint; -using blink::WebString; -using blink::WebTouchEvent; -using blink::WebTouchPoint; -using blink::WebVector; -using blink::WebView; - -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID) || \ - defined(TOOLKIT_GTK) -using blink::WebInputEventFactory; -#endif - -namespace content { - -namespace { - -void InitMouseEvent(WebInputEvent::Type t, - WebMouseEvent::Button b, - const WebPoint& pos, - double time_stamp, - int click_count, - int modifiers, - WebMouseEvent* e) { - e->type = t; - e->button = b; - e->modifiers = modifiers; - e->x = pos.x; - e->y = pos.y; - e->globalX = pos.x; - e->globalY = pos.y; - e->timeStampSeconds = time_stamp; - e->clickCount = click_count; -} - -int GetKeyModifier(const std::string& modifier_name) { - const char* characters = modifier_name.c_str(); - if (!strcmp(characters, "ctrlKey") -#ifndef __APPLE__ - || !strcmp(characters, "addSelectionKey") -#endif - ) { - return WebInputEvent::ControlKey; - } else if (!strcmp(characters, "shiftKey") || - !strcmp(characters, "rangeSelectionKey")) { - return WebInputEvent::ShiftKey; - } else if (!strcmp(characters, "altKey")) { - return WebInputEvent::AltKey; -#ifdef __APPLE__ - } else if (!strcmp(characters, "metaKey") || - !strcmp(characters, "addSelectionKey")) { - return WebInputEvent::MetaKey; -#else - } else if (!strcmp(characters, "metaKey")) { - return WebInputEvent::MetaKey; -#endif - } else if (!strcmp(characters, "autoRepeat")) { - return WebInputEvent::IsAutoRepeat; - } else if (!strcmp(characters, "copyKey")) { -#ifdef __APPLE__ - return WebInputEvent::AltKey; -#else - return WebInputEvent::ControlKey; -#endif - } - - return 0; -} - -int GetKeyModifiers(const std::vector<std::string>& modifier_names) { - int modifiers = 0; - for (std::vector<std::string>::const_iterator it = modifier_names.begin(); - it != modifier_names.end(); ++it) { - modifiers |= GetKeyModifier(*it); - } - return modifiers; -} - -int GetKeyModifiersFromV8(v8::Handle<v8::Value> value) { - std::vector<std::string> modifier_names; - if (value->IsString()) { - modifier_names.push_back(gin::V8ToString(value)); - } else if (value->IsArray()) { - gin::Converter<std::vector<std::string> >::FromV8( - NULL, value, &modifier_names); - } - return GetKeyModifiers(modifier_names); -} - -// Maximum distance (in space and time) for a mouse click to register as a -// double or triple click. -const double kMultipleClickTimeSec = 1; -const int kMultipleClickRadiusPixels = 5; - -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)) > - kMultipleClickRadiusPixels * kMultipleClickRadiusPixels; -} - -// 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. -std::vector<std::string> MakeMenuItemStringsFor( - WebContextMenuData* context_menu, - WebTestRunner::WebTestDelegate* delegate) { - // These constants are based on Safari's context menu because tests are made - // for it. - static const char* kNonEditableMenuStrings[] = { - "Back", - "Reload Page", - "Open in Dashbaord", - "<separator>", - "View Source", - "Save Page As", - "Print Page", - "Inspect Element", - 0 - }; - static const char* kEditableMenuStrings[] = { - "Cut", - "Copy", - "<separator>", - "Paste", - "Spelling and Grammar", - "Substitutions, Transformations", - "Font", - "Speech", - "Paragraph Direction", - "<separator>", - 0 - }; - - // This is possible because mouse events are cancelleable. - if (!context_menu) - return std::vector<std::string>(); - - std::vector<std::string> strings; - - if (context_menu->isEditable) { - for (const char** item = kEditableMenuStrings; *item; ++item) { - strings.push_back(*item); - } - WebVector<WebString> suggestions; - WebTestRunner::MockSpellCheck::fillSuggestionList( - context_menu->misspelledWord, &suggestions); - for (size_t i = 0; i < suggestions.size(); ++i) { - strings.push_back(suggestions[i].utf8()); - } - } else { - for (const char** item = kNonEditableMenuStrings; *item; ++item) { - strings.push_back(*item); - } - } - - return strings; -} - -// How much we should scroll per event - the value here is chosen to match the -// WebKit impl and layout test results. -const float kScrollbarPixelsPerTick = 40.0f; - -WebMouseEvent::Button GetButtonTypeFromButtonNumber(int button_code) { - if (!button_code) - return WebMouseEvent::ButtonLeft; - if (button_code == 2) - return WebMouseEvent::ButtonRight; - return WebMouseEvent::ButtonMiddle; -} - -class MouseDownTask : public WebTestRunner::WebMethodTask<EventSender> { - public: - MouseDownTask(EventSender* obj, int button_number, int modifiers) - : WebMethodTask<EventSender>(obj), - button_number_(button_number), - modifiers_(modifiers) {} - - virtual void runIfValid() OVERRIDE { - m_object->MouseDown(button_number_, modifiers_); - } - - private: - int button_number_; - int modifiers_; -}; - -class MouseUpTask : public WebTestRunner::WebMethodTask<EventSender> { - public: - MouseUpTask(EventSender* obj, int button_number, int modifiers) - : WebMethodTask<EventSender>(obj), - button_number_(button_number), - modifiers_(modifiers) {} - - virtual void runIfValid() OVERRIDE { - m_object->MouseUp(button_number_, modifiers_); - } - - private: - int button_number_; - int modifiers_; -}; - -class KeyDownTask : public WebTestRunner::WebMethodTask<EventSender> { - public: - KeyDownTask(EventSender* obj, - const std::string code_str, - int modifiers, - KeyLocationCode location) - : WebMethodTask<EventSender>(obj), - code_str_(code_str), - modifiers_(modifiers), - location_(location) {} - - virtual void runIfValid() OVERRIDE { - m_object->KeyDown(code_str_, modifiers_, location_); - } - - private: - std::string code_str_; - int modifiers_; - KeyLocationCode location_; -}; - -bool NeedsShiftModifier(int keyCode) { - // If code is an uppercase letter, assign a SHIFT key to eventDown.modifier. - return (keyCode & 0xFF) >= 'A' && (keyCode & 0xFF) <= 'Z'; -} - -// 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, std::string* name) { -#if defined(OS_MACOSX) -// 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. 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 WebTestRunner::VKEY_LEFT: - *name = "MoveToBeginningOfLine"; - break; - case WebTestRunner::VKEY_RIGHT: - *name = "MoveToEndOfLine"; - break; - case WebTestRunner::VKEY_UP: - *name = "MoveToBeginningOfDocument"; - break; - case WebTestRunner::VKEY_DOWN: - *name = "MoveToEndOfDocument"; - break; - default: - return false; - } - - if (event.modifiers & WebKeyboardEvent::ShiftKey) - name->append("AndModifySelection"); - - return true; -#else - return false; -#endif -} - -} // namespace - -class EventSenderBindings : public gin::Wrappable<EventSenderBindings> { - public: - static gin::WrapperInfo kWrapperInfo; - - static void Install(base::WeakPtr<EventSender> sender, - blink::WebFrame* frame); - - private: - explicit EventSenderBindings(base::WeakPtr<EventSender> sender); - virtual ~EventSenderBindings(); - - // gin::Wrappable: - virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) OVERRIDE; - - // Bound methods: - void EnableDOMUIEventLogging(); - void FireKeyboardEventsToElement(); - void ClearKillRing(); - std::vector<std::string> ContextClick(); - void TextZoomIn(); - void TextZoomOut(); - void ZoomPageIn(); - void ZoomPageOut(); - void SetPageScaleFactor(gin::Arguments* args); - void ClearTouchPoints(); - void ReleaseTouchPoint(unsigned index); - void UpdateTouchPoint(unsigned index, int x, int y); - void CancelTouchPoint(unsigned index); - void SetTouchModifier(const std::string& key_name, bool set_mask); - void DumpFilenameBeingDragged(); - void GestureFlingCancel(); - void GestureFlingStart(float x, float y, float velocity_x, float velocity_y); - void GestureScrollFirstPoint(int x, int y); - void TouchStart(); - void TouchMove(); - void TouchCancel(); - void TouchEnd(); - void LeapForward(int milliseconds); - void BeginDragWithFiles(const std::vector<std::string>& files); - void AddTouchPoint(gin::Arguments* args); - void MouseDragBegin(); - void MouseDragEnd(); - void MouseMomentumBegin(); - void GestureScrollBegin(gin::Arguments* args); - void GestureScrollEnd(gin::Arguments* args); - void GestureScrollUpdate(gin::Arguments* args); - void GestureScrollUpdateWithoutPropagation(gin::Arguments* args); - void GestureTap(gin::Arguments* args); - void GestureTapDown(gin::Arguments* args); - void GestureShowPress(gin::Arguments* args); - void GestureTapCancel(gin::Arguments* args); - void GestureLongPress(gin::Arguments* args); - void GestureLongTap(gin::Arguments* args); - void GestureTwoFingerTap(gin::Arguments* args); - void ContinuousMouseScrollBy(gin::Arguments* args); - void DispatchMessage(int msg, int wparam, int lparam); - void MouseMoveTo(gin::Arguments* args); - void MouseScrollBy(gin::Arguments* args); - void MouseMomentumScrollBy(gin::Arguments* args); - void MouseMomentumEnd(); - void ScheduleAsynchronousClick(gin::Arguments* args); - void ScheduleAsynchronousKeyDown(gin::Arguments* args); - void MouseDown(gin::Arguments* args); - void MouseUp(gin::Arguments* args); - void KeyDown(gin::Arguments* args); - - // Binding properties: - bool ForceLayoutOnEvents() const; - void SetForceLayoutOnEvents(bool force); - bool IsDragMode() const; - void SetIsDragMode(bool drag_mode); - -#if defined(OS_WIN) - int WmKeyDown() const; - void SetWmKeyDown(int key_down); - - int WmKeyUp() const; - void SetWmKeyUp(int key_up); - - int WmChar() const; - void SetWmChar(int wm_char); - - int WmDeadChar() const; - void SetWmDeadChar(int dead_char); - - int WmSysKeyDown() const; - void SetWmSysKeyDown(int key_down); - - int WmSysKeyUp() const; - void SetWmSysKeyUp(int key_up); - - int WmSysChar() const; - void SetWmSysChar(int sys_char); - - int WmSysDeadChar() const; - void SetWmSysDeadChar(int sys_dead_char); -#endif - - base::WeakPtr<EventSender> sender_; - - DISALLOW_COPY_AND_ASSIGN(EventSenderBindings); -}; - -gin::WrapperInfo EventSenderBindings::kWrapperInfo = {gin::kEmbedderNativeGin}; - -EventSenderBindings::EventSenderBindings(base::WeakPtr<EventSender> sender) - : sender_(sender) { -} - -EventSenderBindings::~EventSenderBindings() {} - -// static -void EventSenderBindings::Install(base::WeakPtr<EventSender> sender, - WebFrame* frame) { - v8::Isolate* isolate = blink::mainThreadIsolate(); - v8::HandleScope handle_scope(isolate); - v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); - if (context.IsEmpty()) - return; - - v8::Context::Scope context_scope(context); - - gin::Handle<EventSenderBindings> bindings = - gin::CreateHandle(isolate, new EventSenderBindings(sender)); - v8::Handle<v8::Object> global = context->Global(); - global->Set(gin::StringToV8(isolate, "eventSender"), bindings.ToV8()); -} - -gin::ObjectTemplateBuilder -EventSenderBindings::GetObjectTemplateBuilder(v8::Isolate* isolate) { - return gin::Wrappable<EventSenderBindings>::GetObjectTemplateBuilder(isolate) - .SetMethod("enableDOMUIEventLogging", - &EventSenderBindings::EnableDOMUIEventLogging) - .SetMethod("fireKeyboardEventsToElement", - &EventSenderBindings::FireKeyboardEventsToElement) - .SetMethod("clearKillRing", &EventSenderBindings::ClearKillRing) - .SetMethod("contextClick", &EventSenderBindings::ContextClick) - .SetMethod("textZoomIn", &EventSenderBindings::TextZoomIn) - .SetMethod("textZoomOut", &EventSenderBindings::TextZoomOut) - .SetMethod("zoomPageIn", &EventSenderBindings::ZoomPageIn) - .SetMethod("zoomPageOut", &EventSenderBindings::ZoomPageOut) - .SetMethod("setPageScaleFactor", &EventSenderBindings::SetPageScaleFactor) - .SetMethod("clearTouchPoints", &EventSenderBindings::ClearTouchPoints) - .SetMethod("releaseTouchPoint", &EventSenderBindings::ReleaseTouchPoint) - .SetMethod("updateTouchPoint", &EventSenderBindings::UpdateTouchPoint) - .SetMethod("cancelTouchPoint", &EventSenderBindings::CancelTouchPoint) - .SetMethod("setTouchModifier", &EventSenderBindings::SetTouchModifier) - .SetMethod("dumpFilenameBeingDragged", - &EventSenderBindings::DumpFilenameBeingDragged) - .SetMethod("gestureFlingCancel", &EventSenderBindings::GestureFlingCancel) - .SetMethod("gestureFlingStart", &EventSenderBindings::GestureFlingStart) - .SetMethod("gestureScrollFirstPoint", - &EventSenderBindings::GestureScrollFirstPoint) - .SetMethod("touchStart", &EventSenderBindings::TouchStart) - .SetMethod("touchMove", &EventSenderBindings::TouchMove) - .SetMethod("touchCancel", &EventSenderBindings::TouchCancel) - .SetMethod("touchEnd", &EventSenderBindings::TouchEnd) - .SetMethod("leapForward", &EventSenderBindings::LeapForward) - .SetMethod("beginDragWithFiles", &EventSenderBindings::BeginDragWithFiles) - .SetMethod("addTouchPoint", &EventSenderBindings::AddTouchPoint) - .SetMethod("mouseDragBegin", &EventSenderBindings::MouseDragBegin) - .SetMethod("mouseDragEnd", &EventSenderBindings::MouseDragEnd) - .SetMethod("mouseMomentumBegin", &EventSenderBindings::MouseMomentumBegin) - .SetMethod("gestureScrollBegin", &EventSenderBindings::GestureScrollBegin) - .SetMethod("gestureScrollEnd", &EventSenderBindings::GestureScrollEnd) - .SetMethod("gestureScrollUpdate", - &EventSenderBindings::GestureScrollUpdate) - .SetMethod("gestureScrollUpdateWithoutPropagation", - &EventSenderBindings::GestureScrollUpdateWithoutPropagation) - .SetMethod("gestureTap", &EventSenderBindings::GestureTap) - .SetMethod("gestureTapDown", &EventSenderBindings::GestureTapDown) - .SetMethod("gestureShowPress", &EventSenderBindings::GestureShowPress) - .SetMethod("gestureTapCancel", &EventSenderBindings::GestureTapCancel) - .SetMethod("gestureLongPress", &EventSenderBindings::GestureLongPress) - .SetMethod("gestureLongTap", &EventSenderBindings::GestureLongTap) - .SetMethod("gestureTwoFingerTap", - &EventSenderBindings::GestureTwoFingerTap) - .SetMethod("continuousMouseScrollBy", - &EventSenderBindings::ContinuousMouseScrollBy) - .SetMethod("dispatchMessage", &EventSenderBindings::DispatchMessage) - .SetMethod("keyDown", &EventSenderBindings::KeyDown) - .SetMethod("mouseDown", &EventSenderBindings::MouseDown) - .SetMethod("mouseMoveTo", &EventSenderBindings::MouseMoveTo) - .SetMethod("mouseScrollBy", &EventSenderBindings::MouseScrollBy) - .SetMethod("mouseUp", &EventSenderBindings::MouseUp) - .SetMethod("mouseMomentumScrollBy", - &EventSenderBindings::MouseMomentumScrollBy) - .SetMethod("mouseMomentumEnd", &EventSenderBindings::MouseMomentumEnd) - .SetMethod("scheduleAsynchronousClick", - &EventSenderBindings::ScheduleAsynchronousClick) - .SetMethod("scheduleAsynchronousKeyDown", - &EventSenderBindings::ScheduleAsynchronousKeyDown) - .SetProperty("forceLayoutOnEvents", - &EventSenderBindings::ForceLayoutOnEvents, - &EventSenderBindings::SetForceLayoutOnEvents) - .SetProperty("dragMode", - &EventSenderBindings::IsDragMode, - &EventSenderBindings::SetIsDragMode) -#if defined(OS_WIN) - .SetProperty("WM_KEYDOWN", - &EventSenderBindings::WmKeyDown, - &EventSenderBindings::SetWmKeyDown) - .SetProperty("WM_KEYUP", - &EventSenderBindings::WmKeyUp, - &EventSenderBindings::SetWmKeyUp) - .SetProperty("WM_CHAR", - &EventSenderBindings::WmChar, - &EventSenderBindings::SetWmChar) - .SetProperty("WM_DEADCHAR", - &EventSenderBindings::WmDeadChar, - &EventSenderBindings::SetWmDeadChar) - .SetProperty("WM_SYSKEYDOWN", - &EventSenderBindings::WmSysKeyDown, - &EventSenderBindings::SetWmSysKeyDown) - .SetProperty("WM_SYSKEYUP", - &EventSenderBindings::WmSysKeyUp, - &EventSenderBindings::SetWmSysKeyUp) - .SetProperty("WM_SYSCHAR", - &EventSenderBindings::WmSysChar, - &EventSenderBindings::SetWmSysChar) - .SetProperty("WM_SYSDEADCHAR", - &EventSenderBindings::WmSysDeadChar, - &EventSenderBindings::SetWmSysDeadChar); -#else - ; -#endif -} - -void EventSenderBindings::EnableDOMUIEventLogging() { - if (sender_) - sender_->EnableDOMUIEventLogging(); -} - -void EventSenderBindings::FireKeyboardEventsToElement() { - if (sender_) - sender_->FireKeyboardEventsToElement(); -} - -void EventSenderBindings::ClearKillRing() { - if (sender_) - sender_->ClearKillRing(); -} - -std::vector<std::string> EventSenderBindings::ContextClick() { - if (sender_) - return sender_->ContextClick(); - return std::vector<std::string>(); -} - -void EventSenderBindings::TextZoomIn() { - if (sender_) - sender_->TextZoomIn(); -} -void EventSenderBindings::TextZoomOut() { - if (sender_) - sender_->TextZoomOut(); -} -void EventSenderBindings::ZoomPageIn() { - if (sender_) - sender_->ZoomPageIn(); -} -void EventSenderBindings::ZoomPageOut() { - if (sender_) - sender_->ZoomPageOut(); -} -void EventSenderBindings::SetPageScaleFactor(gin::Arguments* args) { - if (!sender_) - return; - float scale_factor; - int x; - int y; - if (args->PeekNext().IsEmpty()) - return; - args->GetNext(&scale_factor); - if (args->PeekNext().IsEmpty()) - return; - args->GetNext(&x); - if (args->PeekNext().IsEmpty()) - return; - args->GetNext(&y); - sender_->SetPageScaleFactor(scale_factor, x, y); -} -void EventSenderBindings::ClearTouchPoints() { - if (sender_) - sender_->ClearTouchPoints(); -} -void EventSenderBindings::ReleaseTouchPoint(unsigned index) { - if (sender_) - sender_->ReleaseTouchPoint(index); -} -void EventSenderBindings::UpdateTouchPoint(unsigned index, int x, int y) { - if (sender_) - sender_->UpdateTouchPoint(index, x, y); -} -void EventSenderBindings::CancelTouchPoint(unsigned index) { - if (sender_) - sender_->CancelTouchPoint(index); -} -void EventSenderBindings::SetTouchModifier(const std::string& key_name, - bool set_mask) { - if (sender_) - sender_->SetTouchModifier(key_name, set_mask); -} -void EventSenderBindings::DumpFilenameBeingDragged() { - if (sender_) - sender_->DumpFilenameBeingDragged(); -} -void EventSenderBindings::GestureFlingCancel() { - if (sender_) - sender_->GestureFlingCancel(); -} -void EventSenderBindings::GestureFlingStart(float x, - float y, - float velocity_x, - float velocity_y) { - if (sender_) - sender_->GestureFlingStart(x, y, velocity_x, velocity_y); -} -void EventSenderBindings::GestureScrollFirstPoint(int x, int y) { - if (sender_) - sender_->GestureScrollFirstPoint(x, y); -} -void EventSenderBindings::TouchStart() { - if (sender_) - sender_->TouchStart(); -} -void EventSenderBindings::TouchMove() { - if (sender_) - sender_->TouchMove(); -} -void EventSenderBindings::TouchCancel() { - if (sender_) - sender_->TouchCancel(); -} -void EventSenderBindings::TouchEnd() { - if (sender_) - sender_->TouchEnd(); -} -void EventSenderBindings::LeapForward(int milliseconds) { - if (sender_) - sender_->LeapForward(milliseconds); -} -void EventSenderBindings::BeginDragWithFiles( - const std::vector<std::string>& files) { - if (sender_) - sender_->BeginDragWithFiles(files); -} -void EventSenderBindings::AddTouchPoint(gin::Arguments* args) { - if (sender_) - sender_->AddTouchPoint(args); -} -void EventSenderBindings::MouseDragBegin() { - if (sender_) - sender_->MouseDragBegin(); -} -void EventSenderBindings::MouseDragEnd() { - if (sender_) - sender_->MouseDragEnd(); -} -void EventSenderBindings::MouseMomentumBegin() { - if (sender_) - sender_->MouseMomentumBegin(); -} -void EventSenderBindings::GestureScrollBegin(gin::Arguments* args) { - if (sender_) - sender_->GestureScrollBegin(args); -} -void EventSenderBindings::GestureScrollEnd(gin::Arguments* args) { - if (sender_) - sender_->GestureScrollEnd(args); -} -void EventSenderBindings::GestureScrollUpdate(gin::Arguments* args) { - if (sender_) - sender_->GestureScrollUpdate(args); -} -void EventSenderBindings::GestureScrollUpdateWithoutPropagation( - gin::Arguments* args) { - if (sender_) - sender_->GestureScrollUpdateWithoutPropagation(args); -} -void EventSenderBindings::GestureTap(gin::Arguments* args) { - if (sender_) - sender_->GestureTap(args); -} -void EventSenderBindings::GestureTapDown(gin::Arguments* args) { - if (sender_) - sender_->GestureTapDown(args); -} -void EventSenderBindings::GestureShowPress(gin::Arguments* args) { - if (sender_) - sender_->GestureShowPress(args); -} -void EventSenderBindings::GestureTapCancel(gin::Arguments* args) { - if (sender_) - sender_->GestureTapCancel(args); -} -void EventSenderBindings::GestureLongPress(gin::Arguments* args) { - if (sender_) - sender_->GestureLongPress(args); -} -void EventSenderBindings::GestureLongTap(gin::Arguments* args) { - if (sender_) - sender_->GestureLongTap(args); -} -void EventSenderBindings::GestureTwoFingerTap(gin::Arguments* args) { - if (sender_) - sender_->GestureTwoFingerTap(args); -} -void EventSenderBindings::ContinuousMouseScrollBy(gin::Arguments* args) { - if (sender_) - sender_->ContinuousMouseScrollBy(args); -} -void EventSenderBindings::DispatchMessage(int msg, int wparam, int lparam) { - if (sender_) - sender_->DispatchMessage(msg, wparam, lparam); -} -void EventSenderBindings::MouseMoveTo(gin::Arguments* args) { - if (sender_) - sender_->MouseMoveTo(args); -} -void EventSenderBindings::MouseScrollBy(gin::Arguments* args) { - if (sender_) - sender_->MouseScrollBy(args); -} -void EventSenderBindings::MouseMomentumScrollBy(gin::Arguments* args) { - if (sender_) - sender_->MouseMomentumScrollBy(args); -} -void EventSenderBindings::MouseMomentumEnd() { - if (sender_) - sender_->MouseMomentumEnd(); -} -void EventSenderBindings::ScheduleAsynchronousClick(gin::Arguments* args) { - if (!sender_) - return; - - int button_number = 0; - int modifiers = 0; - if (!args->PeekNext().IsEmpty()) { - args->GetNext(&button_number); - if (!args->PeekNext().IsEmpty()) - modifiers = GetKeyModifiersFromV8(args->PeekNext()); - } - sender_->ScheduleAsynchronousClick(button_number, modifiers); -} -void EventSenderBindings::ScheduleAsynchronousKeyDown(gin::Arguments* args) { - if (!sender_) - return; - - std::string code_str; - int modifiers = 0; - int location = DOMKeyLocationStandard; - args->GetNext(&code_str); - if (!args->PeekNext().IsEmpty()) { - v8::Handle<v8::Value> value; - args->GetNext(&value); - modifiers = GetKeyModifiersFromV8(value); - if (!args->PeekNext().IsEmpty()) - args->GetNext(&location); - } - sender_->ScheduleAsynchronousKeyDown(code_str, modifiers, - static_cast<KeyLocationCode>(location)); -} -void EventSenderBindings::MouseDown(gin::Arguments* args) { - if (!sender_) - return; - - int button_number = 0; - int modifiers = 0; - if (!args->PeekNext().IsEmpty()) { - args->GetNext(&button_number); - if (!args->PeekNext().IsEmpty()) - modifiers = GetKeyModifiersFromV8(args->PeekNext()); - } - sender_->MouseDown(button_number, modifiers); -} -void EventSenderBindings::MouseUp(gin::Arguments* args) { - if (!sender_) - return; - - int button_number = 0; - int modifiers = 0; - if (!args->PeekNext().IsEmpty()) { - args->GetNext(&button_number); - if (!args->PeekNext().IsEmpty()) - modifiers = GetKeyModifiersFromV8(args->PeekNext()); - } - sender_->MouseUp(button_number, modifiers); -} -void EventSenderBindings::KeyDown(gin::Arguments* args) { - if (!sender_) - return; - - std::string code_str; - int modifiers = 0; - int location = DOMKeyLocationStandard; - args->GetNext(&code_str); - if (!args->PeekNext().IsEmpty()) { - v8::Handle<v8::Value> value; - args->GetNext(&value); - modifiers = GetKeyModifiersFromV8(value); - if (!args->PeekNext().IsEmpty()) - args->GetNext(&location); - } - sender_->KeyDown(code_str, modifiers, static_cast<KeyLocationCode>(location)); -} -bool EventSenderBindings::ForceLayoutOnEvents() const { - if (sender_) - return sender_->force_layout_on_events(); - return false; -} -void EventSenderBindings::SetForceLayoutOnEvents(bool force) { - if (sender_) - sender_->set_force_layout_on_events(force); -} -bool EventSenderBindings::IsDragMode() const { - if (sender_) - return sender_->is_drag_mode(); - return false; -} -void EventSenderBindings::SetIsDragMode(bool drag_mode) { - if (sender_) - sender_->set_is_drag_mode(drag_mode); -} - -#if defined(OS_WIN) -int EventSenderBindings::WmKeyDown() const { - if (sender_) - return sender_->wm_key_down(); - return 0; -} -void EventSenderBindings::SetWmKeyDown(int key_down) { - if (sender_) - sender_->set_wm_key_down(key_down); -} - -int EventSenderBindings::WmKeyUp() const { - if (sender_) - return sender_->wm_key_up(); - return 0; -} -void EventSenderBindings::SetWmKeyUp(int key_up) { - if (sender_) - sender_->set_wm_key_up(key_up); -} - -int EventSenderBindings::WmChar() const { - if (sender_) - return sender_->wm_char(); - return 0; -} -void EventSenderBindings::SetWmChar(int wm_char) { - if (sender_) - sender_->set_wm_char(wm_char); -} - -int EventSenderBindings::WmDeadChar() const { - if (sender_) - return sender_->wm_dead_char(); - return 0; -} -void EventSenderBindings::SetWmDeadChar(int dead_char) { - if (sender_) - sender_->set_wm_dead_char(dead_char); -} - -int EventSenderBindings::WmSysKeyDown() const { - if (sender_) - return sender_->wm_sys_key_down(); - return 0; -} -void EventSenderBindings::SetWmSysKeyDown(int key_down) { - if (sender_) - sender_->set_wm_sys_key_down(key_down); -} - -int EventSenderBindings::WmSysKeyUp() const { - if (sender_) - return sender_->wm_sys_key_up(); - return 0; -} -void EventSenderBindings::SetWmSysKeyUp(int key_up) { - if (sender_) - sender_->set_wm_sys_key_up(key_up); -} - -int EventSenderBindings::WmSysChar() const { - if (sender_) - return sender_->wm_sys_char(); - return 0; -} -void EventSenderBindings::SetWmSysChar(int sys_char) { - if (sender_) - sender_->set_wm_sys_char(sys_char); -} - -int EventSenderBindings::WmSysDeadChar() const { - if (sender_) - return sender_->wm_sys_dead_char(); - return 0; -} -void EventSenderBindings::SetWmSysDeadChar(int sys_dead_char) { - if (sender_) - sender_->set_wm_sys_dead_char(sys_dead_char); -} -#endif - -// EventSender ----------------------------------------------------------------- - -WebMouseEvent::Button EventSender::pressed_button_ = WebMouseEvent::ButtonNone; - -WebPoint EventSender::last_mouse_pos_; - -WebMouseEvent::Button EventSender::last_button_type_ = - WebMouseEvent::ButtonNone; - -EventSender::SavedEvent::SavedEvent() - : type(TYPE_UNSPECIFIED), - button_type(WebMouseEvent::ButtonNone), - milliseconds(0), - modifiers(0) {} - -EventSender::EventSender(WebTestRunner::TestInterfaces* interfaces) - : interfaces_(interfaces), - delegate_(NULL), - view_(NULL), - force_layout_on_events_(false), - is_drag_mode_(true), - touch_modifiers_(0), - replaying_saved_events_(false), - current_drag_effects_allowed_(blink::WebDragOperationNone), - last_click_time_sec_(0), - current_drag_effect_(blink::WebDragOperationNone), - time_offset_ms_(0), - click_count_(0), -#if defined(OS_WIN) - wm_key_down_(0), - wm_key_up_(0), - wm_char_(0), - wm_dead_char_(0), - wm_sys_key_down_(0), - wm_sys_key_up_(0), - wm_sys_char_(0), - wm_sys_dead_char_(0), -#endif - weak_factory_(this) {} - -EventSender::~EventSender() {} - -void EventSender::Reset() { - DCHECK(current_drag_data_.isNull()); - current_drag_data_.reset(); - current_drag_effect_ = blink::WebDragOperationNone; - current_drag_effects_allowed_ = blink::WebDragOperationNone; - if (view_ && pressed_button_ != WebMouseEvent::ButtonNone) - view_->mouseCaptureLost(); - pressed_button_ = WebMouseEvent::ButtonNone; - is_drag_mode_ = true; - force_layout_on_events_ = true; - -#if defined(OS_WIN) - wm_key_down_ = WM_KEYDOWN; - wm_key_up_ = WM_KEYUP; - wm_char_ = WM_CHAR; - wm_dead_char_ = WM_DEADCHAR; - wm_sys_key_down_ = WM_SYSKEYDOWN; - wm_sys_key_up_ = WM_SYSKEYUP; - wm_sys_char_ = WM_SYSCHAR; - wm_sys_dead_char_ = WM_SYSDEADCHAR; -#endif - - last_mouse_pos_ = WebPoint(0, 0); - last_click_time_sec_ = 0; - last_click_pos_ = WebPoint(0, 0); - last_button_type_ = WebMouseEvent::ButtonNone; - touch_points_.clear(); - task_list_.revokeAll(); - current_gesture_location_ = WebPoint(0, 0); - mouse_event_queue_.clear(); - - time_offset_ms_ = 0; - click_count_ = 0; -} - -void EventSender::Install(WebFrame* frame) { - EventSenderBindings::Install(weak_factory_.GetWeakPtr(), frame); -} - -void EventSender::SetDelegate(WebTestRunner::WebTestDelegate* delegate) { - delegate_ = delegate; -} - -void EventSender::SetWebView(WebView* view) { - view_ = view; -} - -void EventSender::SetContextMenuData(const WebContextMenuData& data) { - last_context_menu_data_.reset(new WebContextMenuData(data)); -} - -void EventSender::DoDragDrop(const WebDragData& drag_data, - WebDragOperationsMask mask) { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseDown, - pressed_button_, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - WebPoint client_point(event.x, event.y); - WebPoint screen_point(event.globalX, event.globalY); - current_drag_data_ = drag_data; - current_drag_effects_allowed_ = mask; - current_drag_effect_ = view_->dragTargetDragEnter( - drag_data, client_point, screen_point, current_drag_effects_allowed_, 0); - - // Finish processing events. - ReplaySavedEvents(); -} - -void EventSender::MouseDown(int button_number, int modifiers) { - if (force_layout_on_events_) - view_->layout(); - - DCHECK_NE(-1, button_number); - - WebMouseEvent::Button button_type = - GetButtonTypeFromButtonNumber(button_number); - - UpdateClickCountForButton(button_type); - - pressed_button_ = button_type; - - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseDown, - button_type, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - modifiers, - &event); - view_->handleInputEvent(event); -} - -void EventSender::MouseUp(int button_number, int modifiers) { - if (force_layout_on_events_) - view_->layout(); - - DCHECK_NE(-1, button_number); - - WebMouseEvent::Button button_type = - GetButtonTypeFromButtonNumber(button_number); - - if (is_drag_mode_ && !replaying_saved_events_) { - SavedEvent saved_event; - saved_event.type = SavedEvent::TYPE_MOUSE_UP; - saved_event.button_type = button_type; - saved_event.modifiers = modifiers; - mouse_event_queue_.push_back(saved_event); - ReplaySavedEvents(); - } else { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseUp, - button_type, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - modifiers, - &event); - DoMouseUp(event); - } -} - -void EventSender::KeyDown(const std::string& code_str, - int modifiers, - KeyLocationCode location) { - // 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? - - bool generate_char = false; - - // 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 needs_shift_key_modifier = false; - - if ("\n" == code_str) { - generate_char = true; - text = code = WebTestRunner::VKEY_RETURN; - } else if ("rightArrow" == code_str) { - code = WebTestRunner::VKEY_RIGHT; - } else if ("downArrow" == code_str) { - code = WebTestRunner::VKEY_DOWN; - } else if ("leftArrow" == code_str) { - code = WebTestRunner::VKEY_LEFT; - } else if ("upArrow" == code_str) { - code = WebTestRunner::VKEY_UP; - } else if ("insert" == code_str) { - code = WebTestRunner::VKEY_INSERT; - } else if ("delete" == code_str) { - code = WebTestRunner::VKEY_DELETE; - } else if ("pageUp" == code_str) { - code = WebTestRunner::VKEY_PRIOR; - } else if ("pageDown" == code_str) { - code = WebTestRunner::VKEY_NEXT; - } else if ("home" == code_str) { - code = WebTestRunner::VKEY_HOME; - } else if ("end" == code_str) { - code = WebTestRunner::VKEY_END; - } else if ("printScreen" == code_str) { - code = WebTestRunner::VKEY_SNAPSHOT; - } else if ("menu" == code_str) { - code = WebTestRunner::VKEY_APPS; - } else if ("leftControl" == code_str) { - code = WebTestRunner::VKEY_LCONTROL; - } else if ("rightControl" == code_str) { - code = WebTestRunner::VKEY_RCONTROL; - } else if ("leftShift" == code_str) { - code = WebTestRunner::VKEY_LSHIFT; - } else if ("rightShift" == code_str) { - code = WebTestRunner::VKEY_RSHIFT; - } else if ("leftAlt" == code_str) { - code = WebTestRunner::VKEY_LMENU; - } else if ("rightAlt" == code_str) { - code = WebTestRunner::VKEY_RMENU; - } else if ("numLock" == code_str) { - code = WebTestRunner::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) { - std::string function_key_name = base::StringPrintf("F%d", i); - if (function_key_name == code_str) { - code = WebTestRunner::VKEY_F1 + (i - 1); - break; - } - } - if (!code) { - WebString web_code_str = - WebString::fromUTF8(code_str.data(), code_str.size()); - DCHECK_EQ(1u, web_code_str.length()); - text = code = web_code_str.at(0); - needs_shift_key_modifier = NeedsShiftModifier(code); - if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z') - code -= 'a' - 'A'; - generate_char = true; - } - - if ("(" == code_str) { - code = '9'; - needs_shift_key_modifier = true; - } - } - - // For one generated keyboard event, we need to generate a keyDown/keyUp - // pair; - // 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 event_down; - event_down.type = WebInputEvent::RawKeyDown; - event_down.modifiers = modifiers; - event_down.windowsKeyCode = code; - -#if defined(OS_LINUX) && defined(TOOLKIT_GTK) - event_down.nativeKeyCode = - WebTestRunner::NativeKeyCodeForWindowsKeyCode(code); -#endif - - if (generate_char) { - event_down.text[0] = text; - event_down.unmodifiedText[0] = text; - } - - event_down.setKeyIdentifierFromWindowsKeyCode(); - -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID) || \ - defined(TOOLKIT_GTK) - if (event_down.modifiers != 0) - event_down.isSystemKey = WebInputEventFactory::isSystemKeyEvent(event_down); -#endif - - if (needs_shift_key_modifier) - event_down.modifiers |= WebInputEvent::ShiftKey; - - // See if KeyLocation argument is given. - if (location == DOMKeyLocationNumpad) - event_down.modifiers |= WebInputEvent::IsKeyPad; - - WebKeyboardEvent event_up; - event_up = event_down; - event_up.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 (force_layout_on_events_) - view_->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. - // We just simulate the same behavior here. - std::string edit_command; - if (GetEditCommand(event_down, &edit_command)) - delegate_->setEditCommand(edit_command, ""); - - view_->handleInputEvent(event_down); - - if (code == WebTestRunner::VKEY_ESCAPE && !current_drag_data_.isNull()) { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseDown, - pressed_button_, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - FinishDragAndDrop(event, blink::WebDragOperationNone); - } - - delegate_->clearEditCommand(); - - if (generate_char) { - WebKeyboardEvent event_char = event_up; - event_char.type = WebInputEvent::Char; - event_char.keyIdentifier[0] = '\0'; - view_->handleInputEvent(event_char); - } - - view_->handleInputEvent(event_up); -} - -void EventSender::EnableDOMUIEventLogging() {} - -void EventSender::FireKeyboardEventsToElement() {} - -void EventSender::ClearKillRing() {} - -std::vector<std::string> EventSender::ContextClick() { - if (force_layout_on_events_) { - view_->layout(); - } - - UpdateClickCountForButton(WebMouseEvent::ButtonRight); - - // Clears last context menu data because we need to know if the context menu - // be requested after following mouse events. - last_context_menu_data_.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 (pressed_button_ == WebMouseEvent::ButtonNone) { - pressed_button_ = WebMouseEvent::ButtonRight; - } - InitMouseEvent(WebInputEvent::MouseDown, - WebMouseEvent::ButtonRight, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - view_->handleInputEvent(event); - -#if defined(OS_WIN) - InitMouseEvent(WebInputEvent::MouseUp, - WebMouseEvent::ButtonRight, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - view_->handleInputEvent(event); - - pressed_button_= WebMouseEvent::ButtonNone; -#endif - - return MakeMenuItemStringsFor(last_context_menu_data_.release(), delegate_); -} - -void EventSender::TextZoomIn() { - view_->setTextZoomFactor(view_->textZoomFactor() * 1.2f); -} - -void EventSender::TextZoomOut() { - view_->setTextZoomFactor(view_->textZoomFactor() / 1.2f); -} - -void EventSender::ZoomPageIn() { - const std::vector<WebTestRunner::WebTestProxyBase*>& window_list = - interfaces_->windowList(); - - for (size_t i = 0; i < window_list.size(); ++i) { - window_list.at(i)->webView()->setZoomLevel( - window_list.at(i)->webView()->zoomLevel() + 1); - } -} - -void EventSender::ZoomPageOut() { - const std::vector<WebTestRunner::WebTestProxyBase*>& window_list = - interfaces_->windowList(); - - for (size_t i = 0; i < window_list.size(); ++i) { - window_list.at(i)->webView()->setZoomLevel( - window_list.at(i)->webView()->zoomLevel() - 1); - } -} - -void EventSender::SetPageScaleFactor(float scale_factor, int x, int y) { - view_->setPageScaleFactorLimits(scale_factor, scale_factor); - view_->setPageScaleFactor(scale_factor, WebPoint(x, y)); -} - -void EventSender::ClearTouchPoints() { - touch_points_.clear(); -} - -void EventSender::ReleaseTouchPoint(unsigned index) { - DCHECK_LT(index, touch_points_.size()); - - WebTouchPoint* touch_point = &touch_points_[index]; - touch_point->state = WebTouchPoint::StateReleased; -} - -void EventSender::UpdateTouchPoint(unsigned index, int x, int y) { - DCHECK_LT(index, touch_points_.size()); - - WebTouchPoint* touch_point = &touch_points_[index]; - touch_point->state = WebTouchPoint::StateMoved; - touch_point->position = WebFloatPoint(x, y); - touch_point->screenPosition = touch_point->position; -} - -void EventSender::CancelTouchPoint(unsigned index) { - DCHECK_LT(index, touch_points_.size()); - - WebTouchPoint* touch_point = &touch_points_[index]; - touch_point->state = WebTouchPoint::StateCancelled; -} - -void EventSender::SetTouchModifier(const std::string& key_name, - bool set_mask) { - int mask = 0; - if (key_name == "shift") - mask = WebInputEvent::ShiftKey; - else if (key_name == "alt") - mask = WebInputEvent::AltKey; - else if (key_name == "ctrl") - mask = WebInputEvent::ControlKey; - else if (key_name == "meta") - mask = WebInputEvent::MetaKey; - - if (set_mask) - touch_modifiers_ |= mask; - else - touch_modifiers_ &= ~mask; -} - -void EventSender::DumpFilenameBeingDragged() { - WebString filename; - WebVector<WebDragData::Item> items = current_drag_data_.items(); - for (size_t i = 0; i < items.size(); ++i) { - if (items[i].storageType == WebDragData::Item::StorageTypeBinaryData) { - filename = items[i].title; - break; - } - } - delegate_->printMessage(std::string("Filename being dragged: ") + - filename.utf8().data() + "\n"); -} - -void EventSender::GestureFlingCancel() { - WebGestureEvent event; - event.type = WebInputEvent::GestureFlingCancel; - event.timeStampSeconds = GetCurrentEventTimeSec(); - - if (force_layout_on_events_) - view_->layout(); - - view_->handleInputEvent(event); -} - -void EventSender::GestureFlingStart(float x, - float y, - float velocity_x, - float velocity_y) { - WebGestureEvent event; - event.type = WebInputEvent::GestureFlingStart; - - event.x = x; - event.y = y; - event.globalX = event.x; - event.globalY = event.y; - - event.data.flingStart.velocityX = velocity_x; - event.data.flingStart.velocityY = velocity_y; - event.timeStampSeconds = GetCurrentEventTimeSec(); - - if (force_layout_on_events_) - view_->layout(); - - view_->handleInputEvent(event); -} - -void EventSender::GestureScrollFirstPoint(int x, int y) { - current_gesture_location_ = WebPoint(x, y); -} - -void EventSender::TouchStart() { - SendCurrentTouchEvent(WebInputEvent::TouchStart); -} - -void EventSender::TouchMove() { - SendCurrentTouchEvent(WebInputEvent::TouchMove); -} - -void EventSender::TouchCancel() { - SendCurrentTouchEvent(WebInputEvent::TouchCancel); -} - -void EventSender::TouchEnd() { - SendCurrentTouchEvent(WebInputEvent::TouchEnd); -} - -void EventSender::LeapForward(int milliseconds) { - if (is_drag_mode_ && pressed_button_ == WebMouseEvent::ButtonLeft && - !replaying_saved_events_) { - SavedEvent saved_event; - saved_event.type = SavedEvent::TYPE_LEAP_FORWARD; - saved_event.milliseconds = milliseconds; - mouse_event_queue_.push_back(saved_event); - } else { - DoLeapForward(milliseconds); - } -} - -void EventSender::BeginDragWithFiles(const std::vector<std::string>& files) { - current_drag_data_.initialize(); - WebVector<WebString> absolute_filenames(files.size()); - for (size_t i = 0; i < files.size(); ++i) { - WebDragData::Item item; - item.storageType = WebDragData::Item::StorageTypeFilename; - item.filenameData = delegate_->getAbsoluteWebStringFromUTF8Path(files[i]); - current_drag_data_.addItem(item); - absolute_filenames[i] = item.filenameData; - } - current_drag_data_.setFilesystemId( - delegate_->registerIsolatedFileSystem(absolute_filenames)); - current_drag_effects_allowed_ = blink::WebDragOperationCopy; - - // Provide a drag source. - view_->dragTargetDragEnter(current_drag_data_, - last_mouse_pos_, - last_mouse_pos_, - current_drag_effects_allowed_, - 0); - // |is_drag_mode_| saves events and then replays them later. We don't - // need/want that. - is_drag_mode_ = false; - - // Make the rest of eventSender think a drag is in progress. - pressed_button_ = WebMouseEvent::ButtonLeft; -} - -void EventSender::AddTouchPoint(gin::Arguments* args) { - int x; - int y; - args->GetNext(&x); - args->GetNext(&y); - - WebTouchPoint touch_point; - touch_point.state = WebTouchPoint::StatePressed; - touch_point.position = WebFloatPoint(x, y); - touch_point.screenPosition = touch_point.position; - - if (!args->PeekNext().IsEmpty()) { - int radius_x; - if (!args->GetNext(&radius_x)) { - args->ThrowError(); - return; - } - - int radius_y = radius_x; - if (!args->PeekNext().IsEmpty()) { - if (!args->GetNext(&radius_y)) { - args->ThrowError(); - return; - } - } - - touch_point.radiusX = radius_x; - touch_point.radiusY = radius_y; - } - - int lowest_id = 0; - for (size_t i = 0; i < touch_points_.size(); i++) { - if (touch_points_[i].id == lowest_id) - lowest_id++; - } - touch_point.id = lowest_id; - touch_points_.push_back(touch_point); -} - -void EventSender::MouseDragBegin() { - WebMouseWheelEvent event; - InitMouseEvent(WebInputEvent::MouseWheel, - WebMouseEvent::ButtonNone, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - event.phase = WebMouseWheelEvent::PhaseBegan; - event.hasPreciseScrollingDeltas = true; - view_->handleInputEvent(event); -} - -void EventSender::MouseDragEnd() { - WebMouseWheelEvent event; - InitMouseEvent(WebInputEvent::MouseWheel, - WebMouseEvent::ButtonNone, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - event.phase = WebMouseWheelEvent::PhaseEnded; - event.hasPreciseScrollingDeltas = true; - view_->handleInputEvent(event); -} - -void EventSender::MouseMomentumBegin() { - WebMouseWheelEvent event; - InitMouseEvent(WebInputEvent::MouseWheel, - WebMouseEvent::ButtonNone, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - event.momentumPhase = WebMouseWheelEvent::PhaseBegan; - event.hasPreciseScrollingDeltas = true; - view_->handleInputEvent(event); -} - -void EventSender::GestureScrollBegin(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureScrollBegin, args); -} - -void EventSender::GestureScrollEnd(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureScrollEnd, args); -} - -void EventSender::GestureScrollUpdate(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureScrollUpdate, args); -} - -void EventSender::GestureScrollUpdateWithoutPropagation(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureScrollUpdateWithoutPropagation, args); -} - -void EventSender::GestureTap(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureTap, args); -} - -void EventSender::GestureTapDown(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureTapDown, args); -} - -void EventSender::GestureShowPress(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureShowPress, args); -} - -void EventSender::GestureTapCancel(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureTapCancel, args); -} - -void EventSender::GestureLongPress(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureLongPress, args); -} - -void EventSender::GestureLongTap(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureLongTap, args); -} - -void EventSender::GestureTwoFingerTap(gin::Arguments* args) { - GestureEvent(WebInputEvent::GestureTwoFingerTap, args); -} - -void EventSender::ContinuousMouseScrollBy(gin::Arguments* args) { - WebMouseWheelEvent event; - InitMouseWheelEvent(args, true, &event); - view_->handleInputEvent(event); -} - -void EventSender::DispatchMessage(int msg, int wparam, int lparam) { -#if defined(OS_WIN) - // 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 (force_layout_on_events_) - view_->layout(); - - view_->handleInputEvent( - WebInputEventFactory::keyboardEvent(0, msg, wparam, lparam)); -#endif -} - -void EventSender::MouseMoveTo(gin::Arguments* args) { - if (force_layout_on_events_) - view_->layout(); - - int x; - int y; - args->GetNext(&x); - args->GetNext(&y); - WebPoint mouse_pos(x, y); - - int modifiers = 0; - if (!args->PeekNext().IsEmpty()) - modifiers = GetKeyModifiersFromV8(args->PeekNext()); - - if (is_drag_mode_ && pressed_button_ == WebMouseEvent::ButtonLeft && - !replaying_saved_events_) { - SavedEvent saved_event; - saved_event.type = SavedEvent::TYPE_MOUSE_MOVE; - saved_event.pos = mouse_pos; - saved_event.modifiers = modifiers; - mouse_event_queue_.push_back(saved_event); - } else { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseMove, - pressed_button_, - mouse_pos, - GetCurrentEventTimeSec(), - click_count_, - modifiers, - &event); - DoMouseMove(event); - } -} - -void EventSender::MouseScrollBy(gin::Arguments* args) { - WebMouseWheelEvent event; - InitMouseWheelEvent(args, false, &event); - view_->handleInputEvent(event); -} - -void EventSender::MouseMomentumScrollBy(gin::Arguments* args) { - WebMouseWheelEvent event; - InitMouseWheelEvent(args, true, &event); - event.momentumPhase = WebMouseWheelEvent::PhaseChanged; - event.hasPreciseScrollingDeltas = true; - view_->handleInputEvent(event); -} - -void EventSender::MouseMomentumEnd() { - WebMouseWheelEvent event; - InitMouseEvent(WebInputEvent::MouseWheel, - WebMouseEvent::ButtonNone, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - 0, - &event); - event.momentumPhase = WebMouseWheelEvent::PhaseEnded; - event.hasPreciseScrollingDeltas = true; - view_->handleInputEvent(event); -} - -void EventSender::ScheduleAsynchronousClick(int button_number, int modifiers) { - delegate_->postTask(new MouseDownTask(this, button_number, modifiers)); - delegate_->postTask(new MouseUpTask(this, button_number, modifiers)); -} - -void EventSender::ScheduleAsynchronousKeyDown(const std::string& code_str, - int modifiers, - KeyLocationCode location) { - delegate_->postTask(new KeyDownTask(this, code_str, modifiers, location)); -} - -double EventSender::GetCurrentEventTimeSec() { - return (delegate_->getCurrentTimeInMillisecond() + time_offset_ms_) / 1000.0; -} - -void EventSender::DoLeapForward(int milliseconds) { - time_offset_ms_ += milliseconds; -} - -void EventSender::SendCurrentTouchEvent(WebInputEvent::Type type) { - DCHECK_GT(static_cast<unsigned>(WebTouchEvent::touchesLengthCap), - touch_points_.size()); - if (force_layout_on_events_) - view_->layout(); - - WebTouchEvent touch_event; - touch_event.type = type; - touch_event.modifiers = touch_modifiers_; - touch_event.timeStampSeconds = GetCurrentEventTimeSec(); - touch_event.touchesLength = touch_points_.size(); - for (size_t i = 0; i < touch_points_.size(); ++i) - touch_event.touches[i] = touch_points_[i]; - view_->handleInputEvent(touch_event); - - for (size_t i = 0; i < touch_points_.size(); ++i) { - WebTouchPoint* touch_point = &touch_points_[i]; - if (touch_point->state == WebTouchPoint::StateReleased) { - touch_points_.erase(touch_points_.begin() + i); - --i; - } else - touch_point->state = WebTouchPoint::StateStationary; - } -} - -void EventSender::GestureEvent(WebInputEvent::Type type, - gin::Arguments* args) { - double x; - double y; - args->GetNext(&x); - args->GetNext(&y); - WebPoint point(x, y); - - WebGestureEvent event; - event.type = type; - - switch (type) { - case WebInputEvent::GestureScrollUpdate: - case WebInputEvent::GestureScrollUpdateWithoutPropagation: - event.data.scrollUpdate.deltaX = static_cast<float>(x); - event.data.scrollUpdate.deltaY = static_cast<float>(y); - event.x = current_gesture_location_.x; - event.y = current_gesture_location_.y; - current_gesture_location_.x = - current_gesture_location_.x + event.data.scrollUpdate.deltaX; - current_gesture_location_.y = - current_gesture_location_.y + event.data.scrollUpdate.deltaY; - break; - case WebInputEvent::GestureScrollBegin: - current_gesture_location_ = WebPoint(point.x, point.y); - event.x = current_gesture_location_.x; - event.y = current_gesture_location_.y; - break; - case WebInputEvent::GestureScrollEnd: - case WebInputEvent::GestureFlingStart: - event.x = current_gesture_location_.x; - event.y = current_gesture_location_.y; - break; - case WebInputEvent::GestureTap: - if (!args->PeekNext().IsEmpty()) { - float tap_count; - if (!args->GetNext(&tap_count)) { - args->ThrowError(); - return; - } - event.data.tap.tapCount = tap_count; - } else { - event.data.tap.tapCount = 1; - } - - event.x = point.x; - event.y = point.y; - break; - case WebInputEvent::GestureTapUnconfirmed: - if (!args->PeekNext().IsEmpty()) { - float tap_count; - if (!args->GetNext(&tap_count)) { - args->ThrowError(); - return; - } - event.data.tap.tapCount = tap_count; - } 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 (!args->PeekNext().IsEmpty()) { - float width; - if (!args->GetNext(&width)) { - args->ThrowError(); - return; - } - event.data.tapDown.width = width; - } - if (!args->PeekNext().IsEmpty()) { - float height; - if (!args->GetNext(&height)) { - args->ThrowError(); - return; - } - event.data.tapDown.height = height; - } - break; - case WebInputEvent::GestureShowPress: - event.x = point.x; - event.y = point.y; - if (!args->PeekNext().IsEmpty()) { - float width; - if (!args->GetNext(&width)) { - args->ThrowError(); - return; - } - event.data.showPress.width = width; - if (!args->PeekNext().IsEmpty()) { - float height; - if (!args->GetNext(&height)) { - args->ThrowError(); - return; - } - event.data.showPress.height = height; - } - } - 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 (!args->PeekNext().IsEmpty()) { - float width; - if (!args->GetNext(&width)) { - args->ThrowError(); - return; - } - event.data.longPress.width = width; - if (!args->PeekNext().IsEmpty()) { - float height; - if (!args->GetNext(&height)) { - args->ThrowError(); - return; - } - event.data.longPress.height = height; - } - } - break; - case WebInputEvent::GestureLongTap: - event.x = point.x; - event.y = point.y; - if (!args->PeekNext().IsEmpty()) { - float width; - if (!args->GetNext(&width)) { - args->ThrowError(); - return; - } - event.data.longPress.width = width; - if (!args->PeekNext().IsEmpty()) { - float height; - if (!args->GetNext(&height)) { - args->ThrowError(); - return; - } - event.data.longPress.height = height; - } - } - break; - case WebInputEvent::GestureTwoFingerTap: - event.x = point.x; - event.y = point.y; - if (!args->PeekNext().IsEmpty()) { - float first_finger_width; - if (!args->GetNext(&first_finger_width)) { - args->ThrowError(); - return; - } - event.data.twoFingerTap.firstFingerWidth = first_finger_width; - if (!args->PeekNext().IsEmpty()) { - float first_finger_height; - if (!args->GetNext(&first_finger_height)) { - args->ThrowError(); - return; - } - event.data.twoFingerTap.firstFingerHeight = first_finger_height; - } - } - break; - default: - NOTREACHED(); - } - - event.globalX = event.x; - event.globalY = event.y; - event.timeStampSeconds = GetCurrentEventTimeSec(); - - if (force_layout_on_events_) - view_->layout(); - - view_->handleInputEvent(event); - - // Long press might start a drag drop session. Complete it if so. - if (type == WebInputEvent::GestureLongPress && !current_drag_data_.isNull()) { - WebMouseEvent mouse_event; - InitMouseEvent(WebInputEvent::MouseDown, - pressed_button_, - point, - GetCurrentEventTimeSec(), - click_count_, - 0, - &mouse_event); - - FinishDragAndDrop(mouse_event, blink::WebDragOperationNone); - } -} - -void EventSender::UpdateClickCountForButton( - WebMouseEvent::Button button_type) { - if ((GetCurrentEventTimeSec() - last_click_time_sec_ < - kMultipleClickTimeSec) && - (!OutsideMultiClickRadius(last_mouse_pos_, last_click_pos_)) && - (button_type == last_button_type_)) { - ++click_count_; - } else { - click_count_ = 1; - last_button_type_ = button_type; - } -} - -void EventSender::InitMouseWheelEvent(gin::Arguments* args, - bool continuous, - WebMouseWheelEvent* event) { - // 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 (force_layout_on_events_) - view_->layout(); - - double horizontal; - if (!args->GetNext(&horizontal)) { - args->ThrowError(); - return; - } - double vertical; - if (!args->GetNext(&vertical)) { - args->ThrowError(); - return; - } - - bool paged = false; - bool has_precise_scrolling_deltas = false; - int modifiers = 0; - if (!args->PeekNext().IsEmpty()) { - args->GetNext(&paged); - if (!args->PeekNext().IsEmpty()) { - args->GetNext(&has_precise_scrolling_deltas); - if (!args->PeekNext().IsEmpty()) - modifiers = GetKeyModifiersFromV8(args->PeekNext()); - } - } - - InitMouseEvent(WebInputEvent::MouseWheel, - pressed_button_, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - modifiers, - event); - 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 = has_precise_scrolling_deltas; - - if (continuous) { - event->wheelTicksX /= kScrollbarPixelsPerTick; - event->wheelTicksY /= kScrollbarPixelsPerTick; - } else { - event->deltaX *= kScrollbarPixelsPerTick; - event->deltaY *= kScrollbarPixelsPerTick; - } -} - -void EventSender::FinishDragAndDrop(const WebMouseEvent& e, - blink::WebDragOperation drag_effect) { - WebPoint client_point(e.x, e.y); - WebPoint screen_point(e.globalX, e.globalY); - current_drag_effect_ = drag_effect; - if (current_drag_effect_) { - // Specifically pass any keyboard modifiers to the drop method. This allows - // tests to control the drop type (i.e. copy or move). - view_->dragTargetDrop(client_point, screen_point, e.modifiers); - } else { - view_->dragTargetDragLeave(); - } - view_->dragSourceEndedAt(client_point, screen_point, current_drag_effect_); - view_->dragSourceSystemDragEnded(); - - current_drag_data_.reset(); -} - -void EventSender::DoMouseUp(const WebMouseEvent& e) { - view_->handleInputEvent(e); - - pressed_button_ = WebMouseEvent::ButtonNone; - last_click_time_sec_ = e.timeStampSeconds; - last_click_pos_ = last_mouse_pos_; - - // If we're in a drag operation, complete it. - if (current_drag_data_.isNull()) - return; - - WebPoint client_point(e.x, e.y); - WebPoint screen_point(e.globalX, e.globalY); - FinishDragAndDrop( - e, - view_->dragTargetDragOver( - client_point, screen_point, current_drag_effects_allowed_, 0)); -} - -void EventSender::DoMouseMove(const WebMouseEvent& e) { - last_mouse_pos_ = WebPoint(e.x, e.y); - - view_->handleInputEvent(e); - - if (pressed_button_ == WebMouseEvent::ButtonNone || - current_drag_data_.isNull()) { - return; - } - - WebPoint client_point(e.x, e.y); - WebPoint screen_point(e.globalX, e.globalY); - current_drag_effect_ = view_->dragTargetDragOver( - client_point, screen_point, current_drag_effects_allowed_, 0); -} - -void EventSender::ReplaySavedEvents() { - replaying_saved_events_ = true; - while (!mouse_event_queue_.empty()) { - SavedEvent e = mouse_event_queue_.front(); - mouse_event_queue_.pop_front(); - - switch (e.type) { - case SavedEvent::TYPE_MOUSE_MOVE: { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseMove, - pressed_button_, - e.pos, - GetCurrentEventTimeSec(), - click_count_, - e.modifiers, - &event); - DoMouseMove(event); - break; - } - case SavedEvent::TYPE_LEAP_FORWARD: - DoLeapForward(e.milliseconds); - break; - case SavedEvent::TYPE_MOUSE_UP: { - WebMouseEvent event; - InitMouseEvent(WebInputEvent::MouseUp, - e.button_type, - last_mouse_pos_, - GetCurrentEventTimeSec(), - click_count_, - e.modifiers, - &event); - DoMouseUp(event); - break; - } - default: - NOTREACHED(); - } - } - - replaying_saved_events_ = false; -} - -} // namespace content diff --git a/content/shell/renderer/test_runner/event_sender.h b/content/shell/renderer/test_runner/event_sender.h deleted file mode 100644 index e1423a5..0000000 --- a/content/shell/renderer/test_runner/event_sender.h +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2014 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 CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENT_SENDER_H_ -#define CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENT_SENDER_H_ - -#include <queue> -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "build/build_config.h" -#include "content/shell/renderer/test_runner/WebTask.h" -#include "third_party/WebKit/public/platform/WebDragData.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" -#include "third_party/WebKit/public/web/WebTouchPoint.h" - -namespace blink { -class WebFrame; -class WebView; -struct WebContextMenuData; -} - -namespace gin { -class Arguments; -} - -namespace WebTestRunner { -class TestInterfaces; -class WebTestDelegate; -} - -namespace content { - -// 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 -}; - -class EventSender : public base::SupportsWeakPtr<EventSender> { - public: - explicit EventSender(WebTestRunner::TestInterfaces*); - virtual ~EventSender(); - - void Reset(); - void Install(blink::WebFrame*); - void SetDelegate(WebTestRunner::WebTestDelegate*); - void SetWebView(blink::WebView*); - - void SetContextMenuData(const blink::WebContextMenuData&); - - void DoDragDrop(const blink::WebDragData&, blink::WebDragOperationsMask); - - void MouseDown(int button_number, int modifiers); - void MouseUp(int button_number, int modifiers); - void KeyDown(const std::string& code_str, - int modifiers, - KeyLocationCode location); - - WebTestRunner::WebTaskList* taskList() { return &task_list_; } - - private: - friend class EventSenderBindings; - - struct SavedEvent { - enum SavedEventType { - TYPE_UNSPECIFIED, - TYPE_MOUSE_UP, - TYPE_MOUSE_MOVE, - TYPE_LEAP_FORWARD - }; - - SavedEvent(); - - SavedEventType type; - blink::WebMouseEvent::Button button_type; // For MouseUp. - blink::WebPoint pos; // For MouseMove. - int milliseconds; // For LeapForward. - int modifiers; - }; - - void EnableDOMUIEventLogging(); - void FireKeyboardEventsToElement(); - void ClearKillRing(); - - std::vector<std::string> ContextClick(); - - void TextZoomIn(); - void TextZoomOut(); - - void ZoomPageIn(); - void ZoomPageOut(); - - void SetPageScaleFactor(float scale_factor, int x, int y); - - void ClearTouchPoints(); - void ReleaseTouchPoint(unsigned index); - void UpdateTouchPoint(unsigned index, int x, int y); - void CancelTouchPoint(unsigned index); - void SetTouchModifier(const std::string& key_name, bool set_mask); - - void DumpFilenameBeingDragged(); - - void GestureFlingCancel(); - void GestureFlingStart(float x, float y, float velocity_x, float velocity_y); - void GestureScrollFirstPoint(int x, int y); - - void TouchStart(); - void TouchMove(); - void TouchCancel(); - void TouchEnd(); - - void LeapForward(int milliseconds); - - void BeginDragWithFiles(const std::vector<std::string>& files); - - void AddTouchPoint(gin::Arguments* args); - - void MouseDragBegin(); - void MouseDragEnd(); - void MouseMomentumBegin(); - - void GestureScrollBegin(gin::Arguments* args); - void GestureScrollEnd(gin::Arguments* args); - void GestureScrollUpdate(gin::Arguments* args); - void GestureScrollUpdateWithoutPropagation(gin::Arguments* args); - void GestureTap(gin::Arguments* args); - void GestureTapDown(gin::Arguments* args); - void GestureShowPress(gin::Arguments* args); - void GestureTapCancel(gin::Arguments* args); - void GestureLongPress(gin::Arguments* args); - void GestureLongTap(gin::Arguments* args); - void GestureTwoFingerTap(gin::Arguments* args); - - void ContinuousMouseScrollBy(gin::Arguments* args); - void DispatchMessage(int msg, int wparam, int lparam); - void MouseMoveTo(gin::Arguments* args); - void MouseScrollBy(gin::Arguments* args); - void MouseMomentumScrollBy(gin::Arguments* args); - void MouseMomentumEnd(); - void ScheduleAsynchronousClick(int button_number, int modifiers); - void ScheduleAsynchronousKeyDown(const std::string& code_str, - int modifiers, - KeyLocationCode location); - - double GetCurrentEventTimeSec(); - - void DoLeapForward(int milliseconds); - - void SendCurrentTouchEvent(blink::WebInputEvent::Type); - - void GestureEvent(blink::WebInputEvent::Type, gin::Arguments*); - - void UpdateClickCountForButton(blink::WebMouseEvent::Button); - - void InitMouseWheelEvent(gin::Arguments* args, - bool continuous, - blink::WebMouseWheelEvent* event); - - void FinishDragAndDrop(const blink::WebMouseEvent&, blink::WebDragOperation); - - void DoMouseUp(const blink::WebMouseEvent&); - void DoMouseMove(const blink::WebMouseEvent&); - void ReplaySavedEvents(); - - bool force_layout_on_events() const { return force_layout_on_events_; } - void set_force_layout_on_events(bool force) { - force_layout_on_events_ = force; - } - - bool is_drag_mode() const { return is_drag_mode_; } - void set_is_drag_mode(bool drag_mode) { is_drag_mode_ = drag_mode; } - -#if defined(OS_WIN) - int wm_key_down() const { return wm_key_down_; } - void set_wm_key_down(int key_down) { wm_key_down_ = key_down; } - - int wm_key_up() const { return wm_key_up_; } - void set_wm_key_up(int key_up) { wm_key_up_ = key_up; } - - int wm_char() const { return wm_char_; } - void set_wm_char(int wm_char) { wm_char_ = wm_char; } - - int wm_dead_char() const { return wm_dead_char_; } - void set_wm_dead_char(int dead_char) { - wm_dead_char_ = dead_char; - } - - int wm_sys_key_down() const { return wm_sys_key_down_; } - void set_wm_sys_key_down(int key_down) { wm_sys_key_down_ = key_down; } - - int wm_sys_key_up() const { return wm_sys_key_up_; } - void set_wm_sys_key_up(int key_up) { wm_sys_key_up_ = key_up; } - - int wm_sys_char() const { return wm_sys_char_; } - void set_wm_sys_char(int sys_char) { wm_sys_char_ = sys_char; } - - int wm_sys_dead_char() const { return wm_sys_dead_char_; } - void set_wm_sys_dead_char(int sys_dead_char) { - wm_sys_dead_char_ = sys_dead_char; - } - - int wm_key_down_; - int wm_key_up_; - int wm_char_; - int wm_dead_char_; - int wm_sys_key_down_; - int wm_sys_key_up_; - int wm_sys_char_; - int wm_sys_dead_char_; -#endif - - WebTestRunner::WebTaskList task_list_; - - WebTestRunner::TestInterfaces* interfaces_; - WebTestRunner::WebTestDelegate* delegate_; - blink::WebView* view_; - - bool force_layout_on_events_; - - // When set to true (the default value), we batch mouse move and mouse up - // events so we can simulate drag & drop. - bool is_drag_mode_; - - int touch_modifiers_; - std::vector<blink::WebTouchPoint> touch_points_; - - scoped_ptr<blink::WebContextMenuData> last_context_menu_data_; - - blink::WebDragData current_drag_data_; - - // Location of the touch point that initiated a gesture. - blink::WebPoint current_gesture_location_; - - // Currently pressed mouse button (Left/Right/Middle or None). - static blink::WebMouseEvent::Button pressed_button_; - - bool replaying_saved_events_; - - std::deque<SavedEvent> mouse_event_queue_; - - blink::WebDragOperationsMask current_drag_effects_allowed_; - - // Location of last mouseMoveTo event. - static blink::WebPoint last_mouse_pos_; - - // Time and place of the last mouse up event. - double last_click_time_sec_; - blink::WebPoint last_click_pos_; - - // 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 last_button_type_; - - blink::WebDragOperation current_drag_effect_; - - uint32 time_offset_ms_; - int click_count_; - - base::WeakPtrFactory<EventSender> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(EventSender); -}; - -} // namespace content - -#endif // CONTENT_SHELL_RENDERER_TEST_RUNNER_EVENT_SENDER_H_ |