0

[Mac Window Occlusion] Reenable manual occlusion detection tests

This cl reenables manual occlusion detection for < macOS 13.0 and
>= macOS 13.3. It also combines the previous two experiments
(occlusion detection and display sleep) into a single experiment
that combines both features, per a previous review with Catan.

Test: out/Default/content_browsertests \
  --gtest_filter=*WindowOcclusionBrowser*

Bug: 883031
Change-Id: I2c5a4de21bb64406fa53bff7a7644df6671b7188
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4529204
Reviewed-by: Leonard Grey <lgrey@chromium.org>
Commit-Queue: Jayson Adams <shrike@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1152207}
This commit is contained in:
Jayson Adams
2023-06-01 22:56:43 +00:00
committed by Chromium LUCI CQ
parent 10e1fa2d12
commit 93b0bbc89b
3 changed files with 91 additions and 69 deletions

@ -31,6 +31,14 @@ extern CONTENT_EXPORT const base::FeatureParam<bool>
+ (instancetype)sharedInstance;
// Returns YES if the specified version is less than 13.0 or more than 13.2.
// Manual occlusion detection is not supported on macOS 13.0-13.2.
+ (BOOL)manualOcclusionDetectionSupportedForVersion:(int32_t)major
:(int32_t)minor;
// Returns YES if manual occlusion detection is supported for the current macOS.
+ (BOOL)manualOcclusionDetectionSupportedForCurrentMacOSVersion;
// API exposed for testing.
// Resets the state of `sharedInstance` during tests.

