Skip to content

Commit

Permalink
[iOS 16] All layout tests in fast/events/ios/rotation time out
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=242199
rdar://93784344

Reviewed by Devin Rousso and Tim Horton.

For all apps linked on or after iOS 16, `-[UIDevice setOrientation:animated:]` is a no-op; this
causes all layout tests that attempt to simulate rotation (i.e., all the tests inside
fast/events/ios/rotation) to time out while waiting for the web view to resize during rotation.

In its place, UIKit has added `-[UIWindowScene requestGeometryUpdateWithPreferences:errorHandler:]`,
which can be used to request a scene-level transition to one or more requested interface
orientations; fix these failing layout tests by adopting this API instead on iOS 16.

* Source/WTF/wtf/PlatformHave.h:

Add a new build-time flag to guard the availability of the new UIKit API.

* Tools/WebKitTestRunner/ios/TestControllerIOS.mm:
(WTR::restorePortraitOrientationIfNeeded):
(WTR::TestController::platformResetStateToConsistentValues):

Additionally refactor logic for restoring device orientation to portrait mode between tests, so that
it's also compatible with iOS 16.

* Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h:
* Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptControllerIOS::simulateRotation):
(WTR::UIScriptControllerIOS::simulateRotationLikeSafari):

Factor out common logic into a private helper method; use `-requestGeometryUpdateWithPreferences:`
on iOS 16, and fall back to `-setOrientation:animated:` elsewhere.

(WTR::toWindowSceneGeometryPreferences):
(WTR::toUIDeviceOrientation):

Canonical link: https://commits.webkit.org/252016@main
  • Loading branch information
whsieh committed Jun 30, 2022
1 parent ea74655 commit b751dd0
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 32 deletions.
7 changes: 6 additions & 1 deletion Source/WTF/wtf/PlatformHave.h
Expand Up @@ -1256,9 +1256,14 @@
#define HAVE_VK_IMAGE_TRANSLATION_SUPPORT 1
#endif

#if !defined(HAVE_UIKIT_RESIZABLE_WINDOWS) && PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 160000
#if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 160000
#if !defined(HAVE_UIKIT_RESIZABLE_WINDOWS)
#define HAVE_UIKIT_RESIZABLE_WINDOWS 1
#endif
#if !defined(HAVE_UI_WINDOW_SCENE_GEOMETRY_PREFERENCES)
#define HAVE_UI_WINDOW_SCENE_GEOMETRY_PREFERENCES 1
#endif
#endif

#if !defined(HAVE_AUDIO_COMPONENT_SERVER_REGISTRATIONS) && PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000
#define HAVE_AUDIO_COMPONENT_SERVER_REGISTRATIONS 1
Expand Down
35 changes: 33 additions & 2 deletions Tools/WebKitTestRunner/ios/TestControllerIOS.mm
Expand Up @@ -159,17 +159,48 @@ static _WKDragInteractionPolicy dragInteractionPolicy(const TestOptions& options
return _WKDragInteractionPolicyDefault;
}

