1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/auto_reset.h" 6 #include "base/bind.h" 7 #include "base/command_line.h" 8 #include "base/run_loop.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "content/browser/renderer_host/input/synthetic_gesture.h" 11 #include "content/browser/renderer_host/input/synthetic_gesture_controller.h" 12 #include "content/browser/renderer_host/input/synthetic_gesture_target.h" 13 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" 14 #include "content/browser/renderer_host/input/touch_event_queue.h" 15 #include "content/browser/renderer_host/render_widget_host_impl.h" 16 #include "content/browser/renderer_host/render_widget_host_view_base.h" 17 #include "content/browser/web_contents/web_contents_impl.h" 18 #include "content/common/input/synthetic_gesture_params.h" 19 #include "content/common/input/synthetic_smooth_scroll_gesture_params.h" 20 #include "content/common/input_messages.h" 21 #include "content/public/browser/render_view_host.h" 22 #include "content/public/browser/render_widget_host_view.h" 23 #include "content/public/common/content_switches.h" 24 #include "content/public/test/browser_test_utils.h" 25 #include "content/public/test/content_browser_test.h" 26 #include "content/public/test/content_browser_test_utils.h" 27 #include "content/public/test/test_utils.h" 28 #include "content/shell/browser/shell.h" 29 #include "third_party/WebKit/public/web/WebInputEvent.h" 30 #include "ui/events/event_switches.h" 31 #include "ui/events/latency_info.h" 32 33 using blink::WebInputEvent; 34 35 namespace { 36 37 const char kTouchActionDataURL[] = 38 "data:text/html;charset=utf-8," 39 "<!DOCTYPE html>" 40 "<meta name='viewport' content='width=device-width'/>" 41 "<style>" 42 "html, body {" 43 " margin: 0;" 44 "}" 45 ".box {" 46 " height: 96px;" 47 " width: 96px;" 48 " border: 2px solid blue;" 49 "}" 50 ".spacer { height: 1000px; }" 51 ".ta-none { touch-action: none; }" 52 "</style>" 53 "<div class=box></div>" 54 "<div class='box ta-none'></div>" 55 "<div class=spacer></div>" 56 "<script>" 57 " window.eventCounts = " 58 " {touchstart:0, touchmove:0, touchend: 0, touchcancel:0};" 59 " function countEvent(e) { eventCounts[e.type]++; }" 60 " for (var evt in eventCounts) { " 61 " document.addEventListener(evt, countEvent); " 62 " }" 63 " document.title='ready';" 64 "</script>"; 65 66 } // namespace 67 68 namespace content { 69 70 71 class TouchActionBrowserTest : public ContentBrowserTest { 72 public: 73 TouchActionBrowserTest() {} 74 virtual ~TouchActionBrowserTest() {} 75 76 RenderWidgetHostImpl* GetWidgetHost() { 77 return RenderWidgetHostImpl::From(shell()->web_contents()-> 78 GetRenderViewHost()); 79 } 80 81 void OnSyntheticGestureCompleted(SyntheticGesture::Result result) { 82 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result); 83 runner_->Quit(); 84 } 85 86 protected: 87 void LoadURL() { 88 const GURL data_url(kTouchActionDataURL); 89 NavigateToURL(shell(), data_url); 90 91 RenderWidgetHostImpl* host = GetWidgetHost(); 92 host->GetView()->SetSize(gfx::Size(400, 400)); 93 94 base::string16 ready_title(base::ASCIIToUTF16("ready")); 95 TitleWatcher watcher(shell()->web_contents(), ready_title); 96 ignore_result(watcher.WaitAndGetTitle()); 97 } 98 99 // ContentBrowserTest: 100 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE { 101 cmd->AppendSwitchASCII(switches::kTouchEvents, 102 switches::kTouchEventsEnabled); 103 // TODO(rbyers): Remove this switch once touch-action ships. 104 // http://crbug.com/241964 105 cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures); 106 } 107 108 int ExecuteScriptAndExtractInt(const std::string& script) { 109 int value = 0; 110 EXPECT_TRUE(content::ExecuteScriptAndExtractInt( 111 shell()->web_contents(), 112 "domAutomationController.send(" + script + ")", 113 &value)); 114 return value; 115 } 116 117 int GetScrollTop() { 118 return ExecuteScriptAndExtractInt("document.documentElement.scrollTop"); 119 } 120 121 // Generate touch events for a synthetic scroll from |point| for |distance|. 122 // Returns true if the page scrolled by the desired amount, and false if 123 // it didn't scroll at all. 124 bool DoTouchScroll(const gfx::Point& point, const gfx::Vector2d& distance) { 125 EXPECT_EQ(0, GetScrollTop()); 126 127 int scrollHeight = ExecuteScriptAndExtractInt( 128 "document.documentElement.scrollHeight"); 129 EXPECT_EQ(1200, scrollHeight); 130 131 SyntheticSmoothScrollGestureParams params; 132 params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; 133 params.anchor = point; 134 params.distances.push_back(-distance); 135 136 runner_ = new MessageLoopRunner(); 137 138 scoped_ptr<SyntheticSmoothScrollGesture> gesture( 139 new SyntheticSmoothScrollGesture(params)); 140 GetWidgetHost()->QueueSyntheticGesture( 141 gesture.PassAs<SyntheticGesture>(), 142 base::Bind(&TouchActionBrowserTest::OnSyntheticGestureCompleted, 143 base::Unretained(this))); 144 145 // Runs until we get the OnSyntheticGestureCompleted callback 146 runner_->Run(); 147 runner_ = NULL; 148 149 // Check the scroll offset 150 int scrollTop = GetScrollTop(); 151 if (scrollTop == 0) 152 return false; 153 154 EXPECT_EQ(distance.y(), scrollTop); 155 return true; 156 } 157 158 private: 159 scoped_refptr<MessageLoopRunner> runner_; 160 161 DISALLOW_COPY_AND_ASSIGN(TouchActionBrowserTest); 162 }; 163 164 // TouchActionBrowserTest.DefaultAuto fails under ThreadSanitizer v2, see 165 // http://crbug.com/348539 and is flaky on XP, see 166 // http://crbug.com/354763 167 // 168 // Mac doesn't yet have a gesture recognizer, so can't support turning touch 169 // events into scroll gestures. 170 // Will be fixed with http://crbug.com/337142 171 // 172 // Verify the test infrastructure works - we can touch-scroll the page and get a 173 // touchcancel as expected. 174 IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, DISABLED_DefaultAuto) { 175 LoadURL(); 176 177 bool scrolled = DoTouchScroll(gfx::Point(50, 50), gfx::Vector2d(0, 45)); 178 EXPECT_TRUE(scrolled); 179 180 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart")); 181 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchmove")); 182 if (TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT == 183 TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL) { 184 EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchend")); 185 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchcancel")); 186 } else { 187 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend")); 188 EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel")); 189 } 190 } 191 192 // Verify that touching a touch-action: none region disables scrolling and 193 // enables all touch events to be sent. 194 // Disabled on MacOS because it doesn't support touch input. 195 // Flaky on OS_CHROMEOS http://crbug.com/376695. 196 // Also flaky on Linux Tests (TSan v2) http://crbug.com/376668. 197 #if defined(OS_MACOSX) || defined(OS_CHROMEOS) 198 #define MAYBE_TouchActionNone DISABLED_TouchActionNone 199 #elif defined(THREAD_SANITIZER) && defined(OS_LINUX) 200 #define MAYBE_TouchActionNone DISABLED_TouchActionNone 201 #else 202 #define MAYBE_TouchActionNone TouchActionNone 203 #endif 204 IN_PROC_BROWSER_TEST_F(TouchActionBrowserTest, MAYBE_TouchActionNone) { 205 LoadURL(); 206 207 bool scrolled = DoTouchScroll(gfx::Point(50, 150), gfx::Vector2d(0, 45)); 208 EXPECT_FALSE(scrolled); 209 210 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart")); 211 EXPECT_GT(ExecuteScriptAndExtractInt("eventCounts.touchmove"), 1); 212 EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchend")); 213 EXPECT_EQ(0, ExecuteScriptAndExtractInt("eventCounts.touchcancel")); 214 } 215 216 } // namespace content 217