@ -15,6 +15,7 @@
#import "base/mac/scoped_objc_class_swizzler.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
#include "base/system/sys_info.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
@ -24,8 +25,6 @@ using features::kMacWebContentsOcclusion;
// Experiment features.
const base::FeatureParam<bool> kEnhancedWindowOcclusionDetection{
&kMacWebContentsOcclusion, "EnhancedWindowOcclusionDetection", false};
const base::FeatureParam<bool> kDisplaySleepAndAppHideDetection{
&kMacWebContentsOcclusion, "DisplaySleepAndAppHideDetection", false};
namespace {
@ -82,6 +81,25 @@ bool IsBrowserProcess() {
return sharedInstance->get();
}
+ (BOOL)manualOcclusionDetectionSupportedForVersion:(int32_t)major
:(int32_t)minor {
if (major != 13) {
return YES;
}
return minor >= 3;
}
+ (BOOL)manualOcclusionDetectionSupportedForCurrentMacOSVersion {
int32_t major_version;
int32_t minor_version;
int32_t bugfix_version;
base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
&bugfix_version);
return [self manualOcclusionDetectionSupportedForVersion:
major_version:minor_version];
}
+ (void)resetSharedInstanceForTesting {
[self sharedOcclusionChecker]->reset();
}
@ -125,6 +143,12 @@ bool IsBrowserProcess() {
return _windowClassSwizzler.get();
}
- (BOOL)isManualOcclusionDetectionEnabled {
return [WebContentsOcclusionCheckerMac
manualOcclusionDetectionSupportedForCurrentMacOSVersion] &&
kEnhancedWindowOcclusionDetection.Get();
}
// Alternative implementation of orderWindow:relativeTo:. Replaces
// NSWindow's version, allowing the occlusion checker to learn about
// window ordering events.
@ -135,8 +159,10 @@ bool IsBrowserProcess() {
->InvokeOriginal<void, NSWindowOrderingMode, NSInteger>(
self, _cmd, orderingMode, otherWindowNumber);
if (!kEnhancedWindowOcclusionDetection.Get())
if (![[WebContentsOcclusionCheckerMac sharedInstance]
isManualOcclusionDetectionEnabled]) {
return;
}
[[NSNotificationCenter defaultCenter]
postNotificationName:kWindowDidChangePositionInWindowList
@ -148,7 +174,7 @@ bool IsBrowserProcess() {
NSNotificationCenter* notificationCenter =
[NSNotificationCenter defaultCenter];
if (kEnhancedWindowOcclusionDetection.Get()) {
if ([self isManualOcclusionDetectionEnabled]) {
[notificationCenter addObserver:self
selector:@selector(windowWillMove:)
name:NSWindowWillMoveNotification
@ -174,6 +200,17 @@ bool IsBrowserProcess() {
selector:@selector(windowDidChangePositionInWindowList:)
name:kWindowDidChangePositionInWindowList
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(displaysDidSleep:)
name:NSWorkspaceScreensDidSleepNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(displaysDidWake:)
name:NSWorkspaceScreensDidWakeNotification
object:nil];
}
[notificationCenter addObserver:self
@ -197,19 +234,6 @@ bool IsBrowserProcess() {
selector:@selector(fullscreenTransitionComplete:)
name:NSWindowDidExitFullScreenNotification
object:nil];
if (kDisplaySleepAndAppHideDetection.Get()) {
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(displaysDidSleep:)
name:NSWorkspaceScreensDidSleepNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(displaysDidWake:)
name:NSWorkspaceScreensDidWakeNotification
object:nil];
}
}
- (BOOL)windowCanTriggerOcclusionUpdates:(NSWindow*)window {
@ -400,9 +424,8 @@ bool IsBrowserProcess() {
return YES;
}
// If manual occlusion detection is disabled in the experiement, return the
// answer from macOS.
if (!kEnhancedWindowOcclusionDetection.Get()) {
// If manual occlusion detection is disabled, return the answer from macOS.
if (![self isManualOcclusionDetectionEnabled]) {
return NO;
}

@ -25,9 +25,13 @@ namespace {
const int kNeverCalled = -100;
struct FeatureState {
bool feature_enabled = false;
bool enhanced_occlusion_detection_enabled = false;
bool display_sleep_detection_enabled = false;
};
struct Version {
int32_t major;
int32_t minor;
bool supported;
};
} // namespace
@ -141,6 +145,8 @@ struct FeatureState {
- (instancetype)init {
self = [super init];
// The tests should access WebContentsOcclusionCheckerMac directly, rather
// than through NSClassFromString(). See crbug.com/1450724 .
[WebContentVisibilityUpdateWatcher performOcclusionStateUpdatesSwizzler]
.reset(new base::mac::ScopedObjCClassSwizzler(
NSClassFromString(@"WebContentsOcclusionCheckerMac"),
@ -329,12 +335,9 @@ class WindowOcclusionBrowserTestMac
public ContentBrowserTest {
public:
WindowOcclusionBrowserTestMac() {
if (GetParam().feature_enabled) {
if (GetParam().enhanced_occlusion_detection_enabled) {
base::FieldTrialParams params;
if (GetParam().enhanced_occlusion_detection_enabled)
params["EnhancedWindowOcclusionDetection"] = "true";
if (GetParam().display_sleep_detection_enabled)
params["DisplaySleepAndAppHideDetection"] = "true";
params["EnhancedWindowOcclusionDetection"] = "true";
_features.InitAndEnableFeatureWithParameters(
features::kMacWebContentsOcclusion, params);
} else {
@ -343,9 +346,10 @@ class WindowOcclusionBrowserTestMac
}
void SetUp() override {
if (base::mac::IsAtLeastOS13()) {
if (![NSClassFromString(@"WebContentsOcclusionCheckerMac")
manualOcclusionDetectionSupportedForCurrentMacOSVersion]) {
GTEST_SKIP()
<< "Manual window occlusion detection is broken on macOS Ventura.";
<< "Manual window occlusion detection is broken on macOS 13.0-13.2.";
}
ContentBrowserTest::SetUp();
}
@ -467,8 +471,7 @@ class WindowOcclusionBrowserTestMac
void OrderWindowFront(NSWindow* window) {
base::scoped_nsobject<WebContentVisibilityUpdateCounter> watcher;
if (!kEnhancedWindowOcclusionDetection.Get() &&
!kDisplaySleepAndAppHideDetection.Get()) {
if (!kEnhancedWindowOcclusionDetection.Get()) {
watcher.reset([[WebContentVisibilityUpdateCounter alloc] init]);
}
@ -477,8 +480,6 @@ class WindowOcclusionBrowserTestMac
if (kEnhancedWindowOcclusionDetection.Get()) {
WaitForOcclusionUpdate();
} else if (!kDisplaySleepAndAppHideDetection.Get()) {
EXPECT_TRUE([WebContentVisibilityUpdateCounter methodNeverCalled]);
}
}
@ -569,55 +570,45 @@ using WindowOcclusionBrowserTestMacWithoutOcclusionFeature =
WindowOcclusionBrowserTestMac;
using WindowOcclusionBrowserTestMacWithOcclusionDetectionFeature =
WindowOcclusionBrowserTestMac;
using WindowOcclusionBrowserTestMacWithDisplaySleepDetectionFeature =
WindowOcclusionBrowserTestMac;
// Tests that should only work without the occlusion detection feature.
INSTANTIATE_TEST_SUITE_P(
NoFeature,
WindowOcclusionBrowserTestMacWithoutOcclusionFeature,
::testing::Values(FeatureState{.feature_enabled = false},
// Feature should be a no-op without parameters.
FeatureState{.feature_enabled = true}));
INSTANTIATE_TEST_SUITE_P(NoFeature,
WindowOcclusionBrowserTestMacWithoutOcclusionFeature,
::testing::Values(FeatureState{
.enhanced_occlusion_detection_enabled = false}));
// Tests that should work with or without the occlusion detection feature.
INSTANTIATE_TEST_SUITE_P(
Common,
WindowOcclusionBrowserTestMac,
::testing::Values(FeatureState{.feature_enabled = false},
FeatureState{.feature_enabled = true},
FeatureState{
.feature_enabled = true,
.enhanced_occlusion_detection_enabled = true},
FeatureState{.feature_enabled = true,
.display_sleep_detection_enabled = true},
FeatureState{.feature_enabled = true,
.enhanced_occlusion_detection_enabled = true,
.display_sleep_detection_enabled = true}));
::testing::Values(
FeatureState{.enhanced_occlusion_detection_enabled = false},
FeatureState{.enhanced_occlusion_detection_enabled = true}));
// Tests that require enhanced window occlusion detection.
INSTANTIATE_TEST_SUITE_P(
EnhancedWindowOcclusionDetection,
WindowOcclusionBrowserTestMacWithOcclusionDetectionFeature,
::testing::Values(
FeatureState{.feature_enabled = true,
.enhanced_occlusion_detection_enabled = true},
FeatureState{.feature_enabled = true,
.enhanced_occlusion_detection_enabled = true,
.display_sleep_detection_enabled = true}));
::testing::Values(FeatureState{
.enhanced_occlusion_detection_enabled = true}));
// Tests that require display sleep and app hide detection.
INSTANTIATE_TEST_SUITE_P(
DisplaySleepAndAppHideDetection,
WindowOcclusionBrowserTestMacWithDisplaySleepDetectionFeature,
::testing::Values(FeatureState{.feature_enabled = true,
.display_sleep_detection_enabled = true},
FeatureState{.feature_enabled = true,
.enhanced_occlusion_detection_enabled = true,
.display_sleep_detection_enabled = true}));
// Tests that we correctly disallow unsupported macOS versions.
IN_PROC_BROWSER_TEST_P(WindowOcclusionBrowserTestMac, MacOSVersionChecking) {
Class WebContentsOcclusionCheckerMac =
NSClassFromString(@"WebContentsOcclusionCheckerMac");
std::vector<Version> versions = {
{11, 0, true}, {12, 0, true}, {12, 9, true}, {13, 0, false},
{13, 1, false}, {13, 2, false}, {13, 3, true}, {14, 0, true}};
// Test that enhanced occlusion detection doesn't work if the feature's not
// enabled.
for (const auto& version : versions) {
bool supported = [WebContentsOcclusionCheckerMac manualOcclusionDetectionSupportedForVersion:version.major
:version.minor];
EXPECT_EQ(supported, version.supported);
}
}
// Tests that enhanced occlusion detection isn't triggered if the feature's
// not enabled.
IN_PROC_BROWSER_TEST_P(WindowOcclusionBrowserTestMacWithoutOcclusionFeature,
ManualOcclusionDetectionDisabled) {
InitWindowA();
@ -820,7 +811,7 @@ IN_PROC_BROWSER_TEST_P(
// Checks that web contents are marked kHidden on display sleep.
IN_PROC_BROWSER_TEST_P(
WindowOcclusionBrowserTestMacWithDisplaySleepDetectionFeature,
WindowOcclusionBrowserTestMacWithOcclusionDetectionFeature,
OcclusionDetectionOnDisplaySleep) {
InitWindowA();