Skip to content

Commit

Permalink
[iOS] Enable automatic live resize on iPadOS at runtime
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=274410

Reviewed by Abrar Rahman Protyasha.

Enable automatic live resize on iPad, with runtime enablement guarded by a system feature flag.
Additionally, remove usage of two SPIs that currently support automatic live resize behaviors:

• `_UIWindowSceneDidEndLiveResizeNotification`
• `-[UIWindowScene _isInLiveResize]`

...by instead changing how automatic live resize works:

(a) Don't wait for the live resize gesture to end before allowing geometry updates to commence;
    instead, use an arbitrary hysteresis of 500 ms to determine when the viewport dimensions have
    stabilized, before ending live resize.

(b) Use automatic live resize whenever the window itself is changing size; to detect this, keep
    track of the last known size of the window containing the web view, and only trigger live resize
    when this size is changing.

* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebKit/Platform/spi/ios/UIKitSPI.h:
* Source/WebKit/Shared/WebPreferencesDefaultValues.h:
* Source/WebKit/Shared/ios/WebPreferencesDefaultValuesIOS.mm:
(WebKit::defaultAutomaticLiveResizeEnabled):
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h:
* Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView didMoveToWindow]):
(-[WKWebView _beginAutomaticLiveResizeIfNeeded]):

Refactor this so that we debounce the new "end live resize" timer instead of just early returning
when the size changes.

(-[WKWebView _rescheduleEndLiveResizeTimer]):
(-[WKWebView _acquireResizeAssertionForReason:]):

Remove unnecessary runtime staging, since support for this SPI has long shipped.

(-[WKWebView _endLiveResize]):
(-[WKWebView _destroyEndLiveResizeObserver]): Deleted.

Replace this notification observer with an `NSTimer` instead, which is debounced whenever the web
view size changes, and otherwise fires after a fixed delay of half a second.

Canonical link: https://commits.webkit.org/279030@main
  • Loading branch information
whsieh committed May 21, 2024
1 parent a094f92 commit ea884a9
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 32 deletions.
4 changes: 2 additions & 2 deletions Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,10 @@ AutomaticLiveResizeEnabled:
humanReadableDescription: "Automatically synchronize web view resize with painting"
webcoreBinding: none
exposed: [ WebKit ]
condition: PLATFORM(IOS_FAMILY)
defaultValue:
WebKit:
"PLATFORM(VISION)": true
default: false
default: WebKit::defaultAutomaticLiveResizeEnabled()

BackgroundFetchAPIEnabled:
type: bool
Expand Down
4 changes: 0 additions & 4 deletions Source/WebKit/Platform/spi/ios/UIKitSPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -1006,10 +1006,6 @@ typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {

#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)

@interface UIWindowScene ()
@property (nonatomic, readonly, getter=_isInLiveResize) BOOL _inLiveResize;
@end

extern NSNotificationName const _UIWindowSceneDidBeginLiveResizeNotification;
extern NSNotificationName const _UIWindowSceneDidEndLiveResizeNotification;

Expand Down
4 changes: 4 additions & 0 deletions Source/WebKit/Shared/WebPreferencesDefaultValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ bool defaultRemoveBackgroundEnabled();
bool defaultGamepadVibrationActuatorEnabled();
#endif

#if PLATFORM(IOS_FAMILY)
bool defaultAutomaticLiveResizeEnabled();
#endif

bool defaultRunningBoardThrottlingEnabled();
bool defaultShouldDropNearSuspendedAssertionAfterDelay();
bool defaultShouldTakeNearSuspendedAssertion();
Expand Down
12 changes: 12 additions & 0 deletions Source/WebKit/Shared/ios/WebPreferencesDefaultValuesIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ bool defaultWriteRichTextDataWhenCopyingOrDragging()
return !isAsyncTextInputFeatureFlagEnabled();
}

bool defaultAutomaticLiveResizeEnabled()
{
#if PLATFORM(VISION)
return true;
#elif USE(BROWSERENGINEKIT)
static bool enabled = PAL::deviceHasIPadCapability() && os_feature_enabled(UIKit, async_text_input_ipad);
return enabled;
#else
return false;
#endif
}

} // namespace WebKit

#endif // PLATFORM(IOS_FAMILY)
3 changes: 2 additions & 1 deletion Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ struct PerWebProcessState {
RetainPtr<UIView> _resizeAnimationView;
CGFloat _lastAdjustmentForScroller;

RetainPtr<id> _endLiveResizeNotificationObserver;
CGSize _lastKnownWindowSize;
RetainPtr<NSTimer> _endLiveResizeTimer;

WebCore::FloatBoxExtent _obscuredInsetsWhenSaved;

Expand Down
81 changes: 56 additions & 25 deletions Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ - (void)_action:(id)sender \
[_contentView _action ## ForWebView:sender]; \
}

namespace WebKit {
enum class UpdateLastKnownWindowSizeResult : bool { Changed, DidNotChange };
}

#define WKWEBVIEW_RELEASE_LOG(...) RELEASE_LOG(ViewState, __VA_ARGS__)

static const Seconds delayBeforeNoVisibleContentsRectsLogging = 1_s;
Expand Down Expand Up @@ -1882,6 +1886,16 @@ - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoi
return false;
}

