diff options
Diffstat (limited to 'content/shell/renderer/test_runner/event_sender.cc')
-rw-r--r-- | content/shell/renderer/test_runner/event_sender.cc | 2085 |
1 files changed, 2085 insertions, 0 deletions
diff --git a/content/shell/renderer/test_runner/event_sender.cc b/content/shell/renderer/test_runner/event_sender.cc new file mode 100644 index 0000000..12063af --- /dev/null +++ b/content/shell/renderer/test_runner/event_sender.cc @@ -0,0 +1,2085 @@ +// 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), + 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(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(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(float scale_factor, int x, int y) { + if (sender_) + 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 = 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 |