static void restorePortraitOrientationIfNeeded(PlatformWebView* platformWebView, Seconds timeoutDuration)
{
#if HAVE(UI_WINDOW_SCENE_GEOMETRY_PREFERENCES)
if (!platformWebView)
return;

auto *scene = platformWebView->platformView().window.windowScene;
if (scene.effectiveGeometry.interfaceOrientation == UIInterfaceOrientationPortrait)
return;

auto geometryPreferences = adoptNS([[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:UIInterfaceOrientationMaskPortrait]);
[scene requestGeometryUpdateWithPreferences:geometryPreferences.get() errorHandler:^(NSError *error) {
NSLog(@"Failed to restore portrait orientation with error: %@.", error);
}];

auto startTime = MonotonicTime::now();
while ([NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantPast]) {
if (scene.effectiveGeometry.interfaceOrientation == UIInterfaceOrientationPortrait)
break;

if (MonotonicTime::now() - startTime >= timeoutDuration)
break;
}
#else
UNUSED_PARAM(platformWebView);
UNUSED_PARAM(timeoutDuration);
[[UIDevice currentDevice] setOrientation:UIDeviceOrientationPortrait animated:NO];
#endif
}

bool TestController::platformResetStateToConsistentValues(const TestOptions& options)
{
cocoaResetStateToConsistentValues(options);

[UIKeyboardImpl.activeInstance setCorrectionLearningAllowed:NO];
[pasteboardConsistencyEnforcer() clearPasteboard];
[[UIApplication sharedApplication] _cancelAllTouches];
[[UIDevice currentDevice] setOrientation:UIDeviceOrientationPortrait animated:NO];
[[UIScreen mainScreen] _setScale:2.0];
[[HIDEventGenerator sharedHIDEventGenerator] resetActiveModifiers];

restorePortraitOrientationIfNeeded(mainWebView(), m_currentInvocation->shortTimeout());

// Ensures that only the UCB is on-screen when showing the keyboard, if the hardware keyboard is attached.
TIPreferencesController *textInputPreferences = [getTIPreferencesControllerClass() sharedPreferencesController];
if (!textInputPreferences.automaticMinimizationEnabled)
Expand Down Expand Up @@ -268,7 +299,7 @@ static _WKDragInteractionPolicy dragInteractionPolicy(const TestOptions& options
UIViewController *webViewController = [[webView window] rootViewController];

MonotonicTime waitEndTime = MonotonicTime::now() + m_currentInvocation->shortTimeout();

bool hasPresentedViewController = !![webViewController presentedViewController];
while (hasPresentedViewController && MonotonicTime::now() < waitEndTime) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
Expand Down
2 changes: 2 additions & 0 deletions Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h
Expand Up @@ -176,6 +176,8 @@ class UIScriptControllerIOS final : public UIScriptControllerCocoa {
JSObjectRef toObject(CGRect) const;

bool isWebContentFirstResponder() const override;

void simulateRotation(DeviceOrientation, JSValueRef callback);
};

}
Expand Down
90 changes: 61 additions & 29 deletions Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm
Expand Up @@ -901,12 +901,56 @@ static void clipSelectionViewRectToContentView(CGRect& rect, UIView *contentView
return [webView() _mayContainEditableElementsInRect:[webView() convertRect:contentRect fromView:platformContentView()]];
}

static UIDeviceOrientation toUIDeviceOrientation(DeviceOrientation* orientation)
void UIScriptControllerIOS::simulateRotation(DeviceOrientation* orientation, JSValueRef callback)
{
if (!orientation)
return UIDeviceOrientationPortrait;

switch (*orientation) {
if (!orientation) {
ASSERT_NOT_REACHED();
return;
}

webView().usesSafariLikeRotation = NO;
simulateRotation(*orientation, callback);
}

void UIScriptControllerIOS::simulateRotationLikeSafari(DeviceOrientation* orientation, JSValueRef callback)
{
if (!orientation) {
ASSERT_NOT_REACHED();
return;
}

webView().usesSafariLikeRotation = YES;
simulateRotation(*orientation, callback);
}

#if HAVE(UI_WINDOW_SCENE_GEOMETRY_PREFERENCES)

static RetainPtr<UIWindowSceneGeometryPreferences> toWindowSceneGeometryPreferences(DeviceOrientation orientation)
{
UIInterfaceOrientationMask orientations = 0;
switch (orientation) {
case DeviceOrientation::Portrait:
orientations = UIInterfaceOrientationMaskPortrait;
break;
case DeviceOrientation::PortraitUpsideDown:
orientations = UIInterfaceOrientationMaskPortraitUpsideDown;
break;
case DeviceOrientation::LandscapeLeft:
orientations = UIInterfaceOrientationMaskLandscapeLeft;
break;
case DeviceOrientation::LandscapeRight:
orientations = UIInterfaceOrientationMaskLandscapeRight;
break;
}
ASSERT(orientation);
return adoptNS([[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:orientations]);
}

#else

static UIDeviceOrientation toUIDeviceOrientation(DeviceOrientation orientation)
{
switch (orientation) {
case DeviceOrientation::Portrait:
return UIDeviceOrientationPortrait;
case DeviceOrientation::PortraitUpsideDown:
Expand All @@ -916,40 +960,28 @@ static UIDeviceOrientation toUIDeviceOrientation(DeviceOrientation* orientation)
case DeviceOrientation::LandscapeRight:
return UIDeviceOrientationLandscapeRight;
}

ASSERT_NOT_REACHED();
return UIDeviceOrientationPortrait;
}

void UIScriptControllerIOS::simulateRotation(DeviceOrientation* orientation, JSValueRef callback)
{
TestRunnerWKWebView *webView = this->webView();
webView.usesSafariLikeRotation = NO;

unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);

webView.rotationDidEndCallback = makeBlockPtr([this, strongThis = Ref { *this }, callbackID] {
if (!m_context)
return;
m_context->asyncTaskComplete(callbackID);
}).get();

[[UIDevice currentDevice] setOrientation:toUIDeviceOrientation(orientation) animated:YES];
}
#endif // HAVE(UI_WINDOW_SCENE_GEOMETRY_PREFERENCES)

void UIScriptControllerIOS::simulateRotationLikeSafari(DeviceOrientation* orientation, JSValueRef callback)
void UIScriptControllerIOS::simulateRotation(DeviceOrientation orientation, JSValueRef callback)
{
TestRunnerWKWebView *webView = this->webView();
webView.usesSafariLikeRotation = YES;

unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);

webView.rotationDidEndCallback = makeBlockPtr([this, strongThis = Ref { *this }, callbackID] {
auto callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
webView().rotationDidEndCallback = makeBlockPtr([this, strongThis = Ref { *this }, callbackID] {
if (!m_context)
return;
m_context->asyncTaskComplete(callbackID);
}).get();


#if HAVE(UI_WINDOW_SCENE_GEOMETRY_PREFERENCES)
[webView().window.windowScene requestGeometryUpdateWithPreferences:toWindowSceneGeometryPreferences(orientation).get() errorHandler:^(NSError *error) {
NSLog(@"Failed to simulate rotation with error: %@", error);
}];
#else
[[UIDevice currentDevice] setOrientation:toUIDeviceOrientation(orientation) animated:YES];
#endif
}

void UIScriptControllerIOS::setDidStartFormControlInteractionCallback(JSValueRef callback)
Expand Down

0 comments on commit b751dd0

Please sign in to comment.