blob: b9c0e4bf6516f82b401e57d4e691ab47d3fb7c0b [file] [log] [blame]
// Copyright 2017 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 <vector>
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/renderer_host/input/synthetic_gesture.h"
#include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/input/synthetic_gesture_params.h"
#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/tracing_controller.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace {
const char kMouseUpDownDataURL[] =
"data:text/html;charset=utf-8,"
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>Mouse event trace events reported.</title>"
"<script src=\"../../resources/testharness.js\"></script>"
"<script src=\"../../resources/testharnessreport.js\"></script>"
"<style>"
"body {"
" height:3000px;"
// Prevent text selection logic from triggering, as it makes the test flaky.
" user-select: none;"
"}"
"</style>"
"</head>"
"<body>"
"</body>"
"</html>";
} // namespace
namespace content {
class MouseLatencyBrowserTest : public ContentBrowserTest {
public:
MouseLatencyBrowserTest() : loop_(base::MessageLoop::TYPE_UI) {}
~MouseLatencyBrowserTest() override {}
RenderWidgetHostImpl* GetWidgetHost() {
return RenderWidgetHostImpl::From(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
}
void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
runner_->Quit();
}
void OnTraceDataCollected(
std::unique_ptr<const base::DictionaryValue> metadata,
base::RefCountedString* trace_data_string) {
std::unique_ptr<base::Value> trace_data =
base::JSONReader::Read(trace_data_string->data());
ASSERT_TRUE(trace_data);
trace_data_ = *trace_data;
runner_->Quit();
}
protected:
void LoadURL() {
const GURL data_url(kMouseUpDownDataURL);
NavigateToURL(shell(), data_url);
RenderWidgetHostImpl* host = GetWidgetHost();
host->GetView()->SetSize(gfx::Size(400, 400));
}
// Generate mouse events for a synthetic click at |point|.
void DoSyncClick(const gfx::PointF& position) {
SyntheticTapGestureParams params;
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.position = position;
params.duration_ms = 100;
std::unique_ptr<SyntheticTapGesture> gesture(
new SyntheticTapGesture(params));
GetWidgetHost()->QueueSyntheticGesture(
std::move(gesture),
base::Bind(&MouseLatencyBrowserTest::OnSyntheticGestureCompleted,
base::Unretained(this)));
// Runs until we get the OnSyntheticGestureCompleted callback
runner_ = base::MakeUnique<base::RunLoop>();
runner_->Run();
}
void StartTracing() {
base::trace_event::TraceConfig trace_config(
"{"
"\"enable_argument_filter\":false,"
"\"enable_systrace\":false,"
"\"included_categories\":["
"\"latencyInfo\""
"],"
"\"record_mode\":\"record-until-full\""
"}");
base::RunLoop run_loop;
ASSERT_TRUE(TracingController::GetInstance()->StartTracing(
trace_config, run_loop.QuitClosure()));
}
const base::Value& StopTracing() {
bool success = TracingController::GetInstance()->StopTracing(
TracingController::CreateStringSink(
base::Bind(&MouseLatencyBrowserTest::OnTraceDataCollected,
base::Unretained(this))));
EXPECT_TRUE(success);
// Runs until we get the OnTraceDataCollected callback, which populates
// trace_data_;
runner_ = base::MakeUnique<base::RunLoop>();
runner_->Run();
return trace_data_;
}
private:
base::MessageLoop loop_;
std::unique_ptr<base::RunLoop> runner_;
base::Value trace_data_;
DISALLOW_COPY_AND_ASSIGN(MouseLatencyBrowserTest);
};
// Ensures that LatencyInfo async slices are reported correctly for MouseUp and
// MouseDown events in the case where no swap is generated.
// Disabled on Android because we don't support synthetic mouse input on
// Android (crbug.com/723618).
// Test is flaky on Linux (crbug.com/736836).
#if defined (OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_MouseDownAndUpRecordedWithoutSwap \
DISABLED_MouseDownAndUpRecordedWithoutSwap
#else
#define MAYBE_MouseDownAndUpRecordedWithoutSwap \
MouseDownAndUpRecordedWithoutSwap
#endif
IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
MAYBE_MouseDownAndUpRecordedWithoutSwap) {
LoadURL();
StartTracing();
DoSyncClick(gfx::PointF(100, 100));
const base::Value trace_data = StopTracing();
const base::DictionaryValue* trace_data_dict;
trace_data.GetAsDictionary(&trace_data_dict);
ASSERT_TRUE(trace_data.GetAsDictionary(&trace_data_dict));
const base::ListValue* traceEvents;
ASSERT_TRUE(trace_data_dict->GetList("traceEvents", &traceEvents));
std::vector<std::string> trace_event_names;
for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
const base::DictionaryValue* traceEvent;
ASSERT_TRUE(traceEvents->GetDictionary(i, &traceEvent));
std::string name;
ASSERT_TRUE(traceEvent->GetString("name", &name));
if (name != "InputLatency::MouseUp" && name != "InputLatency::MouseDown")
continue;
trace_event_names.push_back(name);
}
// We see two events per async slice, a begin and an end.
EXPECT_THAT(
trace_event_names,
testing::ElementsAre("InputLatency::MouseDown", "InputLatency::MouseDown",
"InputLatency::MouseUp", "InputLatency::MouseUp"));
}
} // namespace content