1 // Copyright 2013 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 "content/renderer/render_widget.h"
6
7 #include <tuple>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/macros.h"
13 #include "base/optional.h"
14 #include "base/run_loop.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/test/task_environment.h"
17 #include "base/unguessable_token.h"
18 #include "build/build_config.h"
19 #include "cc/layers/solid_color_layer.h"
20 #include "cc/test/fake_layer_tree_frame_sink.h"
21 #include "cc/trees/layer_tree_host.h"
22 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
23 #include "content/common/frame_replication_state.h"
24 #include "content/common/input/input_handler.mojom.h"
25 #include "content/common/input/synthetic_web_input_event_builders.h"
26 #include "content/common/input_messages.h"
27 #include "content/common/view_messages.h"
28 #include "content/common/visual_properties.h"
29 #include "content/common/widget_messages.h"
30 #include "content/public/common/content_features.h"
31 #include "content/public/test/mock_render_thread.h"
32 #include "content/renderer/input/widget_input_handler_manager.h"
33 #include "content/renderer/render_frame_proxy.h"
34 #include "content/renderer/render_widget_delegate.h"
35 #include "content/renderer/render_widget_screen_metrics_emulator.h"
36 #include "content/test/fake_compositor_dependencies.h"
37 #include "content/test/mock_render_process.h"
38 #include "ipc/ipc_test_sink.h"
39 #include "testing/gmock/include/gmock/gmock.h"
40 #include "testing/gtest/include/gtest/gtest.h"
41 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
42 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
43 #include "third_party/blink/public/web/web_device_emulation_params.h"
44 #include "third_party/blink/public/web/web_external_widget.h"
45 #include "third_party/blink/public/web/web_external_widget_client.h"
46 #include "third_party/blink/public/web/web_frame_widget.h"
47 #include "third_party/blink/public/web/web_local_frame.h"
48 #include "third_party/blink/public/web/web_local_frame_client.h"
49 #include "third_party/blink/public/web/web_view.h"
50 #include "third_party/blink/public/web/web_view_client.h"
51 #include "ui/base/cursor/cursor.h"
52 #include "ui/base/mojom/cursor_type.mojom-shared.h"
53 #include "ui/events/base_event_utils.h"
54 #include "ui/events/blink/web_input_event_traits.h"
55 #include "ui/gfx/geometry/rect.h"
56
57 using testing::_;
58
59 namespace ui {
60
operator ==(const ui::DidOverscrollParams & lhs,const ui::DidOverscrollParams & rhs)61 bool operator==(const ui::DidOverscrollParams& lhs,
62 const ui::DidOverscrollParams& rhs) {
63 return lhs.accumulated_overscroll == rhs.accumulated_overscroll &&
64 lhs.latest_overscroll_delta == rhs.latest_overscroll_delta &&
65 lhs.current_fling_velocity == rhs.current_fling_velocity &&
66 lhs.causal_event_viewport_point == rhs.causal_event_viewport_point &&
67 lhs.overscroll_behavior == rhs.overscroll_behavior;
68 }
69
70 } // namespace ui
71
72 namespace cc {
73 class AnimationHost;
74 }
75
76 namespace content {
77
78 namespace {
79
80 const char* EVENT_LISTENER_RESULT_HISTOGRAM = "Event.PassiveListeners";
81
82 // Keep in sync with enum defined in
83 // RenderWidgetInputHandler::LogPassiveEventListenersUma.
84 enum {
85 PASSIVE_LISTENER_UMA_ENUM_PASSIVE,
86 PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
87 PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
88 PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
89 PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
90 PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
91 PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_MAIN_THREAD_RESPONSIVENESS_DEPRECATED,
92 PASSIVE_LISTENER_UMA_ENUM_COUNT
93 };
94
95 class MockWidgetInputHandlerHost : public mojom::WidgetInputHandlerHost {
96 public:
MockWidgetInputHandlerHost()97 MockWidgetInputHandlerHost() {}
98 #if defined(OS_ANDROID)
99 MOCK_METHOD4(FallbackCursorModeLockCursor, void(bool, bool, bool, bool));
100
101 MOCK_METHOD1(FallbackCursorModeSetCursorVisibility, void(bool));
102 #endif
103
104 MOCK_METHOD1(SetTouchActionFromMain, void(cc::TouchAction));
105
106 MOCK_METHOD3(SetWhiteListedTouchAction,
107 void(cc::TouchAction, uint32_t, content::InputEventAckState));
108
109 MOCK_METHOD1(DidOverscroll, void(const ui::DidOverscrollParams&));
110
111 MOCK_METHOD0(DidStopFlinging, void());
112
113 MOCK_METHOD0(DidStartScrollingViewport, void());
114
115 MOCK_METHOD0(ImeCancelComposition, void());
116
117 MOCK_METHOD2(ImeCompositionRangeChanged,
118 void(const gfx::Range&, const std::vector<gfx::Rect>&));
119
120 MOCK_METHOD1(SetMouseCapture, void(bool));
121
122 MOCK_METHOD0(UnlockMouse, void());
123
124 MOCK_METHOD4(RequestMouseLock,
125 void((bool,
126 bool,
127 bool,
128 mojom::WidgetInputHandlerHost::RequestMouseLockCallback)));
129
130 MOCK_METHOD2(RequestMouseLockChange,
131 void((bool,
132 mojom::WidgetInputHandlerHost::RequestMouseLockCallback)));
133
134 mojo::PendingRemote<mojom::WidgetInputHandlerHost>
BindNewPipeAndPassRemote()135 BindNewPipeAndPassRemote() {
136 return receiver_.BindNewPipeAndPassRemote();
137 }
138
139 private:
140 mojo::Receiver<mojom::WidgetInputHandlerHost> receiver_{this};
141
142 DISALLOW_COPY_AND_ASSIGN(MockWidgetInputHandlerHost);
143 };
144
145 // Since std::unique_ptr isn't copyable we can't use the
146 // MockCallback template.
147 class MockHandledEventCallback {
148 public:
149 MockHandledEventCallback() = default;
150 MOCK_METHOD4_T(Run,
151 void(InputEventAckState,
152 const ui::LatencyInfo&,
153 std::unique_ptr<ui::DidOverscrollParams>&,
154 base::Optional<cc::TouchAction>));
155
GetCallback()156 HandledEventCallback GetCallback() {
157 return base::BindOnce(&MockHandledEventCallback::HandleCallback,
158 base::Unretained(this));
159 }
160
161 private:
HandleCallback(InputEventAckState ack_state,const ui::LatencyInfo & latency_info,std::unique_ptr<ui::DidOverscrollParams> overscroll,base::Optional<cc::TouchAction> touch_action)162 void HandleCallback(InputEventAckState ack_state,
163 const ui::LatencyInfo& latency_info,
164 std::unique_ptr<ui::DidOverscrollParams> overscroll,
165 base::Optional<cc::TouchAction> touch_action) {
166 Run(ack_state, latency_info, overscroll, touch_action);
167 }
168
169 DISALLOW_COPY_AND_ASSIGN(MockHandledEventCallback);
170 };
171
172 class MockWebExternalWidgetClient : public blink::WebExternalWidgetClient {
173 public:
174 MockWebExternalWidgetClient() = default;
175
176 // WebExternalWidgetClient implementation.
177 MOCK_METHOD1(DidResize, void(const gfx::Size& size));
178 MOCK_METHOD0(DispatchBufferedTouchEvents, blink::WebInputEventResult());
179 MOCK_METHOD1(
180 HandleInputEvent,
181 blink::WebInputEventResult(const blink::WebCoalescedInputEvent&));
182 };
183
184 } // namespace
185
186 class InteractiveRenderWidget : public RenderWidget {
187 public:
InteractiveRenderWidget(CompositorDependencies * compositor_deps)188 explicit InteractiveRenderWidget(CompositorDependencies* compositor_deps)
189 : RenderWidget(++next_routing_id_,
190 compositor_deps,
191 blink::mojom::DisplayMode::kUndefined,
192 /*is_hidden=*/false,
193 /*never_composited=*/false,
194 mojo::NullReceiver()) {}
195
Init(blink::WebWidget * widget,const ScreenInfo & screen_info)196 void Init(blink::WebWidget* widget, const ScreenInfo& screen_info) {
197 Initialize(base::NullCallback(), widget, screen_info);
198
199 mock_input_handler_host_ = std::make_unique<MockWidgetInputHandlerHost>();
200
201 widget_input_handler_manager_->AddInterface(
202 mojo::PendingReceiver<mojom::WidgetInputHandler>(),
203 mock_input_handler_host_->BindNewPipeAndPassRemote());
204 }
205
206 using RenderWidget::Close;
207
SendInputEvent(const blink::WebInputEvent & event,HandledEventCallback callback)208 void SendInputEvent(const blink::WebInputEvent& event,
209 HandledEventCallback callback) {
210 HandleInputEvent(blink::WebCoalescedInputEvent(
211 event, std::vector<const blink::WebInputEvent*>(),
212 std::vector<const blink::WebInputEvent*>()),
213 ui::LatencyInfo(), std::move(callback));
214 }
215
set_always_overscroll(bool overscroll)216 void set_always_overscroll(bool overscroll) {
217 always_overscroll_ = overscroll;
218 }
219
sink()220 IPC::TestSink* sink() { return &sink_; }
221
mock_input_handler_host()222 MockWidgetInputHandlerHost* mock_input_handler_host() {
223 return mock_input_handler_host_.get();
224 }
225
local_surface_id_allocation_from_parent() const226 const viz::LocalSurfaceIdAllocation& local_surface_id_allocation_from_parent()
227 const {
228 return local_surface_id_allocation_from_parent_;
229 }
230
231 protected:
232 // Overridden from RenderWidget:
WillHandleGestureEvent(const blink::WebGestureEvent & event)233 bool WillHandleGestureEvent(const blink::WebGestureEvent& event) override {
234 if (always_overscroll_ &&
235 event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
236 DidOverscroll(gfx::Vector2dF(event.data.scroll_update.delta_x,
237 event.data.scroll_update.delta_y),
238 gfx::Vector2dF(event.data.scroll_update.delta_x,
239 event.data.scroll_update.delta_y),
240 event.PositionInWidget(),
241 gfx::Vector2dF(event.data.scroll_update.velocity_x,
242 event.data.scroll_update.velocity_y));
243 return true;
244 }
245
246 return false;
247 }
248
Send(IPC::Message * msg)249 bool Send(IPC::Message* msg) override {
250 sink_.OnMessageReceived(*msg);
251 delete msg;
252 return true;
253 }
254
255 private:
256 IPC::TestSink sink_;
257 bool always_overscroll_ = false;
258 std::unique_ptr<MockWidgetInputHandlerHost> mock_input_handler_host_;
259 static int next_routing_id_;
260
261 DISALLOW_COPY_AND_ASSIGN(InteractiveRenderWidget);
262 };
263
264 int InteractiveRenderWidget::next_routing_id_ = 0;
265
266 class RenderWidgetUnittest : public testing::Test {
267 public:
SetUp()268 void SetUp() override {
269 web_view_ = blink::WebView::Create(/*client=*/&web_view_client_,
270 /*is_hidden=*/false,
271 /*compositing_enabled=*/true, nullptr,
272 mojo::ScopedInterfaceEndpointHandle());
273 widget_ = std::make_unique<InteractiveRenderWidget>(&compositor_deps_);
274 web_local_frame_ = blink::WebLocalFrame::CreateMainFrame(
275 web_view_, &web_frame_client_, nullptr, nullptr);
276 web_frame_widget_ = blink::WebFrameWidget::CreateForMainFrame(
277 widget_.get(), web_local_frame_,
278 blink::CrossVariantMojoAssociatedRemote<
279 blink::mojom::FrameWidgetHostInterfaceBase>(),
280 blink::CrossVariantMojoAssociatedReceiver<
281 blink::mojom::FrameWidgetInterfaceBase>(),
282 blink::CrossVariantMojoAssociatedRemote<
283 blink::mojom::WidgetHostInterfaceBase>(),
284 blink::CrossVariantMojoAssociatedReceiver<
285 blink::mojom::WidgetInterfaceBase>());
286 widget_->Init(web_frame_widget_, ScreenInfo());
287 web_view_->DidAttachLocalMainFrame();
288 }
289
TearDown()290 void TearDown() override {
291 widget_->Close(std::move(widget_));
292 web_local_frame_ = nullptr;
293 web_frame_widget_ = nullptr;
294 web_view_ = nullptr;
295 // RenderWidget::Close() posts some destruction. Don't leak them.
296 base::RunLoop loop;
297 compositor_deps_.GetCleanupTaskRunner()->PostTask(FROM_HERE,
298 loop.QuitClosure());
299 loop.Run();
300 }
301
widget() const302 InteractiveRenderWidget* widget() const { return widget_.get(); }
303
frame_widget() const304 blink::WebFrameWidget* frame_widget() const { return web_frame_widget_; }
305
histogram_tester() const306 const base::HistogramTester& histogram_tester() const {
307 return histogram_tester_;
308 }
309
GetFrameSink()310 cc::FakeLayerTreeFrameSink* GetFrameSink() {
311 return compositor_deps_.last_created_frame_sink();
312 }
313
314 private:
315 base::test::TaskEnvironment task_environment_;
316 MockRenderProcess render_process_;
317 MockRenderThread render_thread_;
318 blink::WebViewClient web_view_client_;
319 blink::WebView* web_view_;
320 blink::WebLocalFrame* web_local_frame_;
321 blink::WebFrameWidget* web_frame_widget_;
322 blink::WebLocalFrameClient web_frame_client_;
323 FakeCompositorDependencies compositor_deps_;
324 std::unique_ptr<InteractiveRenderWidget> widget_;
325 base::HistogramTester histogram_tester_;
326 };
327
328 class RenderWidgetExternalWidgetUnittest : public testing::Test {
329 public:
SetUp()330 void SetUp() override {
331 external_web_widget_ = blink::WebExternalWidget::Create(
332 &mock_web_external_widget_client_, blink::WebURL(),
333 blink::CrossVariantMojoAssociatedRemote<
334 blink::mojom::WidgetHostInterfaceBase>(),
335 blink::CrossVariantMojoAssociatedReceiver<
336 blink::mojom::WidgetInterfaceBase>());
337
338 widget_ = std::make_unique<InteractiveRenderWidget>(&compositor_deps_);
339 widget_->Init(external_web_widget_.get(), ScreenInfo());
340 }
341
TearDown()342 void TearDown() override {
343 widget_->Close(std::move(widget_));
344 // RenderWidget::Close() posts some destruction. Don't leak them.
345 base::RunLoop loop;
346 compositor_deps_.GetCleanupTaskRunner()->PostTask(FROM_HERE,
347 loop.QuitClosure());
348 loop.Run();
349 }
350
widget() const351 InteractiveRenderWidget* widget() const { return widget_.get(); }
352
mock_web_external_widget_client()353 MockWebExternalWidgetClient* mock_web_external_widget_client() {
354 return &mock_web_external_widget_client_;
355 }
356
external_web_widget()357 blink::WebExternalWidget* external_web_widget() {
358 return external_web_widget_.get();
359 }
360
histogram_tester() const361 const base::HistogramTester& histogram_tester() const {
362 return histogram_tester_;
363 }
364
GetFrameSink()365 cc::FakeLayerTreeFrameSink* GetFrameSink() {
366 return compositor_deps_.last_created_frame_sink();
367 }
368
369 private:
370 base::test::TaskEnvironment task_environment_;
371 MockRenderProcess render_process_;
372 MockRenderThread render_thread_;
373 FakeCompositorDependencies compositor_deps_;
374 MockWebExternalWidgetClient mock_web_external_widget_client_;
375 std::unique_ptr<blink::WebExternalWidget> external_web_widget_;
376 std::unique_ptr<InteractiveRenderWidget> widget_;
377 base::HistogramTester histogram_tester_;
378 };
379
TEST_F(RenderWidgetExternalWidgetUnittest,CursorChange)380 TEST_F(RenderWidgetExternalWidgetUnittest, CursorChange) {
381 ui::Cursor cursor;
382
383 widget()->DidChangeCursor(cursor);
384 EXPECT_EQ(widget()->sink()->message_count(), 1U);
385 EXPECT_EQ(widget()->sink()->GetMessageAt(0)->type(),
386 WidgetHostMsg_SetCursor::ID);
387 widget()->sink()->ClearMessages();
388
389 widget()->DidChangeCursor(cursor);
390 EXPECT_EQ(widget()->sink()->message_count(), 0U);
391
392 EXPECT_CALL(*mock_web_external_widget_client(), HandleInputEvent(_))
393 .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
394 widget()->SendInputEvent(SyntheticWebMouseEventBuilder::Build(
395 blink::WebInputEvent::Type::kMouseLeave),
396 HandledEventCallback());
397 EXPECT_EQ(widget()->sink()->message_count(), 0U);
398
399 widget()->DidChangeCursor(cursor);
400 EXPECT_EQ(widget()->sink()->message_count(), 1U);
401 EXPECT_EQ(widget()->sink()->GetMessageAt(0)->type(),
402 WidgetHostMsg_SetCursor::ID);
403 }
404
TEST_F(RenderWidgetExternalWidgetUnittest,EventOverscroll)405 TEST_F(RenderWidgetExternalWidgetUnittest, EventOverscroll) {
406 widget()->set_always_overscroll(true);
407
408 EXPECT_CALL(*mock_web_external_widget_client(), HandleInputEvent(_))
409 .WillRepeatedly(
410 ::testing::Return(blink::WebInputEventResult::kNotHandled));
411
412 blink::WebGestureEvent scroll(blink::WebInputEvent::kGestureScrollUpdate,
413 blink::WebInputEvent::kNoModifiers,
414 ui::EventTimeForNow());
415 scroll.SetPositionInWidget(gfx::PointF(-10, 0));
416 scroll.data.scroll_update.delta_y = 10;
417 MockHandledEventCallback handled_event;
418
419 ui::DidOverscrollParams expected_overscroll;
420 expected_overscroll.latest_overscroll_delta = gfx::Vector2dF(0, 10);
421 expected_overscroll.accumulated_overscroll = gfx::Vector2dF(0, 10);
422 expected_overscroll.causal_event_viewport_point = gfx::PointF(-10, 0);
423 expected_overscroll.current_fling_velocity = gfx::Vector2dF();
424
425 // Overscroll notifications received while handling an input event should
426 // be bundled with the event ack IPC.
427 EXPECT_CALL(handled_event, Run(INPUT_EVENT_ACK_STATE_CONSUMED, _,
428 testing::Pointee(expected_overscroll), _))
429 .Times(1);
430
431 widget()->SendInputEvent(scroll, handled_event.GetCallback());
432 }
433
TEST_F(RenderWidgetExternalWidgetUnittest,RenderWidgetInputEventUmaMetrics)434 TEST_F(RenderWidgetExternalWidgetUnittest, RenderWidgetInputEventUmaMetrics) {
435 SyntheticWebTouchEvent touch;
436 touch.PressPoint(10, 10);
437 touch.touch_start_or_first_touch_move = true;
438
439 EXPECT_CALL(*mock_web_external_widget_client(), HandleInputEvent(_))
440 .Times(5)
441 .WillRepeatedly(
442 ::testing::Return(blink::WebInputEventResult::kNotHandled));
443
444 EXPECT_CALL(*mock_web_external_widget_client(), DispatchBufferedTouchEvents())
445 .Times(5)
446 .WillRepeatedly(
447 ::testing::Return(blink::WebInputEventResult::kNotHandled));
448
449 widget()->SendInputEvent(touch, HandledEventCallback());
450 histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
451 PASSIVE_LISTENER_UMA_ENUM_CANCELABLE, 1);
452
453 touch.dispatch_type = blink::WebInputEvent::DispatchType::kEventNonBlocking;
454 widget()->SendInputEvent(touch, HandledEventCallback());
455 histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
456 PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
457 1);
458
459 touch.dispatch_type =
460 blink::WebInputEvent::DispatchType::kListenersNonBlockingPassive;
461 widget()->SendInputEvent(touch, HandledEventCallback());
462 histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
463 PASSIVE_LISTENER_UMA_ENUM_PASSIVE, 1);
464
465 touch.dispatch_type =
466 blink::WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
467 widget()->SendInputEvent(touch, HandledEventCallback());
468 histogram_tester().ExpectBucketCount(
469 EVENT_LISTENER_RESULT_HISTOGRAM,
470 PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 1);
471
472 touch.MovePoint(0, 10, 10);
473 touch.touch_start_or_first_touch_move = true;
474 touch.dispatch_type =
475 blink::WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
476 widget()->SendInputEvent(touch, HandledEventCallback());
477 histogram_tester().ExpectBucketCount(
478 EVENT_LISTENER_RESULT_HISTOGRAM,
479 PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 2);
480
481 EXPECT_CALL(*mock_web_external_widget_client(), HandleInputEvent(_))
482 .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
483 EXPECT_CALL(*mock_web_external_widget_client(), DispatchBufferedTouchEvents())
484 .WillOnce(
485 ::testing::Return(blink::WebInputEventResult::kHandledSuppressed));
486 touch.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
487 widget()->SendInputEvent(touch, HandledEventCallback());
488 histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
489 PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED, 1);
490
491 EXPECT_CALL(*mock_web_external_widget_client(), HandleInputEvent(_))
492 .WillOnce(::testing::Return(blink::WebInputEventResult::kNotHandled));
493 EXPECT_CALL(*mock_web_external_widget_client(), DispatchBufferedTouchEvents())
494 .WillOnce(
495 ::testing::Return(blink::WebInputEventResult::kHandledApplication));
496 touch.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
497 widget()->SendInputEvent(touch, HandledEventCallback());
498 histogram_tester().ExpectBucketCount(
499 EVENT_LISTENER_RESULT_HISTOGRAM,
500 PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED, 1);
501 }
502
503 // Tests that if a RenderWidget is auto-resized, it requests a new
504 // viz::LocalSurfaceId to be allocated on the impl thread.
TEST_F(RenderWidgetUnittest,AutoResizeAllocatedLocalSurfaceId)505 TEST_F(RenderWidgetUnittest, AutoResizeAllocatedLocalSurfaceId) {
506 viz::ParentLocalSurfaceIdAllocator allocator;
507
508 // Enable auto-resize.
509 content::VisualProperties visual_properties;
510 visual_properties.auto_resize_enabled = true;
511 visual_properties.min_size_for_auto_resize = gfx::Size(100, 100);
512 visual_properties.max_size_for_auto_resize = gfx::Size(200, 200);
513 allocator.GenerateId();
514 visual_properties.local_surface_id_allocation =
515 allocator.GetCurrentLocalSurfaceIdAllocation();
516 {
517 WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
518 visual_properties);
519 widget()->OnMessageReceived(msg);
520 }
521 EXPECT_EQ(allocator.GetCurrentLocalSurfaceIdAllocation(),
522 widget()->local_surface_id_allocation_from_parent());
523 EXPECT_FALSE(widget()
524 ->layer_tree_host()
525 ->new_local_surface_id_request_for_testing());
526
527 constexpr gfx::Size size(200, 200);
528 widget()->DidAutoResize(size);
529 EXPECT_EQ(allocator.GetCurrentLocalSurfaceIdAllocation(),
530 widget()->local_surface_id_allocation_from_parent());
531 EXPECT_TRUE(widget()
532 ->layer_tree_host()
533 ->new_local_surface_id_request_for_testing());
534 }
535
536 class StubRenderWidgetDelegate : public RenderWidgetDelegate {
537 public:
SetActiveForWidget(bool active)538 void SetActiveForWidget(bool active) override {}
SupportsMultipleWindowsForWidget()539 bool SupportsMultipleWindowsForWidget() override { return true; }
ShouldAckSyntheticInputImmediately()540 bool ShouldAckSyntheticInputImmediately() override { return true; }
ApplyNewDisplayModeForWidget(blink::mojom::DisplayMode new_display_mode)541 void ApplyNewDisplayModeForWidget(
542 blink::mojom::DisplayMode new_display_mode) override {}
ApplyAutoResizeLimitsForWidget(const gfx::Size & min_size,const gfx::Size & max_size)543 void ApplyAutoResizeLimitsForWidget(const gfx::Size& min_size,
544 const gfx::Size& max_size) override {}
DisableAutoResizeForWidget()545 void DisableAutoResizeForWidget() override {}
ScrollFocusedNodeIntoViewForWidget()546 void ScrollFocusedNodeIntoViewForWidget() override {}
DidReceiveSetFocusEventForWidget()547 void DidReceiveSetFocusEventForWidget() override {}
DidCommitCompositorFrameForWidget()548 void DidCommitCompositorFrameForWidget() override {}
DidCompletePageScaleAnimationForWidget()549 void DidCompletePageScaleAnimationForWidget() override {}
ResizeWebWidgetForWidget(const gfx::Size & main_frame_widget_size,const gfx::Size & visible_viewport_size,cc::BrowserControlsParams)550 void ResizeWebWidgetForWidget(const gfx::Size& main_frame_widget_size,
551 const gfx::Size& visible_viewport_size,
552 cc::BrowserControlsParams) override {}
SetScreenMetricsEmulationParametersForWidget(bool enabled,const blink::WebDeviceEmulationParams & params)553 void SetScreenMetricsEmulationParametersForWidget(
554 bool enabled,
555 const blink::WebDeviceEmulationParams& params) override {}
556 };
557
558 // Tests that the value of VisualProperties::is_pinch_gesture_active is
559 // propagated to the LayerTreeHost when properties are synced for subframes.
TEST_F(RenderWidgetUnittest,ActivePinchGestureUpdatesLayerTreeHostSubFrame)560 TEST_F(RenderWidgetUnittest, ActivePinchGestureUpdatesLayerTreeHostSubFrame) {
561 cc::LayerTreeHost* layer_tree_host = widget()->layer_tree_host();
562 EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
563 content::VisualProperties visual_properties;
564
565 // Sync visual properties on a child RenderWidget.
566 visual_properties.is_pinch_gesture_active = true;
567 {
568 WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
569 visual_properties);
570 widget()->OnMessageReceived(msg);
571 }
572 // We expect the |is_pinch_gesture_active| value to propagate to the
573 // LayerTreeHost for sub-frames. Since GesturePinch events are handled
574 // directly in the main-frame's layer tree (and only there), information about
575 // whether or not we're in a pinch gesture must be communicated separately to
576 // sub-frame layer trees, via OnUpdateVisualProperties. This information
577 // is required to allow sub-frame compositors to throttle rastering while
578 // pinch gestures are active.
579 EXPECT_TRUE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
580 visual_properties.is_pinch_gesture_active = false;
581 {
582 WidgetMsg_UpdateVisualProperties msg(widget()->routing_id(),
583 visual_properties);
584 widget()->OnMessageReceived(msg);
585 }
586 EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
587 }
588
589 // Verify desktop memory limit calculations.
590 #if !defined(OS_ANDROID)
TEST(RenderWidgetTest,IgnoreGivenMemoryPolicy)591 TEST(RenderWidgetTest, IgnoreGivenMemoryPolicy) {
592 auto policy = RenderWidget::GetGpuMemoryPolicy(cc::ManagedMemoryPolicy(256),
593 gfx::Size(), 1.f);
594 EXPECT_EQ(512u * 1024u * 1024u, policy.bytes_limit_when_visible);
595 EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
596 policy.priority_cutoff_when_visible);
597 }
598
TEST(RenderWidgetTest,LargeScreensUseMoreMemory)599 TEST(RenderWidgetTest, LargeScreensUseMoreMemory) {
600 auto policy = RenderWidget::GetGpuMemoryPolicy(cc::ManagedMemoryPolicy(256),
601 gfx::Size(4096, 2160), 1.f);
602 EXPECT_EQ(2u * 512u * 1024u * 1024u, policy.bytes_limit_when_visible);
603 EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
604 policy.priority_cutoff_when_visible);
605
606 policy = RenderWidget::GetGpuMemoryPolicy(cc::ManagedMemoryPolicy(256),
607 gfx::Size(2048, 1080), 2.f);
608 EXPECT_EQ(2u * 512u * 1024u * 1024u, policy.bytes_limit_when_visible);
609 EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
610 policy.priority_cutoff_when_visible);
611 }
612 #endif
613
614 #if defined(OS_ANDROID)
TEST_F(RenderWidgetUnittest,ForceSendMetadataOnInput)615 TEST_F(RenderWidgetUnittest, ForceSendMetadataOnInput) {
616 cc::LayerTreeHost* layer_tree_host = widget()->layer_tree_host();
617 // We should not have any force send metadata requests at start.
618 EXPECT_FALSE(layer_tree_host->TakeForceSendMetadataRequest());
619 // ShowVirtualKeyboard will trigger a text input state update.
620 widget()->ShowVirtualKeyboard();
621 // We should now have a force send metadata request.
622 EXPECT_TRUE(layer_tree_host->TakeForceSendMetadataRequest());
623 }
624 #endif // !defined(OS_ANDROID)
625
626 class NotifySwapTimesRenderWidgetUnittest : public RenderWidgetUnittest {
627 public:
SetUp()628 void SetUp() override {
629 RenderWidgetUnittest::SetUp();
630
631 viz::ParentLocalSurfaceIdAllocator allocator;
632 WidgetMsg_WasShown msg(widget()->routing_id(),
633 /*show_request_timestamp=*/base::TimeTicks(),
634 /*was_evicted=*/false,
635 /*record_tab_switch_time_request=*/base::nullopt);
636 widget()->OnMessageReceived(msg);
637
638 // TODO(danakj): This usually happens through
639 // RenderWidget::OnUpdateVisualProperties() and we are cutting past that for
640 // some reason.
641 allocator.GenerateId();
642 widget()->layer_tree_host()->SetViewportRectAndScale(
643 gfx::Rect(200, 100), 1.f,
644 allocator.GetCurrentLocalSurfaceIdAllocation());
645
646 auto root_layer = cc::SolidColorLayer::Create();
647 root_layer->SetBounds(gfx::Size(200, 100));
648 root_layer->SetBackgroundColor(SK_ColorGREEN);
649 widget()->layer_tree_host()->SetNonBlinkManagedRootLayer(root_layer);
650
651 auto color_layer = cc::SolidColorLayer::Create();
652 color_layer->SetBounds(gfx::Size(100, 100));
653 root_layer->AddChild(color_layer);
654 color_layer->SetBackgroundColor(SK_ColorRED);
655 }
656
657 // |swap_to_presentation| determines how long after swap should presentation
658 // happen. This can be negative, positive, or zero. If zero, an invalid (null)
659 // presentation time is used.
CompositeAndWaitForPresentation(base::TimeDelta swap_to_presentation)660 void CompositeAndWaitForPresentation(base::TimeDelta swap_to_presentation) {
661 base::RunLoop swap_run_loop;
662 base::RunLoop presentation_run_loop;
663
664 // Register callbacks for swap time and presentation time.
665 base::TimeTicks swap_time;
666 frame_widget()->NotifySwapAndPresentationTime(
667 base::BindOnce(
668 [](base::OnceClosure swap_quit_closure, base::TimeTicks* swap_time,
669 blink::WebSwapResult result, base::TimeTicks timestamp) {
670 DCHECK(!timestamp.is_null());
671 *swap_time = timestamp;
672 std::move(swap_quit_closure).Run();
673 },
674 swap_run_loop.QuitClosure(), &swap_time),
675 base::BindOnce(
676 [](base::OnceClosure presentation_quit_closure,
677 blink::WebSwapResult result, base::TimeTicks timestamp) {
678 DCHECK(!timestamp.is_null());
679 std::move(presentation_quit_closure).Run();
680 },
681 presentation_run_loop.QuitClosure()));
682
683 // Composite and wait for the swap to complete.
684 widget()->layer_tree_host()->Composite(base::TimeTicks::Now(),
685 /*raster=*/true);
686 swap_run_loop.Run();
687
688 // Present and wait for it to complete.
689 viz::FrameTimingDetails timing_details;
690 if (!swap_to_presentation.is_zero()) {
691 timing_details.presentation_feedback = gfx::PresentationFeedback(
692 /*presentation_time=*/swap_time + swap_to_presentation,
693 base::TimeDelta::FromMilliseconds(16), 0);
694 }
695 GetFrameSink()->NotifyDidPresentCompositorFrame(1, timing_details);
696 presentation_run_loop.Run();
697 }
698 };
699
TEST_F(NotifySwapTimesRenderWidgetUnittest,PresentationTimestampValid)700 TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampValid) {
701 base::HistogramTester histograms;
702
703 CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(2));
704
705 EXPECT_THAT(histograms.GetAllSamples(
706 "PageLoad.Internal.Renderer.PresentationTime.Valid"),
707 testing::ElementsAre(base::Bucket(true, 1)));
708 EXPECT_THAT(
709 histograms.GetAllSamples(
710 "PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
711 testing::ElementsAre(base::Bucket(2, 1)));
712 }
713
TEST_F(NotifySwapTimesRenderWidgetUnittest,PresentationTimestampInvalid)714 TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampInvalid) {
715 base::HistogramTester histograms;
716
717 CompositeAndWaitForPresentation(base::TimeDelta());
718
719 EXPECT_THAT(histograms.GetAllSamples(
720 "PageLoad.Internal.Renderer.PresentationTime.Valid"),
721 testing::ElementsAre(base::Bucket(false, 1)));
722 EXPECT_THAT(
723 histograms.GetAllSamples(
724 "PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
725 testing::IsEmpty());
726 }
727
TEST_F(NotifySwapTimesRenderWidgetUnittest,PresentationTimestampEarlierThanSwaptime)728 TEST_F(NotifySwapTimesRenderWidgetUnittest,
729 PresentationTimestampEarlierThanSwaptime) {
730 base::HistogramTester histograms;
731
732 CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(-2));
733
734 EXPECT_THAT(histograms.GetAllSamples(
735 "PageLoad.Internal.Renderer.PresentationTime.Valid"),
736 testing::ElementsAre(base::Bucket(false, 1)));
737 EXPECT_THAT(
738 histograms.GetAllSamples(
739 "PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
740 testing::IsEmpty());
741 }
742
743 } // namespace content
744