- (WebKit::UpdateLastKnownWindowSizeResult)_updateLastKnownWindowSize
{
auto size = self.window.bounds.size;
if (CGSizeEqualToSize(_lastKnownWindowSize, size))
return WebKit::UpdateLastKnownWindowSizeResult::DidNotChange;

_lastKnownWindowSize = size;
return WebKit::UpdateLastKnownWindowSizeResult::Changed;
}

- (void)didMoveToWindow
{
if (!_overridesInterfaceOrientation)
Expand All @@ -1896,9 +1910,9 @@ - (void)didMoveToWindow
[self _invalidateResizeAssertions];
#endif
#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
[self _destroyEndLiveResizeObserver];
[self _endLiveResize];
#endif
[self _updateLastKnownWindowSize];
}

#if HAVE(UIKIT_RESIZABLE_WINDOWS)
Expand Down Expand Up @@ -2358,37 +2372,42 @@ - (BOOL)_updateScrollViewContentInsetsIfNecessary

- (void)_beginAutomaticLiveResizeIfNeeded
{
if (![self usesStandardContentView])
if (!_page)
return;

if (_perProcessState.liveResizeParameters)
if (!_page->preferences().automaticLiveResizeEnabled())
return;

if (![self usesStandardContentView])
return;

if (!self.window)
return;

if (!self.window.windowScene._isInLiveResize)
if (CGRectIsEmpty(self.bounds))
return;

[self _rescheduleEndLiveResizeTimer];

if (_perProcessState.liveResizeParameters)
return;

[self _beginLiveResize];

_endLiveResizeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:_UIWindowSceneDidEndLiveResizeNotification object:self.window.windowScene queue:NSOperationQueue.mainQueue usingBlock:makeBlockPtr([weakSelf = WeakObjCPtr<WKWebView>(self)] (NSNotification *) {
auto strongSelf = weakSelf.get();
if (!strongSelf)
return;

[strongSelf _destroyEndLiveResizeObserver];
[strongSelf _endLiveResize];
}).get()];
}

- (void)_destroyEndLiveResizeObserver
- (void)_rescheduleEndLiveResizeTimer
{
if (!_endLiveResizeNotificationObserver)
return;
[_endLiveResizeTimer invalidate];

constexpr auto endLiveResizeHysteresis = 500_ms;

[[NSNotificationCenter defaultCenter] removeObserver:_endLiveResizeNotificationObserver.get()];
_endLiveResizeNotificationObserver = nil;
_endLiveResizeTimer = [NSTimer
scheduledTimerWithTimeInterval:endLiveResizeHysteresis.seconds()
repeats:NO
block:makeBlockPtr([weakSelf = WeakObjCPtr<WKWebView>(self)](NSTimer *) {
auto strongSelf = weakSelf.get();
[strongSelf _endLiveResize];
}).get()];
}

- (void)_updateLiveResizeTransform
Expand All @@ -2410,7 +2429,7 @@ - (void)_updateLiveResizeTransform
- (void)_frameOrBoundsWillChange
{
#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
if (_page && _page->preferences().automaticLiveResizeEnabled())
if ([self _updateLastKnownWindowSize] == WebKit::UpdateLastKnownWindowSizeResult::Changed)
[self _beginAutomaticLiveResizeIfNeeded];
#endif
}
Expand Down Expand Up @@ -2464,11 +2483,12 @@ - (void)_frameOrBoundsMayHaveChanged

- (void)_acquireResizeAssertionForReason:(NSString *)reason
{
if (_page && _page->preferences().automaticLiveResizeEnabled())
return;

UIWindowScene *windowScene = self.window.windowScene;
if (!windowScene)
return;
if (![windowScene respondsToSelector:@selector(_holdLiveResizeSnapshotForReason:)])
return;

if (_resizeAssertions.isEmpty()) {
auto didInvalidateResizeAssertions = Box<bool>::create(false);
Expand Down Expand Up @@ -3314,19 +3334,30 @@ - (void)_endLiveResize
if (!_perProcessState.liveResizeParameters)
return;

UIView *liveResizeSnapshotView = [self snapshotViewAfterScreenUpdates:NO];
[_endLiveResizeTimer invalidate];
_endLiveResizeTimer = nil;

RetainPtr liveResizeSnapshotView = [self snapshotViewAfterScreenUpdates:NO];
[liveResizeSnapshotView setFrame:self.bounds];
[self addSubview:liveResizeSnapshotView];
[self addSubview:liveResizeSnapshotView.get()];

_perProcessState.liveResizeParameters = std::nullopt;

ASSERT(_perProcessState.dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing);
[self _destroyResizeAnimationView];
[self _didStopDeferringGeometryUpdates];

[self _doAfterNextPresentationUpdate:^{
[self _doAfterNextVisibleContentRectUpdate:makeBlockPtr([liveResizeSnapshotView, weakSelf = WeakObjCPtr<WKWebView>(self)]() mutable {
auto strongSelf = weakSelf.get();
[strongSelf _doAfterNextPresentationUpdate:makeBlockPtr([liveResizeSnapshotView] {
[liveResizeSnapshotView removeFromSuperview];
}).get()];
}).get()];

// Ensure that the live resize snapshot is eventually removed, even if the webpage is unresponsive.
RunLoop::main().dispatchAfter(1_s, [liveResizeSnapshotView] {
[liveResizeSnapshotView removeFromSuperview];
}];
});
}

#endif // HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
Expand Down

0 comments on commit ea884a9

Please sign in to comment.