blob: 54011858db913a02e08aa4e35a8c26b5e1c0b585 [file] [log] [blame]
// Copyright 2020 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 "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/test/property_tree_test_utils.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
#include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
namespace blink {
using testing::_;
bool operator==(const InputHandlerProxy::DidOverscrollParams& lhs,
const InputHandlerProxy::DidOverscrollParams& rhs) {
return lhs.accumulated_overscroll == rhs.accumulated_overscroll &&
lhs.latest_overscroll_delta == rhs.latest_overscroll_delta &&
lhs.current_fling_velocity == rhs.current_fling_velocity &&
lhs.causal_event_viewport_point == rhs.causal_event_viewport_point &&
lhs.overscroll_behavior == rhs.overscroll_behavior;
}
namespace {
class TouchMoveEventListener final : public NativeEventListener {
public:
void Invoke(ExecutionContext*, Event*) override { invoked_ = true; }
bool GetInvokedStateAndReset() {
bool invoked = invoked_;
invoked_ = false;
return invoked;
}
private:
bool invoked_ = false;
};
} // namespace
class WebFrameWidgetSimTest : public SimTest {};
// Tests that if a WebView is auto-resized, the associated
// WebFrameWidgetImpl requests a new viz::LocalSurfaceId to be allocated on the
// impl thread.
TEST_F(WebFrameWidgetSimTest, AutoResizeAllocatedLocalSurfaceId) {
viz::ParentLocalSurfaceIdAllocator allocator;
// Enable auto-resize.
VisualProperties visual_properties;
visual_properties.auto_resize_enabled = true;
visual_properties.min_size_for_auto_resize = gfx::Size(100, 100);
visual_properties.max_size_for_auto_resize = gfx::Size(200, 200);
allocator.GenerateId();
visual_properties.local_surface_id = allocator.GetCurrentLocalSurfaceId();
WebView().MainFrameWidget()->ApplyVisualProperties(visual_properties);
WebView().MainFrameViewWidget()->UpdateSurfaceAndScreenInfo(
visual_properties.local_surface_id.value(),
visual_properties.compositor_viewport_pixel_rect,
visual_properties.screen_info);
EXPECT_EQ(allocator.GetCurrentLocalSurfaceId(),
WebView().MainFrameViewWidget()->LocalSurfaceIdFromParent());
EXPECT_FALSE(WebView()
.MainFrameViewWidget()
->LayerTreeHostForTesting()
->new_local_surface_id_request_for_testing());
constexpr gfx::Size size(200, 200);
WebView().MainFrameViewWidget()->DidAutoResize(size);
EXPECT_EQ(allocator.GetCurrentLocalSurfaceId(),
WebView().MainFrameViewWidget()->LocalSurfaceIdFromParent());
EXPECT_TRUE(WebView()
.MainFrameViewWidget()
->LayerTreeHostForTesting()
->new_local_surface_id_request_for_testing());
}
TEST_F(WebFrameWidgetSimTest, FrameSinkIdHitTestAPI) {
SimRequest request("https://example.com/test.html", "text/html");
LoadURL("https://example.com/test.html");
request.Complete(
R"HTML(
<style>
html, body {
margin :0px;
padding: 0px;
}
</style>
<div style='background: green; padding: 100px; margin: 0px;'>
<iframe style='width: 200px; height: 100px;'
srcdoc='<body style="margin : 0px; height : 100px; width : 200px;">
</body>'>
</iframe>
</div>
)HTML");
gfx::PointF point;
viz::FrameSinkId main_frame_sink_id =
WebView().MainFrameViewWidget()->GetFrameSinkIdAtPoint(
gfx::PointF(10.43, 10.74), &point);
EXPECT_EQ(WebView().MainFrameViewWidget()->GetFrameSinkId(),
main_frame_sink_id);
EXPECT_EQ(gfx::PointF(10.43, 10.74), point);
// Targeting a child frame should also return the FrameSinkId for the main
// widget.
viz::FrameSinkId frame_sink_id =
WebView().MainFrameViewWidget()->GetFrameSinkIdAtPoint(
gfx::PointF(150.27, 150.25), &point);
EXPECT_EQ(main_frame_sink_id, frame_sink_id);
EXPECT_EQ(gfx::PointF(150.27, 150.25), point);
}
#if defined(OS_ANDROID)
TEST_F(WebFrameWidgetSimTest, ForceSendMetadataOnInput) {
cc::LayerTreeHost* layer_tree_host =
WebView().MainFrameViewWidget()->LayerTreeHostForTesting();
// We should not have any force send metadata requests at start.
EXPECT_FALSE(layer_tree_host->TakeForceSendMetadataRequest());
// ShowVirtualKeyboard will trigger a text input state update.
WebView().MainFrameViewWidget()->ShowVirtualKeyboard();
// We should now have a force send metadata request.
EXPECT_TRUE(layer_tree_host->TakeForceSendMetadataRequest());
}
#endif // defined(OS_ANDROID)
// A test that forces a RemoteMainFrame to be created.
class WebFrameWidgetImplRemoteFrameSimTest : public SimTest {
public:
void SetUp() override {
SimTest::SetUp();
InitializeRemote();
CHECK(static_cast<WebFrameWidgetImpl*>(LocalFrameRoot().FrameWidget())
->ForSubframe());
}
WebFrameWidgetImpl* LocalFrameRootWidget() {
return static_cast<WebFrameWidgetImpl*>(LocalFrameRoot().FrameWidget());
}
};
// Tests that the value of VisualProperties::is_pinch_gesture_active is
// propagated to the LayerTreeHost when properties are synced for child local
// roots.
TEST_F(WebFrameWidgetImplRemoteFrameSimTest,
ActivePinchGestureUpdatesLayerTreeHostSubFrame) {
cc::LayerTreeHost* layer_tree_host =
LocalFrameRootWidget()->LayerTreeHostForTesting();
EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
blink::VisualProperties visual_properties;
// Sync visual properties on a child widget.
visual_properties.is_pinch_gesture_active = true;
LocalFrameRootWidget()->ApplyVisualProperties(visual_properties);
// We expect the |is_pinch_gesture_active| value to propagate to the
// LayerTreeHost for sub-frames. Since GesturePinch events are handled
// directly in the main-frame's layer tree (and only there), information about
// whether or not we're in a pinch gesture must be communicated separately to
// sub-frame layer trees, via OnUpdateVisualProperties. This information
// is required to allow sub-frame compositors to throttle rastering while
// pinch gestures are active.
EXPECT_TRUE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
visual_properties.is_pinch_gesture_active = false;
LocalFrameRootWidget()->ApplyVisualProperties(visual_properties);
EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
}
const char EVENT_LISTENER_RESULT_HISTOGRAM[] = "Event.PassiveListeners";
// Keep in sync with enum defined in
// RenderWidgetInputHandler::LogPassiveEventListenersUma.
enum {
PASSIVE_LISTENER_UMA_ENUM_PASSIVE,
PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_MAIN_THREAD_RESPONSIVENESS_DEPRECATED,
PASSIVE_LISTENER_UMA_ENUM_COUNT
};
// Since std::unique_ptr<InputHandlerProxy::DidOverscrollParams> isn't copyable
// we can't use the MockCallback template.
class MockHandledEventCallback {
public:
MockHandledEventCallback() = default;
MOCK_METHOD4_T(Run,
void(mojom::InputEventResultState,
const ui::LatencyInfo&,
InputHandlerProxy::DidOverscrollParams*,
base::Optional<cc::TouchAction>));
WebWidget::HandledEventCallback GetCallback() {
return base::BindOnce(&MockHandledEventCallback::HandleCallback,
base::Unretained(this));
}
private:
void HandleCallback(
mojom::InputEventResultState ack_state,
const ui::LatencyInfo& latency_info,
std::unique_ptr<InputHandlerProxy::DidOverscrollParams> overscroll,
base::Optional<cc::TouchAction> touch_action) {
Run(ack_state, latency_info, overscroll.get(), touch_action);
}
DISALLOW_COPY_AND_ASSIGN(MockHandledEventCallback);
};
class MockWebFrameWidgetImpl : public SimWebFrameWidget {
public:
template <typename... Args>
explicit MockWebFrameWidgetImpl(Args&&... args)
: SimWebFrameWidget(std::forward<Args>(args)...) {}
MOCK_METHOD1(HandleInputEvent,
WebInputEventResult(const WebCoalescedInputEvent&));
MOCK_METHOD0(DispatchBufferedTouchEvents, WebInputEventResult());
MOCK_METHOD4(ObserveGestureEventAndResult,
void(const WebGestureEvent& gesture_event,
const gfx::Vector2dF& unused_delta,
const cc::OverscrollBehavior& overscroll_behavior,
bool event_processed));
MOCK_METHOD1(WillHandleGestureEvent, bool(const WebGestureEvent& event));
// mojom::blink::WidgetHost overrides:
using SimWebFrameWidget::SetCursor;
};
class WebFrameWidgetImplSimTest : public SimTest {
public:
SimWebFrameWidget* CreateSimWebFrameWidget(
base::PassKey<WebLocalFrame> pass_key,
CrossVariantMojoAssociatedRemote<
mojom::blink::FrameWidgetHostInterfaceBase> frame_widget_host,
CrossVariantMojoAssociatedReceiver<mojom::blink::FrameWidgetInterfaceBase>
frame_widget,
CrossVariantMojoAssociatedRemote<mojom::blink::WidgetHostInterfaceBase>
widget_host,
CrossVariantMojoAssociatedReceiver<mojom::blink::WidgetInterfaceBase>
widget,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const viz::FrameSinkId& frame_sink_id,
bool hidden,
bool never_composited,
bool is_for_child_local_root,
bool is_for_nested_main_frame,
SimCompositor* compositor) override {
return MakeGarbageCollected<MockWebFrameWidgetImpl>(
compositor, pass_key, std::move(frame_widget_host),
std::move(frame_widget), std::move(widget_host), std::move(widget),
std::move(task_runner), frame_sink_id, hidden, never_composited,
is_for_child_local_root, is_for_nested_main_frame);
}
MockWebFrameWidgetImpl* MockMainFrameWidget() {
return static_cast<MockWebFrameWidgetImpl*>(MainFrame().FrameWidget());
}
void SendInputEvent(const WebInputEvent& event,
WebWidget::HandledEventCallback callback) {
MockMainFrameWidget()->ProcessInputEventSynchronouslyForTesting(
WebCoalescedInputEvent(event.Clone(), {}, {}, ui::LatencyInfo()),
std::move(callback));
}
bool OverscrollGestureEvent(const blink::WebGestureEvent& event) {
if (event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
MockMainFrameWidget()->DidOverscroll(
gfx::Vector2dF(event.data.scroll_update.delta_x,
event.data.scroll_update.delta_y),
gfx::Vector2dF(event.data.scroll_update.delta_x,
event.data.scroll_update.delta_y),
event.PositionInWidget(),
gfx::Vector2dF(event.data.scroll_update.velocity_x,
event.data.scroll_update.velocity_y));
return true;
}
return false;
}
const base::HistogramTester& histogram_tester() const {
return histogram_tester_;
}
private:
base::HistogramTester histogram_tester_;
};
TEST_F(WebFrameWidgetImplSimTest, CursorChange) {
ui::Cursor cursor;
frame_test_helpers::TestWebFrameWidgetHost& widget_host =
MockMainFrameWidget()->WidgetHost();
MockMainFrameWidget()->SetCursor(cursor);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(widget_host.CursorSetCount(), 1u);
MockMainFrameWidget()->SetCursor(cursor);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(widget_host.CursorSetCount(), 1u);
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.WillOnce(::testing::Return(WebInputEventResult::kNotHandled));
SendInputEvent(
SyntheticWebMouseEventBuilder::Build(WebInputEvent::Type::kMouseLeave),
base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(widget_host.CursorSetCount(), 1u);
MockMainFrameWidget()->SetCursor(cursor);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(widget_host.CursorSetCount(), 2u);
}
TEST_F(WebFrameWidgetImplSimTest, EventOverscroll) {
ON_CALL(*MockMainFrameWidget(), WillHandleGestureEvent(_))
.WillByDefault(testing::Invoke(
this, &WebFrameWidgetImplSimTest::OverscrollGestureEvent));
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.WillRepeatedly(::testing::Return(WebInputEventResult::kNotHandled));
WebGestureEvent scroll(WebInputEvent::Type::kGestureScrollUpdate,
WebInputEvent::kNoModifiers, base::TimeTicks::Now());
scroll.SetPositionInWidget(gfx::PointF(-10, 0));
scroll.data.scroll_update.delta_y = 10;
MockHandledEventCallback handled_event;
InputHandlerProxy::DidOverscrollParams expected_overscroll;
expected_overscroll.latest_overscroll_delta = gfx::Vector2dF(0, 10);
expected_overscroll.accumulated_overscroll = gfx::Vector2dF(0, 10);
expected_overscroll.causal_event_viewport_point = gfx::PointF(-10, 0);
expected_overscroll.current_fling_velocity = gfx::Vector2dF();
// Overscroll notifications received while handling an input event should
// be bundled with the event ack IPC.
EXPECT_CALL(handled_event, Run(mojom::InputEventResultState::kConsumed, _,
testing::Pointee(expected_overscroll), _))
.Times(1);
SendInputEvent(scroll, handled_event.GetCallback());
}
TEST_F(WebFrameWidgetImplSimTest, RenderWidgetInputEventUmaMetrics) {
SyntheticWebTouchEvent touch;
touch.PressPoint(10, 10);
touch.touch_start_or_first_touch_move = true;
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.Times(5)
.WillRepeatedly(::testing::Return(WebInputEventResult::kNotHandled));
EXPECT_CALL(*MockMainFrameWidget(), DispatchBufferedTouchEvents())
.Times(5)
.WillRepeatedly(::testing::Return(WebInputEventResult::kNotHandled));
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_CANCELABLE, 1);
touch.dispatch_type = WebInputEvent::DispatchType::kEventNonBlocking;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
1);
touch.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_PASSIVE, 1);
touch.dispatch_type =
WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(
EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 1);
touch.MovePoint(0, 10, 10);
touch.touch_start_or_first_touch_move = true;
touch.dispatch_type =
WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(
EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 2);
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.WillOnce(::testing::Return(WebInputEventResult::kNotHandled));
EXPECT_CALL(*MockMainFrameWidget(), DispatchBufferedTouchEvents())
.WillOnce(::testing::Return(WebInputEventResult::kHandledSuppressed));
touch.dispatch_type = WebInputEvent::DispatchType::kBlocking;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED, 1);
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.WillOnce(::testing::Return(WebInputEventResult::kNotHandled));
EXPECT_CALL(*MockMainFrameWidget(), DispatchBufferedTouchEvents())
.WillOnce(::testing::Return(WebInputEventResult::kHandledApplication));
touch.dispatch_type = WebInputEvent::DispatchType::kBlocking;
SendInputEvent(touch, base::DoNothing());
histogram_tester().ExpectBucketCount(
EVENT_LISTENER_RESULT_HISTOGRAM,
PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED, 1);
}
// Ensures that the compositor thread gets sent the gesture event & overscroll
// amount for an overscroll initiated by a touchpad.
TEST_F(WebFrameWidgetImplSimTest, SendElasticOverscrollForTouchpad) {
WebGestureEvent scroll(WebInputEvent::Type::kGestureScrollUpdate,
WebInputEvent::kNoModifiers, base::TimeTicks::Now(),
WebGestureDevice::kTouchpad);
scroll.SetPositionInWidget(gfx::PointF(-10, 0));
scroll.data.scroll_update.delta_y = 10;
// We only really care that ObserveGestureEventAndResult was called; we
// therefore suppress the warning for the call to
// HandleInputEvent().
EXPECT_CALL(*MockMainFrameWidget(), ObserveGestureEventAndResult(_, _, _, _))
.Times(1);
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.Times(testing::AnyNumber());
SendInputEvent(scroll, base::DoNothing());
}
// Ensures that the compositor thread gets sent the gesture event & overscroll
// amount for an overscroll initiated by a touchscreen.
TEST_F(WebFrameWidgetImplSimTest, SendElasticOverscrollForTouchscreen) {
WebGestureEvent scroll(WebInputEvent::Type::kGestureScrollUpdate,
WebInputEvent::kNoModifiers, base::TimeTicks::Now(),
WebGestureDevice::kTouchscreen);
scroll.SetPositionInWidget(gfx::PointF(-10, 0));
scroll.data.scroll_update.delta_y = 10;
// We only really care that ObserveGestureEventAndResult was called; we
// therefore suppress the warning for the call to
// HandleInputEvent().
EXPECT_CALL(*MockMainFrameWidget(), ObserveGestureEventAndResult(_, _, _, _))
.Times(1);
EXPECT_CALL(*MockMainFrameWidget(), HandleInputEvent(_))
.Times(testing::AnyNumber());
SendInputEvent(scroll, base::DoNothing());
}
class NotifySwapTimesWebFrameWidgetTest : public SimTest {
public:
void SetUp() override {
SimTest::SetUp();
WebView().StopDeferringMainFrameUpdate();
FrameWidgetBase()->UpdateCompositorViewportRect(gfx::Rect(200, 100));
auto* root_layer =
FrameWidgetBase()->LayerTreeHostForTesting()->root_layer();
auto color_layer = cc::SolidColorLayer::Create();
color_layer->SetBounds(gfx::Size(100, 100));
cc::CopyProperties(root_layer, color_layer.get());
root_layer->SetChildLayerList(cc::LayerList({color_layer}));
color_layer->SetBackgroundColor(SK_ColorRED);
}
WebFrameWidgetImpl* FrameWidgetBase() {
return static_cast<WebFrameWidgetImpl*>(MainFrame().FrameWidget());
}
// |swap_to_presentation| determines how long after swap should presentation
// happen. This can be negative, positive, or zero. If zero, an invalid (null)
// presentation time is used.
void CompositeAndWaitForPresentation(base::TimeDelta swap_to_presentation) {
base::RunLoop swap_run_loop;
base::RunLoop presentation_run_loop;
// Register callbacks for presentation time.
base::TimeTicks swap_time;
MainFrame().FrameWidget()->NotifySwapAndPresentationTime(
base::BindOnce(
[](base::OnceClosure swap_quit_closure, base::TimeTicks* swap_time,
blink::WebSwapResult result, base::TimeTicks timestamp) {
DCHECK(!timestamp.is_null());
*swap_time = timestamp;
std::move(swap_quit_closure).Run();
},
swap_run_loop.QuitClosure(), &swap_time),
base::BindOnce(
[](base::OnceClosure presentation_quit_closure,
blink::WebSwapResult result, base::TimeTicks timestamp) {
DCHECK(!timestamp.is_null());
std::move(presentation_quit_closure).Run();
},
presentation_run_loop.QuitClosure()));
// Composite and wait for the swap to complete.
Compositor().BeginFrame(/*time_delta_in_seconds=*/0.016, /*raster=*/true);
swap_run_loop.Run();
// Present and wait for it to complete.
viz::FrameTimingDetails timing_details;
if (!swap_to_presentation.is_zero()) {
timing_details.presentation_feedback = gfx::PresentationFeedback(
/*presentation_time=*/swap_time + swap_to_presentation,
base::TimeDelta::FromMilliseconds(16), 0);
}
auto* last_frame_sink = GetWebFrameWidget().LastCreatedFrameSink();
last_frame_sink->NotifyDidPresentCompositorFrame(1, timing_details);
presentation_run_loop.Run();
}
};
TEST_F(NotifySwapTimesWebFrameWidgetTest, PresentationTimestampValid) {
base::HistogramTester histograms;
CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(2));
EXPECT_THAT(histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.Valid"),
testing::ElementsAre(base::Bucket(true, 1)));
EXPECT_THAT(
histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
testing::ElementsAre(base::Bucket(2, 1)));
}
TEST_F(NotifySwapTimesWebFrameWidgetTest, PresentationTimestampInvalid) {
base::HistogramTester histograms;
CompositeAndWaitForPresentation(base::TimeDelta());
EXPECT_THAT(histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.Valid"),
testing::ElementsAre(base::Bucket(false, 1)));
EXPECT_THAT(
histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
testing::IsEmpty());
}
TEST_F(NotifySwapTimesWebFrameWidgetTest,
PresentationTimestampEarlierThanSwaptime) {
base::HistogramTester histograms;
CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(-2));
EXPECT_THAT(histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.Valid"),
testing::ElementsAre(base::Bucket(false, 1)));
EXPECT_THAT(
histograms.GetAllSamples(
"PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"),
testing::IsEmpty());
}
// Tests that the value of VisualProperties::is_pinch_gesture_active is
// not propagated to the LayerTreeHost when properties are synced for main
// frame.
TEST_F(WebFrameWidgetSimTest, ActivePinchGestureUpdatesLayerTreeHost) {
auto* layer_tree_host =
WebView().MainFrameViewWidget()->LayerTreeHostForTesting();
EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
blink::VisualProperties visual_properties;
// Sync visual properties on a mainframe RenderWidget.
visual_properties.is_pinch_gesture_active = true;
WebView().MainFrameViewWidget()->ApplyVisualProperties(visual_properties);
// We do not expect the |is_pinch_gesture_active| value to propagate to the
// LayerTreeHost for the main-frame. Since GesturePinch events are handled
// directly by the layer tree for the main frame, it already knows whether or
// not a pinch gesture is active, and so we shouldn't propagate this
// information to the layer tree for a main-frame's widget.
EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
}
// Tests that dispatch buffered touch events does not process events during
// drag and devtools handling.
TEST_F(WebFrameWidgetSimTest, DispatchBufferedTouchEvents) {
auto* widget = WebView().MainFrameViewWidget();
auto* listener = MakeGarbageCollected<TouchMoveEventListener>();
Window().addEventListener(
event_type_names::kTouchmove, listener,
MakeGarbageCollected<AddEventListenerOptionsResolved>());
widget->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
// Send a start.
SyntheticWebTouchEvent touch;
touch.PressPoint(10, 10);
touch.touch_start_or_first_touch_move = true;
widget->ProcessInputEventSynchronouslyForTesting(
WebCoalescedInputEvent(touch.Clone(), {}, {}, ui::LatencyInfo()),
base::DoNothing());
// Expect listener gets called.
touch.MovePoint(0, 10, 10);
widget->ProcessInputEventSynchronouslyForTesting(
WebCoalescedInputEvent(touch.Clone(), {}, {}, ui::LatencyInfo()),
base::DoNothing());
EXPECT_TRUE(listener->GetInvokedStateAndReset());
// Expect listener does not get called, due to devtools flag.
touch.MovePoint(0, 12, 12);
WebFrameWidgetImpl::SetIgnoreInputEvents(true);
widget->ProcessInputEventSynchronouslyForTesting(
WebCoalescedInputEvent(touch.Clone(), {}, {}, ui::LatencyInfo()),
base::DoNothing());
EXPECT_TRUE(WebFrameWidgetImpl::IgnoreInputEvents());
EXPECT_FALSE(listener->GetInvokedStateAndReset());
WebFrameWidgetImpl::SetIgnoreInputEvents(false);
// Expect listener does not get called, due to drag.
touch.MovePoint(0, 14, 14);
widget->StartDragging(WebDragData(), kDragOperationCopy, SkBitmap(),
gfx::Point());
widget->ProcessInputEventSynchronouslyForTesting(
WebCoalescedInputEvent(touch.Clone(), {}, {}, ui::LatencyInfo()),
base::DoNothing());
EXPECT_TRUE(widget->DoingDragAndDrop());
EXPECT_FALSE(WebFrameWidgetImpl::IgnoreInputEvents());
EXPECT_FALSE(listener->GetInvokedStateAndReset());
}
// Tests that page scale is propagated to all remote frames controlled
// by a widget.
TEST_F(WebFrameWidgetSimTest, PropagateScaleToRemoteFrames) {
SimRequest request("https://example.com/test.html", "text/html");
LoadURL("https://example.com/test.html");
request.Complete(
R"HTML(
<iframe style='width: 200px; height: 100px;'
srcdoc='<iframe srcdoc="plain text"></iframe>'>
</iframe>
)HTML");
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(WebView().MainFrame()->FirstChild());
{
WebFrame* grandchild = WebView().MainFrame()->FirstChild()->FirstChild();
EXPECT_TRUE(grandchild);
EXPECT_TRUE(grandchild->IsWebLocalFrame());
grandchild->Swap(frame_test_helpers::CreateRemote());
}
auto* widget = WebView().MainFrameViewWidget();
widget->SetPageScaleStateAndLimits(1.3f, true, 1.0f, 3.0f);
EXPECT_EQ(
To<WebRemoteFrameImpl>(WebView().MainFrame()->FirstChild()->FirstChild())
->GetFrame()
->GetPendingVisualPropertiesForTesting()
.page_scale_factor,
1.3f);
WebView().MainFrame()->FirstChild()->FirstChild()->Detach();
}
} // namespace blink