Commit 8ab14436 authored by Sidney San Martín's avatar Sidney San Martín Committed by Commit Bot

[Mac] Fix hidden NSViews by bringing back a dedicated compositor view.

Until macOS 10.13, the relative ordering of a view's subviews' layers and
unassociated sublayers is undefined, so view layers can end up hidden behind
the compositor layer. See:

  https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKit/index.html#10_13Layer-backed%20Views

This change re-adds a dedicated compositor view, but with less plumbing than
what was removed in r594160.

Bug: 865901, 899499
Change-Id: Ibbec83da2e8785e06522008dfc2d9eab7ca43bf9
Reviewed-on: https://chromium-review.googlesource.com/c/1334290
Commit-Queue: Sidney San Martín <sdy@chromium.org>
Reviewed-by: default avatarccameron <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608139}
parent 0e55d445
......@@ -2206,11 +2206,8 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest {
widget_ = CreateTopLevelPlatformWidget();
// It's not a bug if the native view has a different number of subviews,
// but the rest of this test assumes it. It may or may not be worth
// removing that assumption at some point.
ASSERT_EQ(0u,
[[widget_->GetNativeView().GetNativeNSView() subviews] count]);
starting_subviews_.reset(
[[widget_->GetNativeView().GetNativeNSView() subviews] copy]);
native_host_parent_ = new View();
widget_->GetContentsView()->AddChildView(native_host_parent_);
......@@ -2224,7 +2221,9 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest {
}
EXPECT_EQ(kNativeViewCount, native_host_parent_->child_count());
EXPECT_NSEQ([widget_->GetNativeView().GetNativeNSView() subviews],
(@[ hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view()
]]));
}
void TearDown() override {
......@@ -2236,9 +2235,12 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest {
return widget_->GetNativeView().GetNativeNSView();
}
NSArray<NSView*>* GetStartingSubviews() { return starting_subviews_; }
Widget* widget_ = nullptr;
View* native_host_parent_ = nullptr;
std::vector<std::unique_ptr<NativeHostHolder>> hosts_;
base::scoped_nsobject<NSArray<NSView*>> starting_subviews_;
private:
DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacViewsOrderTest);
......@@ -2249,22 +2251,30 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest {
TEST_F(NativeWidgetMacViewsOrderTest, NativeViewAttached) {
hosts_[1]->Detach();
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[0]->view(), hosts_[2]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[2]->view()
]]));
hosts_[1]->AttachNativeView();
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view()
]]));
}
// Tests that NativeViews order changes according to views::View hierarchy.
TEST_F(NativeWidgetMacViewsOrderTest, ReorderViews) {
native_host_parent_->ReorderChildView(hosts_[2]->host(), 1);
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[0]->view(), hosts_[2]->view(), hosts_[1]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[2]->view(), hosts_[1]->view()
]]));
native_host_parent_->RemoveChildView(hosts_[2]->host());
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[0]->view(), hosts_[1]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[1]->view()
]]));
View* new_parent = new View();
native_host_parent_->RemoveChildView(hosts_[1]->host());
......@@ -2272,11 +2282,15 @@ TEST_F(NativeWidgetMacViewsOrderTest, ReorderViews) {
new_parent->AddChildView(hosts_[1]->host());
new_parent->AddChildView(hosts_[2]->host());
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view()
]]));
native_host_parent_->ReorderChildView(new_parent, 0);
EXPECT_NSEQ([GetContentNativeView() subviews],
(@[ hosts_[1]->view(), hosts_[2]->view(), hosts_[0]->view() ]));
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[1]->view(), hosts_[2]->view(), hosts_[0]->view()
]]));
}
// Test that unassociated native views stay on top after reordering.
......@@ -2284,15 +2298,17 @@ TEST_F(NativeWidgetMacViewsOrderTest, UnassociatedViewsIsAbove) {
base::scoped_nsobject<NSView> child_view([[NSView alloc] init]);
[GetContentNativeView() addSubview:child_view];
EXPECT_NSEQ(
[GetContentNativeView() subviews], (@[
[GetContentNativeView() subviews],
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view(), child_view
]));
]]));
native_host_parent_->ReorderChildView(hosts_[2]->host(), 1);
EXPECT_NSEQ(
[GetContentNativeView() subviews], (@[
[GetContentNativeView() subviews],
([GetStartingSubviews() arrayByAddingObjectsFromArray:@[
hosts_[0]->view(), hosts_[2]->view(), hosts_[1]->view(), child_view
]));
]]));
}
// Test -[NSWindowDelegate windowShouldClose:].
......
......@@ -44,6 +44,18 @@ namespace {
constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5);
} // namespace
// The NSView that hosts the composited CALayer drawing the UI. It fills the
// window but is not hittable so that accessibility hit tests always go to the
// BridgedContentView.
@interface ViewsCompositorSuperview : NSView
@end
@implementation ViewsCompositorSuperview
- (NSView*)hitTest:(NSPoint)aPoint {
return nil;
}
@end
// Self-owning animation delegate that starts a hide animation, then calls
// -[NSWindow close] when the animation ends, releasing itself.
@interface ViewsNSWindowCloseAnimator : NSObject<NSAnimationDelegate> {
......@@ -172,6 +184,9 @@ NSComparisonResult SubviewSorter(NSViewComparatorValue lhs,
void* rank_as_void) {
DCHECK_NE(lhs, rhs);
if ([lhs isKindOfClass:[ViewsCompositorSuperview class]])
return NSOrderedAscending;
const RankMap* rank = static_cast<const RankMap*>(rank_as_void);
auto left_rank = rank->find(lhs);
auto right_rank = rank->find(rhs);
......@@ -486,13 +501,20 @@ void BridgedNativeWidgetImpl::CreateContentView(uint64_t ns_view_id,
// this should be treated as an error and caught early.
CHECK(bridged_view_);
// Set the layer first to create a layer-hosting view (not layer-backed), and
// set the compositor output to go to that layer.
base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
// Beware: This view was briefly removed (in favor of a bare CALayer) in
// crrev/c/1236675. The ordering of unassociated layers relative to NSView
// layers is undefined on macOS 10.12 and earlier, so the compositor layer
// ended up covering up subviews (see crbug/899499).
base::scoped_nsobject<NSView> compositor_view(
[[ViewsCompositorSuperview alloc] initWithFrame:[bridged_view_ bounds]]);
[compositor_view
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
auto* background_layer = [CALayer layer];
display_ca_layer_tree_ =
std::make_unique<ui::DisplayCALayerTree>(background_layer.get());
[bridged_view_ setLayer:background_layer];
[bridged_view_ setWantsLayer:YES];
std::make_unique<ui::DisplayCALayerTree>(background_layer);
[compositor_view setLayer:background_layer];
[compositor_view setWantsLayer:YES];
[bridged_view_ addSubview:compositor_view];
[window_ setContentView:bridged_view_];
}
......
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