// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/aura/window_occlusion_tracker.h" #include "base/functional/callback_helpers.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/env.h" #include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/test/window_occlusion_tracker_test_api.h" #include "ui/aura/window_observer.h" #include "ui/aura/window_occlusion_change_builder.h" #include "ui/base/ui_base_features.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/test/layer_animator_test_controller.h" #include "ui/gfx/interpolated_transform.h" namespace aura { namespace { constexpr base::TimeDelta kTransitionDuration = …; class FakeWindowOcclusionChangeBuilder : public WindowOcclusionChangeBuilder { … }; class MockWindowDelegate : public test::ColorTestWindowDelegate { … }; class WindowOcclusionTrackerTest : public test::AuraTestBase { … }; SkRegion SkRegionFromSkIRects(std::initializer_list<SkIRect> rects) { … } } // namespace // Verify that non-overlapping windows have a VISIBLE occlusion state. // _____ _____ // | | | | // |____| |____| TEST_F(WindowOcclusionTrackerTest, NonOverlappingWindows) { … } // Verify that partially overlapping windows have a VISIBLE occlusion state. // ______ // |__| | // |_____| TEST_F(WindowOcclusionTrackerTest, PartiallyOverlappingWindow) { … } // Verify that a window whose bounds are covered by a hidden window is not // occluded. Also, verify that calling Show() on the hidden window causes // occlusion states to be recomputed. // __.... ... = hidden window // |__| . // ....... TEST_F(WindowOcclusionTrackerTest, HiddenWindowCoversWindow) { … } class WindowOcclusionTrackerOpacityTest : public WindowOcclusionTrackerTest, public testing::WithParamInterface<bool> { … }; // Verify that a window whose bounds are covered by a semi-transparent window is // not occluded. Also, verify that that when the opacity of a window changes, // occlusion states are updated. // __.... ... = semi-transparent window // |__| . // ....... TEST_P(WindowOcclusionTrackerOpacityTest, SemiTransparentWindowCoversWindow) { … } // Same as previous test, but the occlusion state of the semi-transparent is not // tracked. TEST_P(WindowOcclusionTrackerOpacityTest, SemiTransparentUntrackedWindowCoversWindow) { … } // Verify that one window whose bounds are covered by a set of two opaque // windows is occluded. // ______ // | | | <-- these two windows cover another window // |__|__| TEST_F(WindowOcclusionTrackerTest, TwoWindowsOccludeOneWindow) { … } // Verify that a window and its child that are covered by a sibling are // occluded. TEST_F(WindowOcclusionTrackerTest, SiblingOccludesWindowAndChild) { … } // Verify that a window with one half covered by a child and the other half // covered by a sibling is non-occluded. TEST_F(WindowOcclusionTrackerTest, ChildAndSiblingOccludeOneWindow) { … } // Verify that a window covered by 2 non-occluded children is non-occluded. TEST_F(WindowOcclusionTrackerTest, ChildrenOccludeOneWindow) { … } // Verify that when the bounds of a child window covers the bounds of a parent // window but is itself visible, the parent window is visible. TEST_F(WindowOcclusionTrackerTest, ChildDoesNotOccludeParent) { … } // Verify that when the stacking order of windows change, occlusion states are // updated. TEST_F(WindowOcclusionTrackerTest, StackingChanged) { … } // Verify that when the stacking order of two transparent window changes, the // occlusion states of their children is updated. The goal of this test is to // ensure that the fact that the windows whose stacking order change are // transparent doesn't prevent occlusion states from being recomputed. TEST_F(WindowOcclusionTrackerTest, TransparentParentStackingChanged) { … } // Verify that when StackChildAtTop() is called on a window whose occlusion // state is not tracked, the occlusion state of tracked siblings is updated. TEST_F(WindowOcclusionTrackerTest, UntrackedWindowStackingChanged) { … } // Verify that occlusion states are updated when the bounds of a window change. TEST_F(WindowOcclusionTrackerTest, BoundsChanged) { … } // Verify that when the bounds of a window are animated, occlusion states are // updated at the beginning and at the end of the animation, but not during the // animation. At the beginning of the animation, the window animated window // should be considered non-occluded and should not occlude other windows. The // animated window starts occluded. TEST_F(WindowOcclusionTrackerTest, OccludedWindowBoundsAnimated) { … } // Same as the previous test, but the animated window starts non-occluded. TEST_F(WindowOcclusionTrackerTest, NonOccludedWindowBoundsAnimated) { … } // Verify that occlusion states are updated when the bounds of a transparent // window with opaque children change. TEST_F(WindowOcclusionTrackerTest, TransparentParentBoundsChanged) { … } // Verify that occlusion states are updated when the bounds of a window whose // occlusion state is not tracked change. TEST_F(WindowOcclusionTrackerTest, UntrackedWindowBoundsChanged) { … } // Verify that occlusion states are updated when the transform of a window // changes. TEST_F(WindowOcclusionTrackerTest, TransformChanged) { … } // Verify that when the transform of a window is animated, occlusion states are // updated at the beginning and at the end of the animation, but not during the // animation. At the beginning of the animation, the window animated window // should be considered non-occluded and should not occlude other windows. The // animated window starts occluded. TEST_F(WindowOcclusionTrackerTest, OccludedWindowTransformAnimated) { … } // Same as the previous test, but the animated window starts non-occluded. TEST_F(WindowOcclusionTrackerTest, NonOccludedWindowTransformAnimated) { … } // Verify that occlusion states are updated when the transform of a transparent // window with opaque children change. TEST_F(WindowOcclusionTrackerTest, TransparentParentTransformChanged) { … } // Verify that occlusion states are updated when the transform of a window whose // occlusion state is not tracked changes. TEST_F(WindowOcclusionTrackerTest, UntrackedWindowTransformChanged) { … } // Verify that deleting an untracked window which covers a tracked window causes // the tracked window to be non-occluded. TEST_F(WindowOcclusionTrackerTest, DeleteUntrackedWindow) { … } // Verify that removing an untracked window which covers a tracked window causes // the tracked window to be non-occluded. TEST_F(WindowOcclusionTrackerTest, RemoveUntrackedWindow) { … } // Verify that when a tracked window is removed and re-added to a root, // occlusion states are still tracked. TEST_F(WindowOcclusionTrackerTest, RemoveAndAddTrackedToRoot) { … } namespace { class ResizeWindowObserver : public WindowObserver { … }; } // namespace // Verify that when the bounds of a child window are updated in response to the // bounds of a parent window being updated, occlusion states are updated once. TEST_F(WindowOcclusionTrackerTest, ResizeChildFromObserver) { … } // Verify that the bounds of windows are changed multiple times within the scope // of a ScopedPause, occlusion states are updated once at the end of the scope. TEST_F(WindowOcclusionTrackerTest, ScopedPause) { … } // Same as the previous test, but with nested ScopedPause. TEST_F(WindowOcclusionTrackerTest, NestedScopedPause) { … } // Verify that bounds are computed correctly when a hierarchy of windows have // transforms. TEST_F(WindowOcclusionTrackerTest, HierarchyOfTransforms) { … } // Verify that clipping is taken into account when computing occlusion. TEST_F(WindowOcclusionTrackerTest, Clipping) { … } // Verify that the DCHECK(!WindowIsAnimated(window)) in // WindowOcclusionTracker::OnWindowDestroyed() doesn't fire if a window is // destroyed with an incomplete animation (~Window should complete the animation // and the window should be removed from |animated_windows_| before // OnWindowDestroyed() is called). TEST_F(WindowOcclusionTrackerTest, DestroyWindowWithPendingAnimation) { … } // Verify that `WindowOcclusionTracker` can be destroyed safely with a pending // animation. This mostly applies to secondary `WindowOcclusionTracker`s, // not the long-lived one in `aura::Env`. TEST_F(WindowOcclusionTrackerTest, DestroyOcclusionTrackerWithPendingAnimation) { … } // Verify that an animated window stops being considered as animated when its // layer is recreated. TEST_F(WindowOcclusionTrackerTest, RecreateLayerOfAnimatedWindow) { … } namespace { class ObserverChangingWindowBounds : public WindowObserver { … }; } // namespace // Verify that no crash occurs if a tracked window is modified by an observer // after it has been added to a new root but before WindowOcclusionTracker has // been notified. TEST_F(WindowOcclusionTrackerTest, ChangeTrackedWindowBeforeObserveAddToRoot) { … } namespace { class ObserverDestroyingWindowOnAnimationEnded : public ui::LayerAnimationObserver { … }; } // namespace // Verify that no crash occurs if a LayerAnimationObserver destroys a tracked // window before WindowOcclusionTracker is notified that the animation ended. TEST_P(WindowOcclusionTrackerOpacityTest, DestroyTrackedWindowFromLayerAnimationObserver) { … } // Verify that no crash occurs if an animation completes on a non-tracked // window's layer after the window has been removed from a root with a tracked // window and deleted. TEST_P(WindowOcclusionTrackerOpacityTest, DeleteNonTrackedAnimatedWindowRemovedFromTrackedRoot) { … } TEST_P(WindowOcclusionTrackerOpacityTest, OpacityAnimationShouldNotOccludeWindow) { … } namespace { class WindowDelegateHidingWindowIfOccluded : public MockWindowDelegate { … }; class WindowDelegateWithQueuedExpectation : public MockWindowDelegate { … }; } // namespace // Verify that a window delegate can change the visibility of another window // when it is notified that its occlusion changed. TEST_F(WindowOcclusionTrackerTest, HideFromOnWindowOcclusionChanged) { … } namespace { class WindowDelegateDeletingWindow : public MockWindowDelegate { … }; } // namespace // Verify that a window can delete a window that is on top of it when it is // notified that its occlusion changed (a crash would occur if // WindowOcclusionTracker accessed that window after it was deleted). TEST_F(WindowOcclusionTrackerTest, DeleteFromOnWindowOcclusionChanged) { … } namespace { class WindowDelegateChangingWindowVisibility : public MockWindowDelegate { … }; } // namespace // Verify that if a window changes its visibility every time it is notified that // its occlusion state changed, a DCHECK occurs. TEST_F(WindowOcclusionTrackerTest, OcclusionStatesDontBecomeStable) { … } // Verify that the occlusion states are correctly updated when a branch of the // tree is hidden. TEST_F(WindowOcclusionTrackerTest, HideTreeBranch) { … } // Verify that a window covered by a shaped window isn't considered occluded. TEST_F(WindowOcclusionTrackerTest, WindowWithAlphaShape) { … } // Verify that a window covered by a window whose parent has an alpha shape // isn't considered occluded. TEST_F(WindowOcclusionTrackerTest, WindowWithParentAlphaShape) { … } namespace { class WindowDelegateHidingWindow : public MockWindowDelegate { … }; class WindowDelegateAddingAndHidingChild : public MockWindowDelegate { … }; } // namespace // Verify that hiding a window that has a hidden parent doesn't cause occlusion // to be recomputed. TEST_F(WindowOcclusionTrackerTest, HideWindowWithHiddenParentOnOcclusionChange) { … } // Verify that hiding a window changes the occlusion region to show that the // window is fully occluded. TEST_F(WindowOcclusionTrackerTest, HideWindowChangesOcclusionRegionToBeFullyOccluded) { … } // Test partial occlusion, test partial occlusion changing hidden, alpha shape // occlusion from multiple windows // Verify that a window can occlude another one partially. TEST_F(WindowOcclusionTrackerTest, WindowOccludesWindowPartially) { … } // Verify that windows with alpha shape do not affect occlusion regions. TEST_F(WindowOcclusionTrackerTest, WindowWithAlphaShapeDoesNotPartiallyOccludeOtherWindows) { … } // Verify that a window can be occluded by multiple other windows. TEST_F(WindowOcclusionTrackerTest, WindowCanBeOccludedByMultipleWindows) { … } // Verify that the excluded window is indeed ignored by occlusion tracking. TEST_P(WindowOcclusionTrackerOpacityTest, ExcludeWindow) { … } // Test that calling OnOcclusionStateChanged on a root window causes children // of the root window to have their delegate notified that it is occluded or // visible, depending on whether the root window is occluded or not. TEST_F(WindowOcclusionTrackerTest, NativeWindowOcclusion) { … } TEST_F(WindowOcclusionTrackerTest, ScopedForceVisible) { … } TEST_F(WindowOcclusionTrackerTest, ScopedForceVisibleSiblingsIgnored) { … } TEST_F(WindowOcclusionTrackerTest, ScopedForceVisibleWithOccludedSibling) { … } // Simulates a scenario in which a browser window is forced visible (e.g. while // projecting) and its parent container (e.g. a virtual desks container) was // hidden. Verifies that the browser window and its descendants remain visible // from an occlusion stand point. TEST_F(WindowOcclusionTrackerTest, ScopedForceVisibleHiddenContainer) { … } TEST_F(WindowOcclusionTrackerTest, ComputeTargetOcclusionForWindow) { … } TEST_F(WindowOcclusionTrackerTest, ComputeTargetOcclusionForWindowUsesTargetBounds) { … } TEST_P(WindowOcclusionTrackerOpacityTest, ComputeTargetOcclusionForWindowUsesTargetOpacity) { … } TEST_F(WindowOcclusionTrackerTest, ComputeTargetOcclusionForWindowUsesTargetVisibility) { … } TEST_F(WindowOcclusionTrackerTest, ComputeTargetOcclusionForWindowTransformHierarchy) { … } TEST_F(WindowOcclusionTrackerTest, ComputeTargetOcclusionForAnimatedWindow) { … } TEST_F(WindowOcclusionTrackerTest, SetOpaqueRegionsForOcclusionAffectsOcclusionOfOtherWindows) { … } TEST_F( WindowOcclusionTrackerTest, SetOpaqueRegionsForOcclusionOfAWindowDoesNotAffectOcclusionOfThatWindowItself) { … } TEST_F(WindowOcclusionTrackerTest, SemiOpaqueSolidColorLayerDoesNotAffectChildOpacity) { … } TEST_F(WindowOcclusionTrackerTest, OccludedFractionalWindow) { … } TEST_F(WindowOcclusionTrackerTest, OccludingFractionalWindow) { … } TEST_F(WindowOcclusionTrackerTest, ClipToRootWindow) { … } // Run tests with LAYER_TEXTURE_LAYER type or LAYER_SOLID_COLOR type. INSTANTIATE_TEST_SUITE_P(…); } // namespace aura