Commit 3da2da6a authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

aura: Do not allow occlusion by shaped windows.

Preivously, the shape of a window was not taken into account when
computing occlusion state. A window with a shape that didn't fully
cover its bounds could wrongly be considered to occlude another
window underneath it.

With this CL, shaped windows can't occlude other windows (similar
to windows that are animated or not axis-aligned).

Bug: 864508
Change-Id: I01ff1afda438982964cdc2d786f37caaa03677be
Reviewed-on: https://chromium-review.googlesource.com/1173318Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Commit-Queue: François Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#586011}
parent 6954c956
......@@ -36,13 +36,23 @@ WindowOcclusionTracker* g_tracker = nullptr;
int g_num_pause_occlusion_tracking = 0;
bool WindowOrParentHasShape(Window* window) {
if (window->layer()->alpha_shape())
return true;
if (window->parent())
return WindowOrParentHasShape(window->parent());
return false;
}
// Returns true if |window| opaquely fills its bounds. |window| must be visible.
bool VisibleWindowIsOpaque(Window* window) {
DCHECK(window->IsVisible());
DCHECK(window->layer());
return !window->transparent() &&
window->layer()->type() != ui::LAYER_NOT_DRAWN &&
window->layer()->GetCombinedOpacity() == 1.0f;
window->layer()->GetCombinedOpacity() == 1.0f &&
// For simplicity, a shaped window is not considered opaque.
!WindowOrParentHasShape(window);
}
// Returns the transform of |window| relative to its root.
......@@ -407,6 +417,15 @@ bool WindowOcclusionTracker::WindowOrDescendantIsOpaque(
return false;
}
bool WindowOcclusionTracker::WindowOpacityChangeMayAffectOcclusionStates(
Window* window) const {
// Changing the opacity of a window has no effect on the occlusion state of
// the window or its children. It can however affect the occlusion state of
// other windows in the tree if it is visible and not animated (animated
// windows aren't considered in occlusion computations).
return window->IsVisible() && !WindowOrParentIsAnimated(window);
}
bool WindowOcclusionTracker::WindowMoveMayAffectOcclusionStates(
Window* window) const {
return !WindowOrParentIsAnimated(window) &&
......@@ -531,7 +550,14 @@ void WindowOcclusionTracker::OnWindowOpacitySet(
(reason == ui::PropertyChangeReason::FROM_ANIMATION) &&
MaybeObserveAnimatedWindow(window);
MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf(window, [=]() {
return animation_started || !WindowOrParentIsAnimated(window);
return animation_started ||
WindowOpacityChangeMayAffectOcclusionStates(window);
});
}
void WindowOcclusionTracker::OnWindowAlphaShapeSet(Window* window) {
MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf(window, [=]() {
return WindowOpacityChangeMayAffectOcclusionStates(window);
});
}
......
......@@ -141,6 +141,10 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
bool assume_parent_opaque = false,
bool assume_window_opaque = false) const;
// Returns true if changing the opacity or alpha state of |window| could
// affect the occlusion state of a tracked window.
bool WindowOpacityChangeMayAffectOcclusionStates(Window* window) const;
// Returns true if changing the transform, bounds or stacking order of
// |window| could affect the occlusion state of a tracked window.
bool WindowMoveMayAffectOcclusionStates(Window* window) const;
......@@ -174,6 +178,7 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
ui::PropertyChangeReason reason) override;
void OnWindowOpacitySet(Window* window,
ui::PropertyChangeReason reason) override;
void OnWindowAlphaShapeSet(Window* window) override;
void OnWindowTransformed(Window* window,
ui::PropertyChangeReason reason) override;
void OnWindowStackingChanged(Window* window) override;
......
......@@ -1525,6 +1525,72 @@ TEST_F(WindowOcclusionTrackerTest, HideTreeBranch) {
EXPECT_FALSE(delegate_c->is_expecting_call());
}
// Verify that a window covered by a shaped window isn't considered occluded.
TEST_F(WindowOcclusionTrackerTest, WindowWithAlphaShape) {
// Create 2 superposed tracked windows.
MockWindowDelegate* delegate_a = new MockWindowDelegate();
delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
CreateTrackedWindow(delegate_a, gfx::Rect(0, 0, 10, 10));
EXPECT_FALSE(delegate_a->is_expecting_call());
MockWindowDelegate* delegate_b = new MockWindowDelegate();
delegate_a->set_expectation(Window::OcclusionState::OCCLUDED);
delegate_b->set_expectation(Window::OcclusionState::VISIBLE);
Window* window_b = CreateTrackedWindow(delegate_b, gfx::Rect(0, 0, 10, 10));
EXPECT_FALSE(delegate_a->is_expecting_call());
EXPECT_FALSE(delegate_b->is_expecting_call());
// Set a shape for the top window. The window underneath should no longer be
// occluded.
auto shape = std::make_unique<ui::Layer::ShapeRects>();
shape->emplace_back(0, 0, 5, 5);
delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
window_b->layer()->SetAlphaShape(std::move(shape));
EXPECT_FALSE(delegate_a->is_expecting_call());
// Clear the shape for the top window. The window underneath should be
// occluded.
delegate_a->set_expectation(Window::OcclusionState::OCCLUDED);
window_b->layer()->SetAlphaShape(nullptr);
EXPECT_FALSE(delegate_a->is_expecting_call());
}
// Verify that a window covered by a window whose parent has an alpha shape
// isn't considered occluded.
TEST_F(WindowOcclusionTrackerTest, WindowWithParentAlphaShape) {
// Create a child and parent that cover another window.
MockWindowDelegate* delegate_a = new MockWindowDelegate();
delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
CreateTrackedWindow(delegate_a, gfx::Rect(0, 0, 20, 20));
EXPECT_FALSE(delegate_a->is_expecting_call());
MockWindowDelegate* delegate_b = new MockWindowDelegate();
delegate_b->set_expectation(Window::OcclusionState::VISIBLE);
Window* window_b = CreateTrackedWindow(delegate_b, gfx::Rect(0, 0, 10, 10));
EXPECT_FALSE(delegate_b->is_expecting_call());
MockWindowDelegate* delegate_c = new MockWindowDelegate();
delegate_a->set_expectation(Window::OcclusionState::OCCLUDED);
delegate_c->set_expectation(Window::OcclusionState::VISIBLE);
CreateTrackedWindow(delegate_c, gfx::Rect(0, 0, 20, 20), window_b);
EXPECT_FALSE(delegate_a->is_expecting_call());
EXPECT_FALSE(delegate_c->is_expecting_call());
// Set a shape for |window_b|. |window_a| and |window_b| should no longer be
// occluded.
auto shape = std::make_unique<ui::Layer::ShapeRects>();
shape->emplace_back(0, 0, 5, 5);
delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
window_b->layer()->SetAlphaShape(std::move(shape));
EXPECT_FALSE(delegate_a->is_expecting_call());
// Clear the shape for |window_b|. |window_a| and |window_b| should be
// occluded.
delegate_a->set_expectation(Window::OcclusionState::OCCLUDED);
window_b->layer()->SetAlphaShape(nullptr);
EXPECT_FALSE(delegate_a->is_expecting_call());
}
namespace {
class WindowDelegateHidingWindow : public MockWindowDelegate {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment