| // Copyright 2011 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 "cc/trees/layer_tree_host_impl.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "cc/animation/animation_events.h" |
| #include "cc/animation/animation_host.h" |
| #include "cc/animation/animation_id_provider.h" |
| #include "cc/animation/transform_operations.h" |
| #include "cc/base/math_util.h" |
| #include "cc/input/main_thread_scrolling_reason.h" |
| #include "cc/input/page_scale_animation.h" |
| #include "cc/input/scrollbar_animation_controller_thinning.h" |
| #include "cc/input/top_controls_manager.h" |
| #include "cc/layers/append_quads_data.h" |
| #include "cc/layers/heads_up_display_layer_impl.h" |
| #include "cc/layers/layer_impl.h" |
| #include "cc/layers/painted_scrollbar_layer_impl.h" |
| #include "cc/layers/render_surface_impl.h" |
| #include "cc/layers/solid_color_layer_impl.h" |
| #include "cc/layers/solid_color_scrollbar_layer_impl.h" |
| #include "cc/layers/texture_layer_impl.h" |
| #include "cc/layers/video_layer_impl.h" |
| #include "cc/layers/viewport.h" |
| #include "cc/output/begin_frame_args.h" |
| #include "cc/output/compositor_frame_ack.h" |
| #include "cc/output/compositor_frame_metadata.h" |
| #include "cc/output/copy_output_request.h" |
| #include "cc/output/copy_output_result.h" |
| #include "cc/output/gl_renderer.h" |
| #include "cc/output/latency_info_swap_promise.h" |
| #include "cc/quads/render_pass_draw_quad.h" |
| #include "cc/quads/solid_color_draw_quad.h" |
| #include "cc/quads/texture_draw_quad.h" |
| #include "cc/quads/tile_draw_quad.h" |
| #include "cc/test/animation_test_common.h" |
| #include "cc/test/begin_frame_args_test.h" |
| #include "cc/test/fake_layer_tree_host_impl.h" |
| #include "cc/test/fake_mask_layer_impl.h" |
| #include "cc/test/fake_output_surface.h" |
| #include "cc/test/fake_output_surface_client.h" |
| #include "cc/test/fake_picture_layer_impl.h" |
| #include "cc/test/fake_raster_source.h" |
| #include "cc/test/fake_video_frame_provider.h" |
| #include "cc/test/geometry_test_utils.h" |
| #include "cc/test/gpu_rasterization_enabled_settings.h" |
| #include "cc/test/layer_test_common.h" |
| #include "cc/test/layer_tree_test.h" |
| #include "cc/test/test_gpu_memory_buffer_manager.h" |
| #include "cc/test/test_shared_bitmap_manager.h" |
| #include "cc/test/test_task_graph_runner.h" |
| #include "cc/test/test_web_graphics_context_3d.h" |
| #include "cc/trees/layer_tree_impl.h" |
| #include "cc/trees/single_thread_proxy.h" |
| #include "media/base/media.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkMallocPixelRef.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/geometry/vector2d_conversions.h" |
| |
| #define EXPECT_SCOPED(statements) \ |
| { \ |
| SCOPED_TRACE(""); \ |
| statements; \ |
| } |
| |
| using ::testing::Mock; |
| using ::testing::Return; |
| using ::testing::AnyNumber; |
| using ::testing::AtLeast; |
| using ::testing::_; |
| using media::VideoFrame; |
| |
| namespace cc { |
| namespace { |
| |
| class LayerTreeHostImplTest : public testing::Test, |
| public LayerTreeHostImplClient { |
| public: |
| LayerTreeHostImplTest() |
| : task_runner_provider_(base::ThreadTaskRunnerHandle::Get()), |
| always_main_thread_blocked_(&task_runner_provider_), |
| on_can_draw_state_changed_called_(false), |
| did_notify_ready_to_activate_(false), |
| did_request_commit_(false), |
| did_request_redraw_(false), |
| did_request_next_frame_(false), |
| did_request_prepare_tiles_(false), |
| did_complete_page_scale_animation_(false), |
| reduce_memory_result_(true), |
| skip_draw_layers_in_on_draw_(false) { |
| media::InitializeMediaLibrary(); |
| } |
| |
| LayerTreeSettings DefaultSettings() { |
| LayerTreeSettings settings; |
| settings.minimum_occlusion_tracking_size = gfx::Size(); |
| settings.renderer_settings.texture_id_allocation_chunk_size = 1; |
| settings.gpu_rasterization_enabled = true; |
| settings.verify_clip_tree_calculations = true; |
| settings.verify_transform_tree_calculations = true; |
| return settings; |
| } |
| |
| void SetUp() override { |
| CreateHostImpl(DefaultSettings(), CreateOutputSurface()); |
| } |
| |
| void TearDown() override { |
| if (host_impl_) |
| host_impl_->ReleaseOutputSurface(); |
| } |
| |
| void UpdateRendererCapabilitiesOnImplThread() override {} |
| void DidLoseOutputSurfaceOnImplThread() override {} |
| void CommitVSyncParameters(base::TimeTicks timebase, |
| base::TimeDelta interval) override {} |
| void SetBeginFrameSource(BeginFrameSource* source) override {} |
| void SetEstimatedParentDrawTime(base::TimeDelta draw_time) override {} |
| void DidSwapBuffersOnImplThread() override {} |
| void DidSwapBuffersCompleteOnImplThread() override {} |
| void OnCanDrawStateChanged(bool can_draw) override { |
| on_can_draw_state_changed_called_ = true; |
| } |
| void NotifyReadyToActivate() override { |
| did_notify_ready_to_activate_ = true; |
| host_impl_->ActivateSyncTree(); |
| } |
| void NotifyReadyToDraw() override {} |
| void SetNeedsRedrawOnImplThread() override { did_request_redraw_ = true; } |
| void SetNeedsRedrawRectOnImplThread(const gfx::Rect& damage_rect) override { |
| did_request_redraw_ = true; |
| } |
| void SetNeedsOneBeginImplFrameOnImplThread() override { |
| did_request_next_frame_ = true; |
| } |
| void SetNeedsPrepareTilesOnImplThread() override { |
| did_request_prepare_tiles_ = true; |
| } |
| void SetNeedsCommitOnImplThread() override { did_request_commit_ = true; } |
| void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {} |
| void PostAnimationEventsToMainThreadOnImplThread( |
| std::unique_ptr<AnimationEvents> events) override {} |
| bool IsInsideDraw() override { return false; } |
| void RenewTreePriority() override {} |
| void PostDelayedAnimationTaskOnImplThread(const base::Closure& task, |
| base::TimeDelta delay) override { |
| animation_task_ = task; |
| requested_animation_delay_ = delay; |
| } |
| void DidActivateSyncTree() override {} |
| void WillPrepareTiles() override {} |
| void DidPrepareTiles() override {} |
| void DidCompletePageScaleAnimationOnImplThread() override { |
| did_complete_page_scale_animation_ = true; |
| } |
| void OnDrawForOutputSurface(bool resourceless_software_draw) override { |
| std::unique_ptr<LayerTreeHostImpl::FrameData> frame( |
| new LayerTreeHostImpl::FrameData); |
| EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(frame.get())); |
| last_on_draw_render_passes_.clear(); |
| RenderPass::CopyAll(frame->render_passes, &last_on_draw_render_passes_); |
| if (!skip_draw_layers_in_on_draw_) |
| host_impl_->DrawLayers(frame.get()); |
| host_impl_->DidDrawAllLayers(*frame); |
| host_impl_->SwapBuffers(*frame); |
| last_on_draw_frame_ = std::move(frame); |
| } |
| |
| void set_reduce_memory_result(bool reduce_memory_result) { |
| reduce_memory_result_ = reduce_memory_result; |
| } |
| |
| virtual bool CreateHostImpl(const LayerTreeSettings& settings, |
| std::unique_ptr<OutputSurface> output_surface) { |
| return CreateHostImplWithTaskRunnerProvider( |
| settings, std::move(output_surface), &task_runner_provider_); |
| } |
| |
| virtual bool CreateHostImplWithTaskRunnerProvider( |
| const LayerTreeSettings& settings, |
| std::unique_ptr<OutputSurface> output_surface, |
| TaskRunnerProvider* task_runner_provider) { |
| if (host_impl_) |
| host_impl_->ReleaseOutputSurface(); |
| host_impl_ = LayerTreeHostImpl::Create( |
| settings, this, task_runner_provider, &stats_instrumentation_, |
| &shared_bitmap_manager_, &gpu_memory_buffer_manager_, |
| &task_graph_runner_, |
| AnimationHost::CreateForTesting(ThreadInstance::IMPL), 0); |
| output_surface_ = std::move(output_surface); |
| host_impl_->SetVisible(true); |
| bool init = host_impl_->InitializeRenderer(output_surface_.get()); |
| host_impl_->SetViewportSize(gfx::Size(10, 10)); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); |
| // Set the BeginFrameArgs so that methods which use it are able to. |
| host_impl_->WillBeginImplFrame(CreateBeginFrameArgsForTesting( |
| BEGINFRAME_FROM_HERE, |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(1))); |
| host_impl_->DidFinishImplFrame(); |
| |
| timeline_ = |
| AnimationTimeline::Create(AnimationIdProvider::NextTimelineId()); |
| host_impl_->animation_host()->AddAnimationTimeline(timeline_); |
| |
| return init; |
| } |
| |
| void SetupRootLayerImpl(std::unique_ptr<LayerImpl> root) { |
| root->SetPosition(gfx::PointF()); |
| root->SetBounds(gfx::Size(10, 10)); |
| root->SetDrawsContent(true); |
| root->draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10); |
| root->test_properties()->force_render_surface = true; |
| host_impl_->active_tree()->SetRootLayerForTesting(std::move(root)); |
| } |
| |
| static gfx::Vector2dF ScrollDelta(LayerImpl* layer_impl) { |
| gfx::ScrollOffset delta = |
| layer_impl->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.GetScrollOffsetDeltaForTesting(layer_impl->id()); |
| return gfx::Vector2dF(delta.x(), delta.y()); |
| } |
| |
| static void ExpectClearedScrollDeltasRecursive(LayerImpl* root) { |
| for (auto* layer : *root->layer_tree_impl()) |
| ASSERT_EQ(ScrollDelta(layer), gfx::Vector2d()); |
| } |
| |
| static ::testing::AssertionResult ScrollInfoContains( |
| const ScrollAndScaleSet& scroll_info, |
| int id, |
| const gfx::Vector2d& scroll_delta) { |
| int times_encountered = 0; |
| |
| for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) { |
| if (scroll_info.scrolls[i].layer_id != id) |
| continue; |
| |
| if (scroll_delta != scroll_info.scrolls[i].scroll_delta) { |
| return ::testing::AssertionFailure() |
| << "Expected " << scroll_delta.ToString() << ", not " |
| << scroll_info.scrolls[i].scroll_delta.ToString(); |
| } |
| times_encountered++; |
| } |
| |
| if (times_encountered != 1) |
| return ::testing::AssertionFailure() << "No layer found with id " << id; |
| return ::testing::AssertionSuccess(); |
| } |
| |
| static void ExpectNone(const ScrollAndScaleSet& scroll_info, int id) { |
| int times_encountered = 0; |
| |
| for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) { |
| if (scroll_info.scrolls[i].layer_id != id) |
| continue; |
| times_encountered++; |
| } |
| |
| ASSERT_EQ(0, times_encountered); |
| } |
| |
| LayerImpl* CreateScrollAndContentsLayers(LayerTreeImpl* layer_tree_impl, |
| const gfx::Size& content_size) { |
| // Create both an inner viewport scroll layer and an outer viewport scroll |
| // layer. The MaxScrollOffset of the outer viewport scroll layer will be |
| // 0x0, so the scrolls will be applied directly to the inner viewport. |
| const int kOuterViewportClipLayerId = 116; |
| const int kOuterViewportScrollLayerId = 117; |
| const int kContentLayerId = 118; |
| const int kInnerViewportScrollLayerId = 2; |
| const int kInnerViewportClipLayerId = 4; |
| const int kPageScaleLayerId = 5; |
| |
| std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); |
| root->SetBounds(content_size); |
| root->SetPosition(gfx::PointF()); |
| root->test_properties()->force_render_surface = true; |
| |
| std::unique_ptr<LayerImpl> inner_scroll = |
| LayerImpl::Create(layer_tree_impl, kInnerViewportScrollLayerId); |
| inner_scroll->test_properties()->is_container_for_fixed_position_layers = |
| true; |
| inner_scroll->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(inner_scroll->id(), |
| gfx::ScrollOffset()); |
| |
| std::unique_ptr<LayerImpl> inner_clip = |
| LayerImpl::Create(layer_tree_impl, kInnerViewportClipLayerId); |
| inner_clip->SetBounds( |
| gfx::Size(content_size.width() / 2, content_size.height() / 2)); |
| |
| std::unique_ptr<LayerImpl> page_scale = |
| LayerImpl::Create(layer_tree_impl, kPageScaleLayerId); |
| |
| inner_scroll->SetScrollClipLayer(inner_clip->id()); |
| inner_scroll->SetBounds(content_size); |
| inner_scroll->SetPosition(gfx::PointF()); |
| |
| std::unique_ptr<LayerImpl> outer_clip = |
| LayerImpl::Create(layer_tree_impl, kOuterViewportClipLayerId); |
| outer_clip->SetBounds(content_size); |
| outer_clip->test_properties()->is_container_for_fixed_position_layers = |
| true; |
| |
| std::unique_ptr<LayerImpl> outer_scroll = |
| LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId); |
| outer_scroll->SetScrollClipLayer(outer_clip->id()); |
| outer_scroll->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(outer_scroll->id(), |
| gfx::ScrollOffset()); |
| outer_scroll->SetBounds(content_size); |
| outer_scroll->SetPosition(gfx::PointF()); |
| |
| std::unique_ptr<LayerImpl> contents = |
| LayerImpl::Create(layer_tree_impl, kContentLayerId); |
| contents->SetDrawsContent(true); |
| contents->SetBounds(content_size); |
| contents->SetPosition(gfx::PointF()); |
| |
| outer_scroll->test_properties()->AddChild(std::move(contents)); |
| outer_clip->test_properties()->AddChild(std::move(outer_scroll)); |
| inner_scroll->test_properties()->AddChild(std::move(outer_clip)); |
| page_scale->test_properties()->AddChild(std::move(inner_scroll)); |
| inner_clip->test_properties()->AddChild(std::move(page_scale)); |
| root->test_properties()->AddChild(std::move(inner_clip)); |
| |
| layer_tree_impl->SetRootLayerForTesting(std::move(root)); |
| layer_tree_impl->BuildPropertyTreesForTesting(); |
| layer_tree_impl->SetViewportLayersFromIds( |
| Layer::INVALID_ID, kPageScaleLayerId, kInnerViewportScrollLayerId, |
| kOuterViewportScrollLayerId); |
| |
| layer_tree_impl->DidBecomeActive(); |
| return layer_tree_impl->InnerViewportScrollLayer(); |
| } |
| |
| LayerImpl* SetupScrollAndContentsLayers(const gfx::Size& content_size) { |
| LayerImpl* scroll_layer = CreateScrollAndContentsLayers( |
| host_impl_->active_tree(), content_size); |
| host_impl_->active_tree()->DidBecomeActive(); |
| return scroll_layer; |
| } |
| |
| // Sets up a typical virtual viewport setup with one child content layer. |
| // Returns a pointer to the content layer. |
| LayerImpl* CreateBasicVirtualViewportLayers(const gfx::Size& viewport_size, |
| const gfx::Size& content_size) { |
| // CreateScrollAndContentsLayers makes the outer viewport unscrollable and |
| // the inner a different size from the outer. We'll reuse its layer |
| // hierarchy but adjust the sizing to our needs. |
| CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size); |
| |
| LayerImpl* content_layer = host_impl_->OuterViewportScrollLayer() |
| ->test_properties() |
| ->children.back(); |
| content_layer->SetBounds(content_size); |
| host_impl_->OuterViewportScrollLayer()->SetBounds(content_size); |
| |
| LayerImpl* outer_clip = |
| host_impl_->OuterViewportScrollLayer()->test_properties()->parent; |
| outer_clip->SetBounds(viewport_size); |
| |
| LayerImpl* inner_clip_layer = host_impl_->InnerViewportScrollLayer() |
| ->test_properties() |
| ->parent->test_properties() |
| ->parent; |
| inner_clip_layer->SetBounds(viewport_size); |
| host_impl_->InnerViewportScrollLayer()->SetBounds(viewport_size); |
| |
| // Needs to happen before building property trees as the latter propagates |
| // these element ids to property tree nodes. |
| host_impl_->active_tree()->SetElementIdsForTesting(); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| host_impl_->SetViewportSize(viewport_size); |
| host_impl_->active_tree()->DidBecomeActive(); |
| |
| return content_layer; |
| } |
| |
| std::unique_ptr<LayerImpl> CreateScrollableLayer(int id, |
| const gfx::Size& size, |
| LayerImpl* clip_layer) { |
| DCHECK(clip_layer); |
| DCHECK(id != clip_layer->id()); |
| std::unique_ptr<LayerImpl> layer = |
| LayerImpl::Create(host_impl_->active_tree(), id); |
| layer->SetScrollClipLayer(clip_layer->id()); |
| layer->SetDrawsContent(true); |
| layer->SetBounds(size); |
| clip_layer->SetBounds(gfx::Size(size.width() / 2, size.height() / 2)); |
| return layer; |
| } |
| |
| std::unique_ptr<ScrollState> BeginState(const gfx::Point& point) { |
| ScrollStateData scroll_state_data; |
| scroll_state_data.is_beginning = true; |
| scroll_state_data.position_x = point.x(); |
| scroll_state_data.position_y = point.y(); |
| std::unique_ptr<ScrollState> scroll_state( |
| new ScrollState(scroll_state_data)); |
| return scroll_state; |
| } |
| |
| std::unique_ptr<ScrollState> UpdateState(const gfx::Point& point, |
| const gfx::Vector2dF& delta) { |
| ScrollStateData scroll_state_data; |
| scroll_state_data.delta_x = delta.x(); |
| scroll_state_data.delta_y = delta.y(); |
| scroll_state_data.position_x = point.x(); |
| scroll_state_data.position_y = point.y(); |
| std::unique_ptr<ScrollState> scroll_state( |
| new ScrollState(scroll_state_data)); |
| return scroll_state; |
| } |
| |
| std::unique_ptr<ScrollState> EndState() { |
| ScrollStateData scroll_state_data; |
| scroll_state_data.is_ending = true; |
| std::unique_ptr<ScrollState> scroll_state( |
| new ScrollState(scroll_state_data)); |
| return scroll_state; |
| } |
| |
| void DrawFrame() { |
| LayerTreeHostImpl::FrameData frame; |
| EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); |
| host_impl_->DrawLayers(&frame); |
| host_impl_->DidDrawAllLayers(frame); |
| } |
| |
| void pinch_zoom_pan_viewport_forces_commit_redraw(float device_scale_factor); |
| void pinch_zoom_pan_viewport_test(float device_scale_factor); |
| void pinch_zoom_pan_viewport_and_scroll_test(float device_scale_factor); |
| void pinch_zoom_pan_viewport_and_scroll_boundary_test( |
| float device_scale_factor); |
| |
| void SetupMouseMoveAtWithDeviceScale(float device_scale_factor); |
| |
| scoped_refptr<AnimationTimeline> timeline() { return timeline_; } |
| |
| protected: |
| virtual std::unique_ptr<OutputSurface> CreateOutputSurface() { |
| return FakeOutputSurface::Create3d(); |
| } |
| |
| void DrawOneFrame() { |
| LayerTreeHostImpl::FrameData frame_data; |
| host_impl_->PrepareToDraw(&frame_data); |
| host_impl_->DidDrawAllLayers(frame_data); |
| } |
| |
| static void SetScrollOffsetDelta(LayerImpl* layer_impl, |
| const gfx::Vector2dF& delta) { |
| if (layer_impl->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.SetScrollOffsetDeltaForTesting(layer_impl->id(), |
| delta)) |
| layer_impl->layer_tree_impl()->DidUpdateScrollOffset( |
| layer_impl->id(), layer_impl->transform_tree_index()); |
| } |
| |
| void BeginImplFrameAndAnimate(BeginFrameArgs begin_frame_args, |
| base::TimeTicks frame_time) { |
| begin_frame_args.frame_time = frame_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| host_impl_->UpdateAnimationState(true); |
| host_impl_->DidFinishImplFrame(); |
| } |
| |
| FakeImplTaskRunnerProvider task_runner_provider_; |
| DebugScopedSetMainThreadBlocked always_main_thread_blocked_; |
| |
| TestSharedBitmapManager shared_bitmap_manager_; |
| TestGpuMemoryBufferManager gpu_memory_buffer_manager_; |
| TestTaskGraphRunner task_graph_runner_; |
| std::unique_ptr<OutputSurface> output_surface_; |
| std::unique_ptr<LayerTreeHostImpl> host_impl_; |
| FakeRenderingStatsInstrumentation stats_instrumentation_; |
| bool on_can_draw_state_changed_called_; |
| bool did_notify_ready_to_activate_; |
| bool did_request_commit_; |
| bool did_request_redraw_; |
| bool did_request_next_frame_; |
| bool did_request_prepare_tiles_; |
| bool did_complete_page_scale_animation_; |
| bool reduce_memory_result_; |
| base::Closure animation_task_; |
| base::TimeDelta requested_animation_delay_; |
| bool skip_draw_layers_in_on_draw_; |
| std::unique_ptr<LayerTreeHostImpl::FrameData> last_on_draw_frame_; |
| RenderPassList last_on_draw_render_passes_; |
| scoped_refptr<AnimationTimeline> timeline_; |
| }; |
| |
| // A test fixture for new animation timelines tests. |
| class LayerTreeHostImplTimelinesTest : public LayerTreeHostImplTest { |
| public: |
| void SetUp() override { |
| CreateHostImpl(DefaultSettings(), CreateOutputSurface()); |
| } |
| }; |
| |
| TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) { |
| // Note: It is not possible to disable the renderer once it has been set, |
| // so we do not need to test that disabling the renderer notifies us |
| // that can_draw changed. |
| EXPECT_FALSE(host_impl_->CanDraw()); |
| on_can_draw_state_changed_called_ = false; |
| |
| // Set up the root layer, which allows us to draw. |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| EXPECT_TRUE(host_impl_->CanDraw()); |
| EXPECT_TRUE(on_can_draw_state_changed_called_); |
| on_can_draw_state_changed_called_ = false; |
| |
| // Toggle the root layer to make sure it toggles can_draw |
| host_impl_->active_tree()->SetRootLayerForTesting(nullptr); |
| host_impl_->active_tree()->DetachLayers(); |
| EXPECT_FALSE(host_impl_->CanDraw()); |
| EXPECT_TRUE(on_can_draw_state_changed_called_); |
| on_can_draw_state_changed_called_ = false; |
| |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| EXPECT_TRUE(host_impl_->CanDraw()); |
| EXPECT_TRUE(on_can_draw_state_changed_called_); |
| on_can_draw_state_changed_called_ = false; |
| |
| // Toggle the device viewport size to make sure it toggles can_draw. |
| host_impl_->SetViewportSize(gfx::Size()); |
| EXPECT_FALSE(host_impl_->CanDraw()); |
| EXPECT_TRUE(on_can_draw_state_changed_called_); |
| on_can_draw_state_changed_called_ = false; |
| |
| host_impl_->SetViewportSize(gfx::Size(100, 100)); |
| EXPECT_TRUE(host_impl_->CanDraw()); |
| EXPECT_TRUE(on_can_draw_state_changed_called_); |
| on_can_draw_state_changed_called_ = false; |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) { |
| CreateHostImpl(DefaultSettings(), |
| FakeOutputSurface::CreateSoftware( |
| base::WrapUnique(new SoftwareOutputDevice()))); |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| EXPECT_TRUE(host_impl_->CanDraw()); |
| host_impl_->SetViewportSize(gfx::Size()); |
| EXPECT_FALSE(host_impl_->CanDraw()); |
| |
| FakeOutputSurface* fake_output_surface = |
| static_cast<FakeOutputSurface*>(host_impl_->output_surface()); |
| EXPECT_EQ(fake_output_surface->num_sent_frames(), 0u); |
| gfx::Transform identity; |
| gfx::Rect viewport(100, 100); |
| const bool resourceless_software_draw = true; |
| host_impl_->OnDraw(identity, viewport, viewport, resourceless_software_draw); |
| ASSERT_EQ(fake_output_surface->num_sent_frames(), 1u); |
| EXPECT_EQ(gfx::SizeF(100.f, 100.f), |
| fake_output_surface->last_sent_frame()->metadata.root_layer_size); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) { |
| ASSERT_FALSE(host_impl_->active_tree()->root_layer_for_testing()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| ASSERT_EQ(scroll_info->scrolls.size(), 0u); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) { |
| { |
| std::unique_ptr<LayerImpl> root = |
| LayerImpl::Create(host_impl_->active_tree(), 1); |
| root->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 2)); |
| root->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 3)); |
| root->test_properties()->children[1]->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 4)); |
| root->test_properties()->children[1]->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 5)); |
| root->test_properties() |
| ->children[1] |
| ->test_properties() |
| ->children[0] |
| ->test_properties() |
| ->AddChild(LayerImpl::Create(host_impl_->active_tree(), 6)); |
| host_impl_->active_tree()->SetRootLayerForTesting(std::move(root)); |
| } |
| LayerImpl* root = *host_impl_->active_tree()->begin(); |
| |
| ExpectClearedScrollDeltasRecursive(root); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info; |
| |
| scroll_info = host_impl_->ProcessScrollDeltas(); |
| ASSERT_EQ(scroll_info->scrolls.size(), 0u); |
| ExpectClearedScrollDeltasRecursive(root); |
| |
| scroll_info = host_impl_->ProcessScrollDeltas(); |
| ASSERT_EQ(scroll_info->scrolls.size(), 0u); |
| ExpectClearedScrollDeltasRecursive(root); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) { |
| gfx::ScrollOffset scroll_offset(20, 30); |
| gfx::Vector2d scroll_delta(11, -15); |
| |
| auto root_clip_owned = LayerImpl::Create(host_impl_->active_tree(), 2); |
| auto* root_clip = root_clip_owned.get(); |
| auto root_owned = LayerImpl::Create(host_impl_->active_tree(), 1); |
| auto* root = root_owned.get(); |
| |
| root_clip->SetBounds(gfx::Size(10, 10)); |
| root_clip->test_properties()->AddChild(std::move(root_owned)); |
| root->SetBounds(gfx::Size(110, 110)); |
| root->SetScrollClipLayer(root_clip->id()); |
| root->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(root->id(), scroll_offset); |
| host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_clip_owned)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info; |
| |
| root->ScrollBy(scroll_delta); |
| scroll_info = host_impl_->ProcessScrollDeltas(); |
| ASSERT_EQ(scroll_info->scrolls.size(), 1u); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(), scroll_delta)); |
| |
| gfx::Vector2d scroll_delta2(-5, 27); |
| root->ScrollBy(scroll_delta2); |
| scroll_info = host_impl_->ProcessScrollDeltas(); |
| ASSERT_EQ(scroll_info->scrolls.size(), 1u); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(), |
| scroll_delta + scroll_delta2)); |
| |
| root->ScrollBy(gfx::Vector2d()); |
| scroll_info = host_impl_->ProcessScrollDeltas(); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(), |
| scroll_delta + scroll_delta2)); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(), |
| InputHandler::WHEEL)); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); |
| EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10), |
| InputHandler::WHEEL)); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(), |
| InputHandler::WHEEL)); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_commit_); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollActiveOnlyAfterScrollMovement) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| EXPECT_FALSE(host_impl_->IsActivelyScrolling()); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); |
| EXPECT_TRUE(host_impl_->IsActivelyScrolling()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->IsActivelyScrolling()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) { |
| // We should not crash when trying to scroll an empty layer tree. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) { |
| std::unique_ptr<TestWebGraphicsContext3D> context_owned = |
| TestWebGraphicsContext3D::Create(); |
| context_owned->set_context_lost(true); |
| |
| // Initialization will fail. |
| EXPECT_FALSE( |
| CreateHostImpl(DefaultSettings(), |
| FakeOutputSurface::Create3d(std::move(context_owned)))); |
| |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| |
| // We should not crash when trying to scroll after the renderer initialization |
| // fails. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) { |
| LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| // We should not crash if the tree is replaced while we are scrolling. |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::WHEEL) |
| .thread); |
| host_impl_->active_tree()->DetachLayers(); |
| |
| scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| |
| // We should still be scrolling, because the scrolled layer also exists in the |
| // new tree. |
| gfx::Vector2d scroll_delta(0, 10); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_TRUE( |
| ScrollInfoContains(*scroll_info, scroll_layer->id(), scroll_delta)); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| // Wheel handlers determine whether mouse events block scroll. |
| host_impl_->active_tree()->set_event_listener_properties( |
| EventListenerClass::kMouseWheel, EventListenerProperties::kBlocking); |
| EXPECT_EQ( |
| EventListenerProperties::kBlocking, |
| host_impl_->GetEventListenerProperties(EventListenerClass::kMouseWheel)); |
| |
| // But they don't influence the actual handling of the scroll gestures. |
| InputHandler::ScrollStatus status = |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| host_impl_->ScrollEnd(EndState().get()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) { |
| LayerImpl* scroll = SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); |
| |
| LayerImpl* child = 0; |
| { |
| std::unique_ptr<LayerImpl> child_layer = |
| LayerImpl::Create(host_impl_->active_tree(), 6); |
| child = child_layer.get(); |
| child_layer->SetDrawsContent(true); |
| child_layer->SetPosition(gfx::PointF(0, 20)); |
| child_layer->SetBounds(gfx::Size(50, 50)); |
| scroll->test_properties()->AddChild(std::move(child_layer)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| } |
| |
| // Touch handler regions determine whether touch events block scroll. |
| root->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 100, 100)); |
| EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10))); |
| |
| // But they don't influence the actual handling of the scroll gestures. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30))); |
| root->SetTouchEventHandlerRegion(gfx::Rect()); |
| EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30))); |
| child->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 50, 50)); |
| EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30))); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| // Ignore the fling since no layer is being scrolled |
| InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin(); |
| EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, |
| status.main_thread_scrolling_reasons); |
| |
| // Start scrolling a layer |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| // Now the fling should go ahead since we've started scrolling a layer |
| status = host_impl_->FlingScrollBegin(); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| // Ignore the fling since no layer is being scrolled |
| InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin(); |
| EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, |
| status.main_thread_scrolling_reasons); |
| |
| // Start scrolling a layer |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| // Now the fling should go ahead since we've started scrolling a layer |
| status = host_impl_->FlingScrollBegin(); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); |
| |
| root->set_main_thread_scrolling_reasons( |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| DrawFrame(); |
| |
| // Start scrolling a layer |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, |
| status.main_thread_scrolling_reasons); |
| |
| // The fling should be ignored since there's no layer being scrolled impl-side |
| status = host_impl_->FlingScrollBegin(); |
| EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); |
| |
| root->set_main_thread_scrolling_reasons( |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, |
| status.main_thread_scrolling_reasons); |
| |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) { |
| LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); |
| gfx::Size content_size = gfx::Size(360, 600); |
| gfx::Size scroll_content_size = gfx::Size(345, 3800); |
| gfx::Size scrollbar_size = gfx::Size(15, 600); |
| |
| host_impl_->SetViewportSize(content_size); |
| std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); |
| root->SetBounds(content_size); |
| root->SetPosition(gfx::PointF()); |
| |
| std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 2); |
| clip->SetBounds(content_size); |
| clip->SetPosition(gfx::PointF()); |
| |
| std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3); |
| scroll->SetBounds(scroll_content_size); |
| scroll->SetScrollClipLayer(clip->id()); |
| scroll->SetDrawsContent(true); |
| |
| std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar = |
| SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0, |
| false, true); |
| scrollbar->SetBounds(scrollbar_size); |
| scrollbar->SetPosition(gfx::PointF(345, 0)); |
| scrollbar->SetScrollLayerId(scroll->id()); |
| scrollbar->SetDrawsContent(true); |
| scrollbar->test_properties()->opacity = 1.f; |
| |
| std::unique_ptr<LayerImpl> squash1 = LayerImpl::Create(layer_tree_impl, 5); |
| squash1->SetBounds(gfx::Size(140, 300)); |
| squash1->SetPosition(gfx::PointF(220, 0)); |
| squash1->SetDrawsContent(true); |
| |
| std::unique_ptr<LayerImpl> squash2 = LayerImpl::Create(layer_tree_impl, 6); |
| squash2->SetBounds(gfx::Size(140, 300)); |
| squash2->SetPosition(gfx::PointF(220, 300)); |
| squash2->SetDrawsContent(true); |
| |
| scroll->test_properties()->AddChild(std::move(squash2)); |
| clip->test_properties()->AddChild(std::move(scroll)); |
| clip->test_properties()->AddChild(std::move(scrollbar)); |
| clip->test_properties()->AddChild(std::move(squash1)); |
| root->test_properties()->AddChild(std::move(clip)); |
| |
| layer_tree_impl->SetRootLayerForTesting(std::move(root)); |
| layer_tree_impl->BuildPropertyTreesForTesting(); |
| layer_tree_impl->DidBecomeActive(); |
| |
| // The point hits squash1 layer and also scroll layer, because scroll layer is |
| // not an ancestor of squash1 layer, we cannot scroll on impl thread. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point(230, 150)).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, |
| status.main_thread_scrolling_reasons); |
| |
| // The point hits squash1 layer and also scrollbar layer. |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(350, 150)).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, |
| status.main_thread_scrolling_reasons); |
| |
| // The point hits squash2 layer and also scroll layer, because scroll layer is |
| // an ancestor of squash2 layer, we should scroll on impl. |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(230, 450)).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| host_impl_->SetViewportSize(gfx::Size(100, 100)); |
| |
| LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); |
| root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50)); |
| |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| // All scroll types inside the non-fast scrollable region should fail. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point(25, 25)).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, |
| status.main_thread_scrolling_reasons); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25), |
| InputHandler::WHEEL)); |
| |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, |
| status.main_thread_scrolling_reasons); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(25, 25), InputHandler::TOUCHSCREEN)); |
| |
| // All scroll types outside this region should succeed. |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(25, 25), InputHandler::TOUCHSCREEN)); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); |
| |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( |
| gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| host_impl_->SetViewportSize(gfx::Size(100, 100)); |
| |
| LayerImpl* root = *host_impl_->active_tree()->begin(); |
| root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50)); |
| root->SetPosition(gfx::PointF(-25.f, 0.f)); |
| root->SetDrawsContent(true); |
| |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| // This point would fall into the non-fast scrollable region except that we've |
| // moved the layer down by 25 pixels. |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point(40, 10)).get(), InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10), |
| InputHandler::WHEEL)); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 1)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| // This point is still inside the non-fast region. |
| status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(), |
| InputHandler::WHEEL); |
| EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, |
| status.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers()); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| host_impl_->active_tree()->set_have_scroll_event_handlers(true); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollByReturnsCorrectValue) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| host_impl_->SetViewportSize(gfx::Size(100, 100)); |
| |
| DrawFrame(); |
| |
| InputHandler::ScrollStatus status = host_impl_->ScrollBegin( |
| BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| status.main_thread_scrolling_reasons); |
| |
| // Trying to scroll to the left/top will not succeed. |
| EXPECT_FALSE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) |
| .did_scroll); |
| EXPECT_FALSE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) |
| .did_scroll); |
| EXPECT_FALSE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) |
| .did_scroll); |
| |
| // Scrolling to the right/bottom will succeed. |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get()) |
| .did_scroll); |
| |
| // Scrolling to left/top will now succeed. |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get()) |
| .did_scroll); |
| |
| // Scrolling diagonally against an edge will succeed. |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get()) |
| .did_scroll); |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get()) |
| .did_scroll); |
| |
| // Trying to scroll more than the available space will also succeed. |
| EXPECT_TRUE( |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get()) |
| .did_scroll); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) { |
| SetupScrollAndContentsLayers(gfx::Size(200, 2000)); |
| host_impl_->SetViewportSize(gfx::Size(100, 1000)); |
| |
| DrawFrame(); |
| |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::WHEEL) |
| .thread); |
| |
| // Trying to scroll if not user_scrollable_vertical will fail. |
| host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(false); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| EXPECT_FALSE(host_impl_->ScrollVerticallyByPage( |
| gfx::Point(), SCROLL_FORWARD)); |
| EXPECT_FALSE(host_impl_->ScrollVerticallyByPage( |
| gfx::Point(), SCROLL_BACKWARD)); |
| |
| host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| EXPECT_TRUE(host_impl_->ScrollVerticallyByPage( |
| gfx::Point(), SCROLL_FORWARD)); |
| EXPECT_FLOAT_EQ(875.f, |
| ScrollDelta(host_impl_->InnerViewportScrollLayer()).y()); |
| EXPECT_TRUE(host_impl_->ScrollVerticallyByPage( |
| gfx::Point(), SCROLL_BACKWARD)); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) { |
| LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(200, 200)); |
| host_impl_->SetViewportSize(gfx::Size(100, 100)); |
| |
| gfx::Size overflow_size(400, 400); |
| ASSERT_EQ(1u, scroll_layer->test_properties()->children.size()); |
| LayerImpl* overflow = scroll_layer->test_properties()->children[0]; |
| overflow->SetBounds(overflow_size); |
| overflow->SetScrollClipLayer( |
| scroll_layer->test_properties()->parent->test_properties()->parent->id()); |
| overflow->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(overflow->id(), |
| gfx::ScrollOffset()); |
| overflow->SetPosition(gfx::PointF()); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| DrawFrame(); |
| gfx::Point scroll_position(10, 10); |
| |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(scroll_position).get(), |
| InputHandler::WHEEL) |
| .thread); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset()); |
| |
| gfx::Vector2dF scroll_delta(10, 10); |
| host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset()); |
| |
| overflow->set_user_scrollable_horizontal(false); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| DrawFrame(); |
| |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(scroll_position).get(), |
| InputHandler::WHEEL) |
| .thread); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset()); |
| |
| host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset()); |
| |
| overflow->set_user_scrollable_vertical(false); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(scroll_position).get(), |
| InputHandler::WHEEL) |
| .thread); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset()); |
| |
| host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, AnimationSchedulingPendingTree) { |
| EXPECT_FALSE(host_impl_->CommitToActiveTree()); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| host_impl_->CreatePendingTree(); |
| auto root_owned = LayerImpl::Create(host_impl_->pending_tree(), 1); |
| auto* root = root_owned.get(); |
| host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); |
| root->SetBounds(gfx::Size(50, 50)); |
| root->test_properties()->force_render_surface = true; |
| |
| root->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->pending_tree(), 2)); |
| LayerImpl* child = root->test_properties()->children[0]; |
| child->SetBounds(gfx::Size(10, 10)); |
| child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); |
| child->SetDrawsContent(true); |
| |
| host_impl_->pending_tree()->SetElementIdsForTesting(); |
| |
| AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 10.0, |
| 3, 0); |
| |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| |
| host_impl_->AnimatePendingTreeAfterCommit(); |
| |
| // An animation exists on the pending layer. Doing |
| // AnimatePendingTreeAfterCommit() requests another frame. |
| // In reality, animations without has_set_start_time() == true do not need to |
| // be continuously ticked on the pending tree, so it should not request |
| // another animation frame here. But we currently do so blindly if any |
| // animation exists. |
| EXPECT_TRUE(did_request_next_frame_); |
| // The pending tree with an animation does not need to draw after animating. |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| |
| did_request_next_frame_ = false; |
| did_request_redraw_ = false; |
| did_request_commit_ = false; |
| |
| host_impl_->ActivateSyncTree(); |
| |
| // When the animation activates, we should request another animation frame |
| // to keep the animation moving. |
| EXPECT_TRUE(did_request_next_frame_); |
| // On activation we don't need to request a redraw for the animation, |
| // activating will draw on its own when it's ready. |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, AnimationSchedulingActiveTree) { |
| EXPECT_FALSE(host_impl_->CommitToActiveTree()); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| host_impl_->active_tree()->SetRootLayerForTesting( |
| LayerImpl::Create(host_impl_->active_tree(), 1)); |
| LayerImpl* root = *host_impl_->active_tree()->begin(); |
| root->SetBounds(gfx::Size(50, 50)); |
| root->test_properties()->force_render_surface = true; |
| |
| root->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 2)); |
| LayerImpl* child = root->test_properties()->children[0]; |
| child->SetBounds(gfx::Size(10, 10)); |
| child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); |
| child->SetDrawsContent(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| host_impl_->active_tree()->SetElementIdsForTesting(); |
| |
| // Add a translate from 6,7 to 8,9. |
| TransformOperations start; |
| start.AppendTranslate(6.f, 7.f, 0.f); |
| TransformOperations end; |
| end.AppendTranslate(8.f, 9.f, 0.f); |
| AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 4.0, |
| start, end); |
| |
| base::TimeTicks now = base::TimeTicks::Now(); |
| host_impl_->WillBeginImplFrame( |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); |
| |
| // TODO(crbug.com/551134): We always request a new frame and a draw for |
| // animations that are on the pending tree, but we don't need to do that |
| // unless they are waiting for some future time to start. |
| EXPECT_TRUE(did_request_next_frame_); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| did_request_next_frame_ = false; |
| did_request_redraw_ = false; |
| did_request_commit_ = false; |
| |
| host_impl_->ActivateAnimations(); |
| |
| // On activating an animation, we should request another frame so that we'll |
| // continue ticking the animation. |
| EXPECT_TRUE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| did_request_next_frame_ = false; |
| did_request_redraw_ = false; |
| did_request_commit_ = false; |
| |
| // The next frame after activating, we'll tick the animation again. |
| host_impl_->Animate(); |
| |
| // An animation exists on the active layer. Doing Animate() requests another |
| // frame after the current one. |
| EXPECT_TRUE(did_request_next_frame_); |
| // The animation should cause us to draw at the frame's deadline. |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) { |
| FakeImplTaskRunnerProvider provider(nullptr); |
| CreateHostImplWithTaskRunnerProvider(DefaultSettings(), CreateOutputSurface(), |
| &provider); |
| EXPECT_TRUE(host_impl_->CommitToActiveTree()); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| auto root_owned = LayerImpl::Create(host_impl_->active_tree(), 1); |
| auto* root = root_owned.get(); |
| host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_owned)); |
| root->SetBounds(gfx::Size(50, 50)); |
| root->SetHasRenderSurface(true); |
| |
| auto child_owned = LayerImpl::Create(host_impl_->active_tree(), 2); |
| auto* child = child_owned.get(); |
| root->test_properties()->AddChild(std::move(child_owned)); |
| child->SetBounds(gfx::Size(10, 10)); |
| child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); |
| child->SetDrawsContent(true); |
| |
| host_impl_->active_tree()->SetElementIdsForTesting(); |
| |
| AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 10.0, |
| 3, 0); |
| |
| // Set up the property trees so that UpdateDrawProperties will work in |
| // CommitComplete below. |
| LayerImplList list; |
| LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( |
| root, gfx::Size(50, 50), &list); |
| LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); |
| |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| |
| host_impl_->CommitComplete(); |
| |
| // Animations on the active tree should be started and ticked, and a new frame |
| // should be requested to continue ticking them. |
| EXPECT_TRUE(did_request_next_frame_); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_FALSE(did_request_commit_); |
| |
| // Delete the LayerTreeHostImpl before the TaskRunnerProvider goes away. |
| host_impl_->ReleaseOutputSurface(); |
| host_impl_ = nullptr; |
| } |
| |
| TEST_F(LayerTreeHostImplTest, AnimationSchedulingOnLayerDestruction) { |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| host_impl_->active_tree()->SetRootLayerForTesting( |
| LayerImpl::Create(host_impl_->active_tree(), 1)); |
| LayerImpl* root = *host_impl_->active_tree()->begin(); |
| root->SetBounds(gfx::Size(50, 50)); |
| |
| root->test_properties()->AddChild( |
| LayerImpl::Create(host_impl_->active_tree(), 2)); |
| LayerImpl* child = root->test_properties()->children[0]; |
| child->SetBounds(gfx::Size(10, 10)); |
| child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); |
| child->SetDrawsContent(true); |
| |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| host_impl_->active_tree()->SetElementIdsForTesting(); |
| |
| // Add a translate animation. |
| TransformOperations start; |
| start.AppendTranslate(6.f, 7.f, 0.f); |
| TransformOperations end; |
| end.AppendTranslate(8.f, 9.f, 0.f); |
| AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 4.0, |
| start, end); |
| |
| base::TimeTicks now = base::TimeTicks::Now(); |
| host_impl_->WillBeginImplFrame( |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); |
| EXPECT_TRUE(did_request_next_frame_); |
| did_request_next_frame_ = false; |
| |
| host_impl_->ActivateAnimations(); |
| // On activating an animation, we should request another frame so that we'll |
| // continue ticking the animation. |
| EXPECT_TRUE(did_request_next_frame_); |
| did_request_next_frame_ = false; |
| |
| // The next frame after activating, we'll tick the animation again. |
| host_impl_->Animate(); |
| // An animation exists on the active layer. Doing Animate() requests another |
| // frame after the current one. |
| EXPECT_TRUE(did_request_next_frame_); |
| did_request_next_frame_ = false; |
| |
| // Destroy layer, unregister animation target (element). |
| child->test_properties()->parent = nullptr; |
| root->test_properties()->RemoveChild(child); |
| child = nullptr; |
| |
| // Doing Animate() doesn't request another frame after the current one. |
| host_impl_->Animate(); |
| EXPECT_FALSE(did_request_next_frame_); |
| |
| host_impl_->Animate(); |
| EXPECT_FALSE(did_request_next_frame_); |
| } |
| |
| class MissingTilesLayer : public LayerImpl { |
| public: |
| MissingTilesLayer(LayerTreeImpl* layer_tree_impl, int id) |
| : LayerImpl(layer_tree_impl, id), has_missing_tiles_(true) {} |
| |
| void set_has_missing_tiles(bool has_missing_tiles) { |
| has_missing_tiles_ = has_missing_tiles; |
| } |
| |
| void AppendQuads(RenderPass* render_pass, |
| AppendQuadsData* append_quads_data) override { |
| append_quads_data->num_missing_tiles += has_missing_tiles_; |
| } |
| |
| private: |
| bool has_missing_tiles_; |
| }; |
| |
| TEST_F(LayerTreeHostImplTest, AnimationMarksLayerNotReady) { |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| host_impl_->active_tree()->SetRootLayerForTesting( |
| LayerImpl::Create(host_impl_->active_tree(), 1)); |
| LayerImpl* root = *host_impl_->active_tree()->begin(); |
| root->SetBounds(gfx::Size(50, 50)); |
| root->SetHasRenderSurface(true); |
| |
| root->test_properties()->AddChild(std::unique_ptr<MissingTilesLayer>( |
| new MissingTilesLayer(host_impl_->active_tree(), 2))); |
| MissingTilesLayer* child = |
| static_cast<MissingTilesLayer*>(root->test_properties()->children[0]); |
| child->SetBounds(gfx::Size(10, 10)); |
| child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); |
| child->SetDrawsContent(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| host_impl_->active_tree()->SetElementIdsForTesting(); |
| |
| EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); |
| |
| // Add a translate from 6,7 to 8,9. |
| TransformOperations start; |
| start.AppendTranslate(6.f, 7.f, 0.f); |
| TransformOperations end; |
| end.AppendTranslate(8.f, 9.f, 0.f); |
| int animation_id = AddAnimatedTransformToElementWithPlayer( |
| child->element_id(), timeline(), 4.0, start, end); |
| |
| base::TimeTicks now = base::TimeTicks::Now(); |
| host_impl_->WillBeginImplFrame( |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now)); |
| |
| host_impl_->ActivateAnimations(); |
| host_impl_->Animate(); |
| |
| EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation()); |
| |
| host_impl_->ResetRequiresHighResToDraw(); |
| |
| // Child layer has an animating transform but missing tiles. |
| FakeLayerTreeHostImpl::FrameData frame; |
| DrawResult result = host_impl_->PrepareToDraw(&frame); |
| EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result); |
| host_impl_->DidDrawAllLayers(frame); |
| |
| child->set_has_missing_tiles(false); |
| |
| // Child layer has an animating and no missing tiles. |
| result = host_impl_->PrepareToDraw(&frame); |
| EXPECT_EQ(DRAW_SUCCESS, result); |
| EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); |
| host_impl_->DidDrawAllLayers(frame); |
| |
| // Remove the animation. |
| child->set_has_missing_tiles(true); |
| RemoveAnimationFromElementWithExistingPlayer(child->element_id(), timeline(), |
| animation_id); |
| child->draw_properties().screen_space_transform_is_animating = false; |
| |
| // Child layer doesn't have an animation, but was never ready since the last |
| // time it animated (and has missing tiles). |
| result = host_impl_->PrepareToDraw(&frame); |
| EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result); |
| EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation()); |
| host_impl_->DidDrawAllLayers(frame); |
| |
| child->set_has_missing_tiles(false); |
| |
| // Child layer doesn't have an animation and all tiles are ready. |
| result = host_impl_->PrepareToDraw(&frame); |
| EXPECT_EQ(DRAW_SUCCESS, result); |
| EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); |
| host_impl_->DidDrawAllLayers(frame); |
| |
| child->set_has_missing_tiles(true); |
| |
| // Child layer doesn't have an animation, and was ready at least once since |
| // the last time it animated. |
| result = host_impl_->PrepareToDraw(&frame); |
| EXPECT_EQ(DRAW_SUCCESS, result); |
| EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); |
| host_impl_->DidDrawAllLayers(frame); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { |
| LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| EXPECT_EQ(scroll_layer, host_impl_->InnerViewportScrollLayer()); |
| LayerImpl* container_layer = scroll_layer->scroll_clip_layer(); |
| EXPECT_EQ(gfx::Size(50, 50), container_layer->bounds()); |
| |
| float min_page_scale = 1.f, max_page_scale = 4.f; |
| float page_scale_factor = 1.f; |
| |
| // The impl-based pinch zoom should adjust the max scroll position. |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread( |
| page_scale_factor, min_page_scale, max_page_scale); |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| |
| float page_scale_delta = 2.f; |
| |
| host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_commit_); |
| EXPECT_EQ(gfx::Size(50, 50), container_layer->bounds()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta); |
| |
| EXPECT_EQ(gfx::ScrollOffset(75.0, 75.0).ToString(), |
| scroll_layer->MaxScrollOffset().ToString()); |
| } |
| |
| // Scrolling after a pinch gesture should always be in local space. The |
| // scroll deltas have the page scale factor applied. |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread( |
| page_scale_factor, min_page_scale, max_page_scale); |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| |
| float page_scale_delta = 2.f; |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point()); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| gfx::Vector2d scroll_delta(0, 10); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), |
| InputHandler::WHEEL) |
| .thread); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_TRUE(ScrollInfoContains( |
| *scroll_info.get(), scroll_layer->id(), |
| gfx::Vector2d(0, scroll_delta.y() / page_scale_delta))); |
| } |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) { |
| LayerTreeSettings settings = DefaultSettings(); |
| CreateHostImpl(settings, |
| CreateOutputSurface()); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.25f, 4.f); |
| |
| const gfx::Size content_size(1000, 1000); |
| const gfx::Size viewport_size(500, 500); |
| CreateBasicVirtualViewportLayers(viewport_size, content_size); |
| |
| LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer(); |
| outer_scroll_layer->SetDrawsContent(true); |
| LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| inner_scroll_layer->SetDrawsContent(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(500, 500), |
| outer_scroll_layer->MaxScrollOffset()); |
| |
| host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| // Sanity check - we're zoomed in, starting from the origin. |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| inner_scroll_layer->CurrentScrollOffset()); |
| |
| // Scroll down - only the inner viewport should scroll. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(50, 50), |
| inner_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| outer_scroll_layer->CurrentScrollOffset()); |
| |
| // Scroll down - outer viewport should start scrolling after the inner is at |
| // its maximum. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000.f, 1000.f)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(250, 250), |
| inner_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(300, 300), |
| outer_scroll_layer->CurrentScrollOffset()); |
| } |
| |
| // Make sure scrolls smaller than a unit applied to the viewport don't get |
| // dropped. crbug.com/539334. |
| TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) { |
| LayerTreeSettings settings = DefaultSettings(); |
| CreateHostImpl(settings, CreateOutputSurface()); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f); |
| |
| const gfx::Size content_size(1000, 1000); |
| const gfx::Size viewport_size(500, 500); |
| CreateBasicVirtualViewportLayers(viewport_size, content_size); |
| |
| LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer(); |
| outer_scroll_layer->SetDrawsContent(true); |
| LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| inner_scroll_layer->SetDrawsContent(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| // Sanity checks. |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(500, 500), |
| outer_scroll_layer->MaxScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll_layer->CurrentScrollOffset()); |
| |
| // Scroll only the layout viewport. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f)).get()); |
| EXPECT_VECTOR2DF_EQ( |
| gfx::Vector2dF(0.125f, 0.125f), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR2DF_EQ( |
| gfx::Vector2dF(0, 0), |
| inner_scroll_layer->CurrentScrollOffset()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f); |
| |
| // Now that we zoomed in, the scroll should be applied to the inner viewport. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f)).get()); |
| EXPECT_VECTOR2DF_EQ( |
| gfx::Vector2dF(0.125f, 0.125f), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR2DF_EQ( |
| gfx::Vector2dF(0.25f, 0.25f), |
| inner_scroll_layer->CurrentScrollOffset()); |
| host_impl_->ScrollEnd(EndState().get()); |
| } |
| |
| // Tests that scrolls during a pinch gesture (i.e. "two-finger" scrolls) work |
| // as expected. That is, scrolling during a pinch should bubble from the inner |
| // to the outer viewport. |
| TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) { |
| LayerTreeSettings settings = DefaultSettings(); |
| CreateHostImpl(settings, |
| CreateOutputSurface()); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f); |
| |
| const gfx::Size content_size(1000, 1000); |
| const gfx::Size viewport_size(500, 500); |
| CreateBasicVirtualViewportLayers(viewport_size, content_size); |
| |
| LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer(); |
| outer_scroll_layer->SetDrawsContent(true); |
| LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| inner_scroll_layer->SetDrawsContent(true); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(500, 500), |
| outer_scroll_layer->MaxScrollOffset()); |
| |
| host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| |
| host_impl_->PinchGestureUpdate(2, gfx::Point(250, 250)); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(125, 125), |
| inner_scroll_layer->CurrentScrollOffset()); |
| |
| // Needed so that the pinch is accounted for in draw properties. |
| DrawFrame(); |
| |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10.f, 10.f)).get()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(130, 130), |
| inner_scroll_layer->CurrentScrollOffset()); |
| |
| DrawFrame(); |
| |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400.f, 400.f)).get()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(80, 80), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(250, 250), |
| inner_scroll_layer->CurrentScrollOffset()); |
| |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| } |
| |
| // Tests the "snapping" of pinch-zoom gestures to the screen edge. That is, when |
| // a pinch zoom is anchored within a certain margin of the screen edge, we |
| // should assume the user means to scroll into the edge of the screen. |
| TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) { |
| LayerTreeSettings settings = DefaultSettings(); |
| CreateHostImpl(settings, |
| CreateOutputSurface()); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f); |
| |
| const gfx::Size content_size(1000, 1000); |
| const gfx::Size viewport_size(500, 500); |
| CreateBasicVirtualViewportLayers(viewport_size, content_size); |
| |
| int offsetFromEdge = Viewport::kPinchZoomSnapMarginDips - 5; |
| gfx::Point anchor(viewport_size.width() - offsetFromEdge, |
| viewport_size.height() - offsetFromEdge); |
| |
| // Pinch in within the margins. The scroll should stay exactly locked to the |
| // bottom and right. |
| host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2, anchor); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(250, 250), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| |
| // Reset. |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f); |
| SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d()); |
| SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d()); |
| |
| // Pinch in within the margins. The scroll should stay exactly locked to the |
| // top and left. |
| anchor = gfx::Point(offsetFromEdge, offsetFromEdge); |
| host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2, anchor); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| |
| // Reset. |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f); |
| SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d()); |
| SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d()); |
| |
| // Pinch in just outside the margin. There should be no snapping. |
| offsetFromEdge = Viewport::kPinchZoomSnapMarginDips; |
| anchor = gfx::Point(offsetFromEdge, offsetFromEdge); |
| host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2, anchor); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(50, 50), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| |
| // Reset. |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f); |
| SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d()); |
| SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d()); |
| |
| // Pinch in just outside the margin. There should be no snapping. |
| offsetFromEdge = Viewport::kPinchZoomSnapMarginDips; |
| anchor = gfx::Point(viewport_size.width() - offsetFromEdge, |
| viewport_size.height() - offsetFromEdge); |
| host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2, anchor); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(200, 200), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) { |
| const gfx::Size content_size(200, 200); |
| const gfx::Size viewport_size(100, 100); |
| CreateBasicVirtualViewportLayers(viewport_size, content_size); |
| |
| LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer(); |
| LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| |
| // Zoom into the page by a 2X factor |
| float min_page_scale = 1.f, max_page_scale = 4.f; |
| float page_scale_factor = 2.f; |
| host_impl_->active_tree()->PushPageScaleFromMainThread( |
| page_scale_factor, min_page_scale, max_page_scale); |
| host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor); |
| |
| // Scroll by a small amount, there should be no bubbling to the outer |
| // viewport. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::WHEEL); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2dF(10.f, 20.f)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(5, 10), |
| inner_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(), |
| outer_scroll_layer->CurrentScrollOffset()); |
| |
| // Scroll by the inner viewport's max scroll extent, the remainder |
| // should bubble up to the outer viewport. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::WHEEL); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(50, 50), |
| inner_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(5, 10), |
| outer_scroll_layer->CurrentScrollOffset()); |
| |
| // Scroll by the outer viewport's max scroll extent, it should all go to the |
| // outer viewport. |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::WHEEL); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2dF(190.f, 180.f)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(100, 100), |
| outer_scroll_layer->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(50, 50), |
| inner_scroll_layer->CurrentScrollOffset()); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) { |
| ui::LatencyInfo latency_info; |
| latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, |
| 1234); |
| std::unique_ptr<SwapPromise> swap_promise( |
| new LatencyInfoSwapPromise(latency_info)); |
| |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, |
| host_impl_ |
| ->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN) |
| .thread); |
| host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); |
| host_impl_->QueueSwapPromiseForMainThreadScrollUpdate( |
| std::move(swap_promise)); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(1u, scroll_info->swap_promises.size()); |
| EXPECT_EQ(latency_info.trace_id(), scroll_info->swap_promises[0]->TraceId()); |
| } |
| |
| // Test that scrolls targeting a layer with a non-null scroll_parent() don't |
| // bubble up. |
| TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) { |
| LayerImpl* viewport_scroll = |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| |
| // Set up two scrolling children of the root, one of which is a scroll parent |
| // to the other. Scrolls shouldn't bubbling from the child. |
| LayerImpl *parent; |
| LayerImpl *child; |
| LayerImpl *child_clip; |
| |
| std::unique_ptr<LayerImpl> scroll_parent_clip = |
| LayerImpl::Create(host_impl_->active_tree(), 6); |
| std::unique_ptr<LayerImpl> scroll_parent = |
| CreateScrollableLayer(7, gfx::Size(10, 10), scroll_parent_clip.get()); |
| parent = scroll_parent.get(); |
| scroll_parent_clip->test_properties()->AddChild(std::move(scroll_parent)); |
| |
| viewport_scroll->test_properties()->AddChild(std::move(scroll_parent_clip)); |
| |
| std::unique_ptr<LayerImpl> scroll_child_clip = |
| LayerImpl::Create(host_impl_->active_tree(), 8); |
| std::unique_ptr<LayerImpl> scroll_child = |
| CreateScrollableLayer(9, gfx::Size(10, 10), scroll_child_clip.get()); |
| child = scroll_child.get(); |
| scroll_child->SetPosition(gfx::PointF(20.f, 20.f)); |
| scroll_child_clip->test_properties()->AddChild(std::move(scroll_child)); |
| |
| child_clip = scroll_child_clip.get(); |
| viewport_scroll->test_properties()->AddChild(std::move(scroll_child_clip)); |
| |
| child_clip->test_properties()->scroll_parent = parent; |
| std::unique_ptr<std::set<LayerImpl*>> scroll_children( |
| new std::set<LayerImpl*>); |
| scroll_children->insert(child_clip); |
| parent->test_properties()->scroll_children.reset(scroll_children.release()); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| |
| DrawFrame(); |
| |
| { |
| host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(5, 5)).get()); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(100, 100)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| // The child should be fully scrolled by the first ScrollBy. |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 5), child->CurrentScrollOffset()); |
| |
| // The scroll_parent shouldn't receive the second ScrollBy. |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), parent->CurrentScrollOffset()); |
| |
| // The viewport shouldn't have been scrolled at all. |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| host_impl_->OuterViewportScrollLayer()->CurrentScrollOffset()); |
| } |
| |
| { |
| host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(3, 4)).get()); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get()); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get()); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get()); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| // The ScrollBy's should scroll the parent to its extent. |
| EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 5), parent->CurrentScrollOffset()); |
| |
| // The viewport shouldn't receive any scroll delta. |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset()); |
| EXPECT_VECTOR_EQ( |
| gfx::Vector2dF(0, 0), |
| host_impl_->OuterViewportScrollLayer()->CurrentScrollOffset()); |
| } |
| } |
| |
| |
| TEST_F(LayerTreeHostImplTest, PinchGesture) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| DCHECK(scroll_layer); |
| |
| float min_page_scale = 1.f; |
| float max_page_scale = 4.f; |
| |
| // Basic pinch zoom in gesture |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| |
| float page_scale_delta = 2.f; |
| host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_commit_); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta); |
| } |
| |
| // Zoom-in clamping |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| float page_scale_delta = 10.f; |
| |
| host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, max_page_scale); |
| } |
| |
| // Zoom-out clamping |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.CollectScrollDeltasForTesting(); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(50, 50)); |
| |
| float page_scale_delta = 0.1f; |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point()); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale); |
| |
| EXPECT_TRUE(scroll_info->scrolls.empty()); |
| } |
| |
| // Two-finger panning should not happen based on pinch events only |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.CollectScrollDeltasForTesting(); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(20, 20)); |
| |
| float page_scale_delta = 1.f; |
| host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10)); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta); |
| EXPECT_TRUE(scroll_info->scrolls.empty()); |
| } |
| |
| // Two-finger panning should work with interleaved scroll events |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.CollectScrollDeltasForTesting(); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(20, 20)); |
| |
| float page_scale_delta = 1.f; |
| host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10)); |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(10, 10), gfx::Vector2d(-10, -10)).get()); |
| host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(), |
| gfx::Vector2d(-10, -10))); |
| } |
| |
| // Two-finger panning should work when starting fully zoomed out. |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(0.5f, 0.5f, 4.f); |
| SetScrollOffsetDelta(scroll_layer, gfx::Vector2d()); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.CollectScrollDeltasForTesting(); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(), |
| gfx::ScrollOffset(0, 0)); |
| |
| host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), |
| InputHandler::TOUCHSCREEN); |
| host_impl_->PinchGestureBegin(); |
| host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0)); |
| host_impl_->PinchGestureUpdate(1.f, gfx::Point(0, 0)); |
| |
| // Needed so layer transform includes page scale. |
| DrawFrame(); |
| |
| host_impl_->ScrollBy( |
| UpdateState(gfx::Point(0, 0), gfx::Vector2d(10, 10)).get()); |
| host_impl_->PinchGestureUpdate(1.f, gfx::Point(10, 10)); |
| host_impl_->PinchGestureEnd(); |
| host_impl_->ScrollEnd(EndState().get()); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, 2.f); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(), |
| gfx::Vector2d(10, 10))); |
| } |
| } |
| |
| TEST_F(LayerTreeHostImplTest, PageScaleAnimation) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| DCHECK(scroll_layer); |
| |
| float min_page_scale = 0.5f; |
| float max_page_scale = 4.f; |
| base::TimeTicks start_time = base::TimeTicks() + |
| base::TimeDelta::FromSeconds(1); |
| base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| base::TimeTicks halfway_through_animation = start_time + duration / 2; |
| base::TimeTicks end_time = start_time + duration; |
| |
| BeginFrameArgs begin_frame_args = |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); |
| |
| // Non-anchor zoom-in |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(50, 50)); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| host_impl_->active_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>( |
| new PendingPageScaleAnimation(gfx::Vector2d(), false, 2.f, |
| duration))); |
| host_impl_->ActivateSyncTree(); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = start_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = halfway_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| did_request_commit_ = false; |
| begin_frame_args.frame_time = end_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_commit_); |
| EXPECT_FALSE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, 2); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(), |
| gfx::Vector2d(-50, -50))); |
| } |
| |
| start_time += base::TimeDelta::FromSeconds(10); |
| halfway_through_animation += base::TimeDelta::FromSeconds(10); |
| end_time += base::TimeDelta::FromSeconds(10); |
| |
| // Anchor zoom-out |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(50, 50)); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| host_impl_->active_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>( |
| new PendingPageScaleAnimation(gfx::Vector2d(25, 25), true, |
| min_page_scale, duration))); |
| host_impl_->ActivateSyncTree(); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = start_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| did_request_redraw_ = false; |
| did_request_commit_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = end_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_TRUE(did_request_commit_); |
| host_impl_->DidFinishImplFrame(); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale); |
| // Pushed to (0,0) via clamping against contents layer size. |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(), |
| gfx::Vector2d(-50, -50))); |
| } |
| } |
| |
| TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| DCHECK(scroll_layer); |
| |
| float min_page_scale = 0.5f; |
| float max_page_scale = 4.f; |
| base::TimeTicks start_time = base::TimeTicks() + |
| base::TimeDelta::FromSeconds(1); |
| base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| base::TimeTicks halfway_through_animation = start_time + duration / 2; |
| base::TimeTicks end_time = start_time + duration; |
| |
| BeginFrameArgs begin_frame_args = |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); |
| |
| // Anchor zoom with unchanged page scale should not change scroll or scale. |
| { |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting( |
| scroll_layer->id(), gfx::ScrollOffset(50, 50)); |
| |
| host_impl_->active_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>( |
| new PendingPageScaleAnimation(gfx::Vector2d(), true, 1.f, |
| duration))); |
| host_impl_->ActivateSyncTree(); |
| begin_frame_args.frame_time = start_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| host_impl_->DidFinishImplFrame(); |
| |
| begin_frame_args.frame_time = halfway_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| host_impl_->DidFinishImplFrame(); |
| |
| begin_frame_args.frame_time = end_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_commit_); |
| host_impl_->DidFinishImplFrame(); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, 1); |
| ExpectNone(*scroll_info, scroll_layer->id()); |
| } |
| } |
| |
| TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) { |
| host_impl_->CreatePendingTree(); |
| host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); |
| CreateScrollAndContentsLayers( |
| host_impl_->pending_tree(), |
| gfx::Size(100, 100)); |
| host_impl_->pending_tree()->BuildPropertyTreesForTesting(); |
| host_impl_->ActivateSyncTree(); |
| DrawFrame(); |
| |
| LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| DCHECK(scroll_layer); |
| |
| float min_page_scale = 0.5f; |
| float max_page_scale = 4.f; |
| host_impl_->sync_tree()->PushPageScaleFromMainThread(1.f, min_page_scale, |
| max_page_scale); |
| host_impl_->ActivateSyncTree(); |
| |
| base::TimeTicks start_time = base::TimeTicks() + |
| base::TimeDelta::FromSeconds(1); |
| base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| base::TimeTicks third_through_animation = start_time + duration / 3; |
| base::TimeTicks halfway_through_animation = start_time + duration / 2; |
| base::TimeTicks end_time = start_time + duration; |
| float target_scale = 2.f; |
| |
| BeginFrameArgs begin_frame_args = |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); |
| |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(), |
| gfx::ScrollOffset(50, 50)); |
| |
| // Make sure TakePageScaleAnimation works properly. |
| |
| host_impl_->sync_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( |
| gfx::Vector2d(), false, target_scale, duration))); |
| std::unique_ptr<PendingPageScaleAnimation> psa = |
| host_impl_->sync_tree()->TakePendingPageScaleAnimation(); |
| EXPECT_EQ(target_scale, psa->scale); |
| EXPECT_EQ(duration, psa->duration); |
| EXPECT_EQ(nullptr, host_impl_->sync_tree()->TakePendingPageScaleAnimation()); |
| |
| // Recreate the PSA. Nothing should happen here since the tree containing the |
| // PSA hasn't been activated yet. |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| host_impl_->sync_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( |
| gfx::Vector2d(), false, target_scale, duration))); |
| begin_frame_args.frame_time = halfway_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_); |
| host_impl_->DidFinishImplFrame(); |
| |
| // Activate the sync tree. This should cause the animation to become enabled. |
| // It should also clear the pointer on the sync tree. |
| host_impl_->ActivateSyncTree(); |
| EXPECT_EQ(nullptr, |
| host_impl_->sync_tree()->TakePendingPageScaleAnimation().get()); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| |
| start_time += base::TimeDelta::FromSeconds(10); |
| third_through_animation += base::TimeDelta::FromSeconds(10); |
| halfway_through_animation += base::TimeDelta::FromSeconds(10); |
| end_time += base::TimeDelta::FromSeconds(10); |
| |
| // From here on, make sure the animation runs as normal. |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = start_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = third_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| // Another activation shouldn't have any effect on the animation. |
| host_impl_->ActivateSyncTree(); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| begin_frame_args.frame_time = halfway_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_redraw_); |
| EXPECT_TRUE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| did_request_redraw_ = false; |
| did_request_next_frame_ = false; |
| did_request_commit_ = false; |
| begin_frame_args.frame_time = end_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_request_commit_); |
| EXPECT_FALSE(did_request_next_frame_); |
| host_impl_->DidFinishImplFrame(); |
| |
| std::unique_ptr<ScrollAndScaleSet> scroll_info = |
| host_impl_->ProcessScrollDeltas(); |
| EXPECT_EQ(scroll_info->page_scale_delta, target_scale); |
| EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(), |
| gfx::Vector2d(-50, -50))); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| DrawFrame(); |
| |
| LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer(); |
| DCHECK(scroll_layer); |
| |
| base::TimeTicks start_time = |
| base::TimeTicks() + base::TimeDelta::FromSeconds(1); |
| base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100); |
| base::TimeTicks halfway_through_animation = start_time + duration / 2; |
| base::TimeTicks end_time = start_time + duration; |
| |
| BeginFrameArgs begin_frame_args = |
| CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE); |
| |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f); |
| scroll_layer->layer_tree_impl() |
| ->property_trees() |
| ->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(), |
| gfx::ScrollOffset(50, 50)); |
| |
| did_complete_page_scale_animation_ = false; |
| host_impl_->active_tree()->SetPendingPageScaleAnimation( |
| std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation( |
| gfx::Vector2d(), false, 2.f, duration))); |
| host_impl_->ActivateSyncTree(); |
| begin_frame_args.frame_time = start_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_FALSE(did_complete_page_scale_animation_); |
| host_impl_->DidFinishImplFrame(); |
| |
| begin_frame_args.frame_time = halfway_through_animation; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_FALSE(did_complete_page_scale_animation_); |
| host_impl_->DidFinishImplFrame(); |
| |
| begin_frame_args.frame_time = end_time; |
| host_impl_->WillBeginImplFrame(begin_frame_args); |
| host_impl_->Animate(); |
| EXPECT_TRUE(did_complete_page_scale_animation_); |
| host_impl_->DidFinishImplFrame(); |
| } |
| |
| TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByBoundsDelta) { |
| SetupScrollAndContentsLayers(gfx::Size(100, 100)); |
| host_impl_->SetViewportSize(gfx::Size(50, 50)); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer(); |
| LayerImpl* inner_container = inner_scroll->scroll_clip_layer(); |
| DCHECK(inner_scroll); |
| DCHECK(inner_container); |
| EXPECT_EQ(gfx::ScrollOffset(50, 50), inner_scroll->MaxScrollOffset()); |
| |
| inner_container->SetBoundsDelta(gfx::Vector2dF(15.f, 15.f)); |
| inner_scroll->SetBoundsDelta(gfx::Vector2dF(7.f, 7.f)); |
| EXPECT_EQ(gfx::ScrollOffset(42, 42), inner_scroll->MaxScrollOffset()); |
| |
| inner_container->SetBoundsDelta(gfx::Vector2dF()); |
| inner_scroll->SetBoundsDelta(gfx::Vector2dF()); |
| inner_scroll->SetBounds(gfx::Size()); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| DrawFrame(); |
| |
| inner_scroll->SetBoundsDelta(gfx::Vector2dF(60.f, 60.f)); |
| EXPECT_EQ(gfx::ScrollOffset(10, 10), inner_scroll->MaxScrollOffset()); |
| } |
| |
| class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl { |
| public: |
| LayerTreeHostImplOverridePhysicalTime( |
| const LayerTreeSettings& settings, |
| LayerTreeHostImplClient* client, |
| TaskRunnerProvider* task_runner_provider, |
| SharedBitmapManager* manager, |
| TaskGraphRunner* task_graph_runner, |
| RenderingStatsInstrumentation* rendering_stats_instrumentation) |
| : LayerTreeHostImpl(settings, |
| client, |
| task_runner_provider, |
| rendering_stats_instrumentation, |
| manager, |
| nullptr, |
| task_graph_runner, |
| AnimationHost::CreateForTesting(ThreadInstance::IMPL), |
| 0) {} |
| |
| BeginFrameArgs CurrentBeginFrameArgs() const override { |
| return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, |
| fake_current_physical_time_); |
| } |
| |
| void SetCurrentPhysicalTimeTicksForTest(base::TimeTicks fake_now) { |
| fake_current_physical_time_ = fake_now; |
| } |
| |
| private: |
| base::TimeTicks fake_current_physical_time_; |
| }; |
| |
| class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { |
| protected: |
| void SetupLayers(LayerTreeSettings settings) { |
| host_impl_->ReleaseOutputSurface(); |
| host_impl_ = nullptr; |
| |
| gfx::Size content_size(100, 100); |
| |
| LayerTreeHostImplOverridePhysicalTime* host_impl_override_time = |
| new LayerTreeHostImplOverridePhysicalTime( |
| settings, this, &task_runner_provider_, &shared_bitmap_manager_, |
| &task_graph_runner_, &stats_instrumentation_); |
| host_impl_ = base::WrapUnique(host_impl_override_time); |
| output_surface_ = CreateOutputSurface(); |
| host_impl_->SetVisible(true); |
| host_impl_->InitializeRenderer(output_surface_.get()); |
| |
| SetupScrollAndContentsLayers(content_size); |
| host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f); |
| host_impl_->SetViewportSize( |
| gfx::Size(content_size.width() / 2, content_size.height() / 2)); |
| |
| std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar = |
| SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), 400, |
| VERTICAL, 10, 0, false, true); |
| scrollbar->test_properties()->opacity = 0.f; |
| EXPECT_FLOAT_EQ(0.f, scrollbar->test_properties()->opacity); |
| |
| LayerImpl* scroll = host_impl_->active_tree()->OuterViewportScrollLayer(); |
| LayerImpl* root = host_impl_->active_tree()->InnerViewportContainerLayer(); |
| scrollbar->SetScrollLayerId(scroll->id()); |
| root->test_properties()->AddChild(std::move(scrollbar)); |
| host_impl_->active_tree()->BuildPropertyTreesForTesting(); |
| host_impl_->active_tree()->DidBecomeActive(); |
| DrawFrame(); |
| } |
| |
| void RunTest(LayerTreeSettings::ScrollbarAnimator animator) { |
| LayerTreeSettings settings = DefaultSettings(); |
| settings.scrollbar_animator = animator; |
| settings.scrollbar_fade_delay_ms = 20; |
| settings.scrollbar_fade_duration_ms = 20; |
| |
| SetupLayers(settings); |
| |
| base::TimeTicks fake_now = base::TimeTicks::Now(); |
| |
| // A task will be posted to fade the initial scrollbar. |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_); |
| EXPECT_FALSE(animation_task_.Equals(base::Closure())); |
| requested_animation_delay_ = base::TimeDelta(); |
| animation_task_ = base::Closure(); |
| |
| // If no scroll happened during a scroll gesture, it should have no effect. |
| host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), |
| InputHandler::WHEEL); |
| host_impl_->ScrollEnd(EndState().get()); |
| EXPECT_FALSE(did_request_next_frame_); |
| EXPECT_FALSE(did_request_redraw_<
|