1 // Copyright 2017 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 <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/json/json_reader.h"
12 #include "base/run_loop.h"
13 #include "base/test/test_timeouts.h"
14 #include "build/build_config.h"
15 #include "content/browser/renderer_host/input/synthetic_gesture.h"
16 #include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
17 #include "content/browser/renderer_host/input/synthetic_gesture_target.h"
18 #include "content/browser/renderer_host/input/synthetic_smooth_move_gesture.h"
19 #include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
20 #include "content/browser/renderer_host/render_widget_host_factory.h"
21 #include "content/browser/renderer_host/render_widget_host_impl.h"
22 #include "content/browser/web_contents/web_contents_impl.h"
23 #include "content/common/input/synthetic_gesture_params.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/render_widget_host_view.h"
26 #include "content/public/browser/tracing_controller.h"
27 #include "content/public/test/browser_test_utils.h"
28 #include "content/public/test/content_browser_test.h"
29 #include "content/public/test/content_browser_test_utils.h"
30 #include "content/shell/browser/shell.h"
31 #include "mojo/public/cpp/bindings/pending_remote.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 
34 namespace {
35 
36 const char kDataURL[] =
37     "data:text/html;charset=utf-8,"
38     "<!DOCTYPE html>"
39     "<html>"
40     "<head>"
41     "<title>Mouse event trace events reported.</title>"
42     "<script>"
43     "  let i=0;"
44     "  document.addEventListener('mousemove', () => {"
45     "    var end = performance.now() + 20;"
46     "    while(performance.now() < end);"
47     "    document.body.style.backgroundColor = 'rgb(' + (i++) + ',0,0)'"
48     "  });"
49     "  document.addEventListener('wheel', (e) => {"
50     "    if (!e.cancelable)"
51     "      return;"
52     "    var end = performance.now() + 50;"
53     "    while(performance.now() < end);"
54     "  });"
55     "</script>"
56     "<style>"
57     "body {"
58     "  height:3000px;"
59     // Prevent text selection logic from triggering, as it makes the test flaky.
60     "  user-select: none;"
61     "}"
62     "</style>"
63     "</head>"
64     "<body>"
65     "</body>"
66     "</html>";
67 
68 }  // namespace
69 
70 namespace content {
71 
72 // This class listens for terminated latency info events. It listens
73 // for both the mouse event ack and the gpu swap buffers event since
74 // the event could occur in either.
75 class TracingRenderWidgetHost : public RenderWidgetHostImpl {
76  public:
TracingRenderWidgetHost(RenderWidgetHostDelegate * delegate,RenderProcessHost * process,int32_t routing_id,mojo::PendingRemote<mojom::Widget> widget,bool hidden)77   TracingRenderWidgetHost(RenderWidgetHostDelegate* delegate,
78                           RenderProcessHost* process,
79                           int32_t routing_id,
80                           mojo::PendingRemote<mojom::Widget> widget,
81                           bool hidden)
82       : RenderWidgetHostImpl(delegate,
83                              process,
84                              routing_id,
85                              std::move(widget),
86                              hidden,
87                              std::make_unique<FrameTokenMessageQueue>()) {
88   }
89 
OnMouseEventAck(const MouseEventWithLatencyInfo & event,InputEventAckSource ack_source,InputEventAckState ack_result)90   void OnMouseEventAck(const MouseEventWithLatencyInfo& event,
91                        InputEventAckSource ack_source,
92                        InputEventAckState ack_result) override {
93     RenderWidgetHostImpl::OnMouseEventAck(event, ack_source, ack_result);
94   }
95 
96  private:
97 };
98 
99 class TracingRenderWidgetHostFactory : public RenderWidgetHostFactory {
100  public:
TracingRenderWidgetHostFactory()101   TracingRenderWidgetHostFactory() {
102     RenderWidgetHostFactory::RegisterFactory(this);
103   }
104 
~TracingRenderWidgetHostFactory()105   ~TracingRenderWidgetHostFactory() override {
106     RenderWidgetHostFactory::UnregisterFactory();
107   }
108 
CreateRenderWidgetHost(RenderWidgetHostDelegate * delegate,RenderProcessHost * process,int32_t routing_id,mojo::PendingRemote<mojom::Widget> widget_interface,bool hidden)109   std::unique_ptr<RenderWidgetHostImpl> CreateRenderWidgetHost(
110       RenderWidgetHostDelegate* delegate,
111       RenderProcessHost* process,
112       int32_t routing_id,
113       mojo::PendingRemote<mojom::Widget> widget_interface,
114       bool hidden) override {
115     return std::make_unique<TracingRenderWidgetHost>(
116         delegate, process, routing_id, std::move(widget_interface), hidden);
117   }
118 
119  private:
120   DISALLOW_COPY_AND_ASSIGN(TracingRenderWidgetHostFactory);
121 };
122 
123 class MouseLatencyBrowserTest : public ContentBrowserTest {
124  public:
MouseLatencyBrowserTest()125   MouseLatencyBrowserTest() {}
~MouseLatencyBrowserTest()126   ~MouseLatencyBrowserTest() override {}
127 
GetWidgetHost()128   RenderWidgetHostImpl* GetWidgetHost() {
129     return RenderWidgetHostImpl::From(
130         shell()->web_contents()->GetRenderViewHost()->GetWidget());
131   }
132 
OnSyntheticGestureCompleted(SyntheticGesture::Result result)133   void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
134     EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
135     runner_->Quit();
136   }
137 
OnTraceDataCollected(std::unique_ptr<std::string> trace_data_string)138   void OnTraceDataCollected(std::unique_ptr<std::string> trace_data_string) {
139     std::unique_ptr<base::Value> trace_data =
140         base::JSONReader::ReadDeprecated(*trace_data_string);
141     ASSERT_TRUE(trace_data);
142     trace_data_ = trace_data->Clone();
143     runner_->Quit();
144   }
145 
146  protected:
LoadURL()147   void LoadURL() {
148     const GURL data_url(kDataURL);
149     EXPECT_TRUE(NavigateToURL(shell(), data_url));
150 
151     RenderWidgetHostImpl* host = GetWidgetHost();
152     host->GetView()->SetSize(gfx::Size(400, 400));
153   }
154 
155   // Generate mouse events for a synthetic click at |point|.
DoSyncClick(const gfx::PointF & position)156   void DoSyncClick(const gfx::PointF& position) {
157     SyntheticTapGestureParams params;
158     params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
159     params.position = position;
160     params.duration_ms = 100;
161     std::unique_ptr<SyntheticTapGesture> gesture(
162         new SyntheticTapGesture(params));
163 
164     GetWidgetHost()->QueueSyntheticGesture(
165         std::move(gesture),
166         base::BindOnce(&MouseLatencyBrowserTest::OnSyntheticGestureCompleted,
167                        base::Unretained(this)));
168 
169     // Runs until we get the OnSyntheticGestureCompleted callback
170     runner_ = std::make_unique<base::RunLoop>();
171     runner_->Run();
172   }
173 
174   // Generate mouse events drag from |position|.
DoSyncCoalescedMoves(const gfx::PointF position,const gfx::Vector2dF & delta1,const gfx::Vector2dF & delta2)175   void DoSyncCoalescedMoves(const gfx::PointF position,
176                             const gfx::Vector2dF& delta1,
177                             const gfx::Vector2dF& delta2) {
178     SyntheticSmoothMoveGestureParams params;
179     params.input_type = SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT;
180     params.start_point.SetPoint(position.x(), position.y());
181     params.distances.push_back(delta1);
182     params.distances.push_back(delta2);
183 
184     std::unique_ptr<SyntheticSmoothMoveGesture> gesture(
185         new SyntheticSmoothMoveGesture(params));
186 
187     GetWidgetHost()->QueueSyntheticGesture(
188         std::move(gesture),
189         base::BindOnce(&MouseLatencyBrowserTest::OnSyntheticGestureCompleted,
190                        base::Unretained(this)));
191 
192     // Runs until we get the OnSyntheticGestureCompleted callback
193     runner_ = std::make_unique<base::RunLoop>();
194     runner_->Run();
195   }
196 
197   // Generate mouse wheel scroll.
DoSyncCoalescedMouseWheel(const gfx::PointF position,const gfx::Vector2dF & delta)198   void DoSyncCoalescedMouseWheel(const gfx::PointF position,
199                                  const gfx::Vector2dF& delta) {
200     SyntheticSmoothScrollGestureParams params;
201     params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
202     params.anchor = position;
203     params.distances.push_back(delta);
204 
205     GetWidgetHost()->QueueSyntheticGesture(
206         SyntheticGesture::Create(params),
207         base::BindOnce(&MouseLatencyBrowserTest::OnSyntheticGestureCompleted,
208                        base::Unretained(this)));
209 
210     // Runs until we get the OnSyntheticGestureCompleted callback
211     runner_ = std::make_unique<base::RunLoop>();
212     runner_->Run();
213   }
214 
StartTracing()215   void StartTracing() {
216     base::trace_event::TraceConfig trace_config(
217         "{"
218         "\"enable_argument_filter\":false,"
219         "\"enable_systrace\":false,"
220         "\"included_categories\":["
221         "\"latencyInfo\""
222         "],"
223         "\"record_mode\":\"record-until-full\""
224         "}");
225 
226     base::RunLoop run_loop;
227     ASSERT_TRUE(TracingController::GetInstance()->StartTracing(
228         trace_config, run_loop.QuitClosure()));
229     run_loop.Run();
230   }
231 
StopTracing()232   const base::Value& StopTracing() {
233     bool success = TracingController::GetInstance()->StopTracing(
234         TracingController::CreateStringEndpoint(
235             base::BindOnce(&MouseLatencyBrowserTest::OnTraceDataCollected,
236                            base::Unretained(this))));
237     EXPECT_TRUE(success);
238 
239     // Runs until we get the OnTraceDataCollected callback, which populates
240     // trace_data_;
241     runner_ = std::make_unique<base::RunLoop>();
242     runner_->Run();
243     return trace_data_;
244   }
245 
ShowTraceEventsWithId(const std::string & id_to_show,const base::ListValue * traceEvents)246   std::string ShowTraceEventsWithId(const std::string& id_to_show,
247                                     const base::ListValue* traceEvents) {
248     std::stringstream stream;
249     for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
250       const base::DictionaryValue* traceEvent;
251       if (!traceEvents->GetDictionary(i, &traceEvent))
252         continue;
253 
254       std::string id;
255       if (!traceEvent->GetString("id", &id))
256         continue;
257 
258       if (id == id_to_show)
259         stream << *traceEvent;
260     }
261     return stream.str();
262   }
263 
AssertTraceIdsBeginAndEnd(const base::Value & trace_data,const std::string & trace_event_name)264   void AssertTraceIdsBeginAndEnd(const base::Value& trace_data,
265                                  const std::string& trace_event_name) {
266     const base::DictionaryValue* trace_data_dict;
267     ASSERT_TRUE(trace_data.GetAsDictionary(&trace_data_dict));
268 
269     const base::ListValue* traceEvents;
270     ASSERT_TRUE(trace_data_dict->GetList("traceEvents", &traceEvents));
271 
272     std::map<std::string, int> trace_ids;
273 
274     for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
275       const base::DictionaryValue* traceEvent;
276       ASSERT_TRUE(traceEvents->GetDictionary(i, &traceEvent));
277 
278       std::string name;
279       ASSERT_TRUE(traceEvent->GetString("name", &name));
280 
281       if (name != trace_event_name)
282         continue;
283 
284       std::string id;
285       if (traceEvent->GetString("id", &id))
286         ++trace_ids[id];
287     }
288 
289     for (auto i : trace_ids) {
290       // Each trace id should show up once for the begin, and once for the end.
291       EXPECT_EQ(2, i.second) << ShowTraceEventsWithId(i.first, traceEvents);
292     }
293   }
294 
295  private:
296   std::unique_ptr<base::RunLoop> runner_;
297   base::Value trace_data_;
298   TracingRenderWidgetHostFactory widget_factory_;
299 
300   DISALLOW_COPY_AND_ASSIGN(MouseLatencyBrowserTest);
301 };
302 
303 // Ensures that LatencyInfo async slices are reported correctly for MouseUp and
304 // MouseDown events in the case where no swap is generated.
305 // Disabled on Android because we don't support synthetic mouse input on
306 // Android (crbug.com/723618).
307 // Disabled on Windows due to flakyness (https://crbug.com/800303).
308 // Disabled on Linux due to flakyness (https://crbug.com/815363).
309 #if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_LINUX)
310 #define MAYBE_MouseDownAndUpRecordedWithoutSwap \
311   DISABLED_MouseDownAndUpRecordedWithoutSwap
312 #else
313 #define MAYBE_MouseDownAndUpRecordedWithoutSwap \
314   MouseDownAndUpRecordedWithoutSwap
315 #endif
IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,MAYBE_MouseDownAndUpRecordedWithoutSwap)316 IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
317                        MAYBE_MouseDownAndUpRecordedWithoutSwap) {
318   LoadURL();
319 
320   auto filter = std::make_unique<InputMsgWatcher>(
321       GetWidgetHost(), blink::WebInputEvent::kMouseUp);
322   StartTracing();
323   DoSyncClick(gfx::PointF(100, 100));
324   EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
325             filter->GetAckStateWaitIfNecessary());
326   const base::Value& trace_data = StopTracing();
327 
328   const base::DictionaryValue* trace_data_dict;
329   trace_data.GetAsDictionary(&trace_data_dict);
330   ASSERT_TRUE(trace_data.GetAsDictionary(&trace_data_dict));
331 
332   const base::ListValue* traceEvents;
333   ASSERT_TRUE(trace_data_dict->GetList("traceEvents", &traceEvents));
334 
335   std::vector<std::string> trace_event_names;
336 
337   for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
338     const base::DictionaryValue* traceEvent;
339     ASSERT_TRUE(traceEvents->GetDictionary(i, &traceEvent));
340 
341     std::string name;
342     ASSERT_TRUE(traceEvent->GetString("name", &name));
343 
344     if (name != "InputLatency::MouseUp" && name != "InputLatency::MouseDown")
345       continue;
346     trace_event_names.push_back(name);
347   }
348 
349   // We see two events per async slice, a begin and an end.
350   EXPECT_THAT(trace_event_names,
351               testing::UnorderedElementsAre(
352                   "InputLatency::MouseDown", "InputLatency::MouseDown",
353                   "InputLatency::MouseUp", "InputLatency::MouseUp"));
354 }
355 
356 // Ensures that LatencyInfo async slices are reported correctly for MouseMove
357 // events in the case where events are coalesced. (crbug.com/771165).
358 // Disabled on Android because we don't support synthetic mouse input on Android
359 // (crbug.com/723618).
360 // http://crbug.com/801629 : Flaky on Linux and Windows, and Mac with
361 // --enable-features=VizDisplayCompositor
362 #if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_LINUX)
363 #define MAYBE_CoalescedMouseMovesCorrectlyTerminated \
364   DISABLED_CoalescedMouseMovesCorrectlyTerminated
365 #else
366 #define MAYBE_CoalescedMouseMovesCorrectlyTerminated \
367   CoalescedMouseMovesCorrectlyTerminated
368 #endif
IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,MAYBE_CoalescedMouseMovesCorrectlyTerminated)369 IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
370                        MAYBE_CoalescedMouseMovesCorrectlyTerminated) {
371   LoadURL();
372 
373   StartTracing();
374   DoSyncCoalescedMoves(gfx::PointF(100, 100), gfx::Vector2dF(150, 150),
375                        gfx::Vector2dF(250, 250));
376   // The following wait is the upper bound for gpu swap completed callback. It
377   // is two frames to account for double buffering.
378   MainThreadFrameObserver observer(RenderWidgetHostImpl::From(
379       shell()->web_contents()->GetRenderViewHost()->GetWidget()));
380   observer.Wait();
381   observer.Wait();
382 
383   const base::Value& trace_data = StopTracing();
384 
385   AssertTraceIdsBeginAndEnd(trace_data, "InputLatency::MouseMove");
386 }
387 
388 // TODO(https://crbug.com/923627): This is flaky on multiple platforms.
IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,DISABLED_CoalescedMouseWheelsCorrectlyTerminated)389 IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
390                        DISABLED_CoalescedMouseWheelsCorrectlyTerminated) {
391   LoadURL();
392 
393   StartTracing();
394   DoSyncCoalescedMouseWheel(gfx::PointF(100, 100), gfx::Vector2dF(0, -100));
395   const base::Value& trace_data = StopTracing();
396 
397   AssertTraceIdsBeginAndEnd(trace_data, "InputLatency::MouseWheel");
398 }
399 
400 }  // namespace content
401