Title: [279245] trunk/Source/WebKit
Revision
279245
Author
wenson_hs...@apple.com
Date
2021-06-24 14:41:25 -0700 (Thu, 24 Jun 2021)

Log Message

[watchOS] Automatically apply system minimum layout margins as scroll view content insets
https://bugs.webkit.org/show_bug.cgi?id=227178
rdar://76784095

Reviewed by Tim Horton.

Respect `-[UIViewController systemMinimumLayoutMargins]` on watchOS by treating them in a similar way as safe
area insets, such that we avoid laying out parts of the web page within these margins unless the page's meta
viewport specifies `viewport-fit=cover`. On watch, avoiding these layout margins is crucial.

* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _computedObscuredInsetForSafeBrowsingWarning]):
(-[WKWebView _contentInsetsFromSystemMinimumLayoutMargins]):

Add a helper method to compute the amount of additional content inset we need to apply to the scroll view in
order for content to fit within WKWebView's view controller's `-systemMinimumLayoutMargins`. Note that this
accounts for cases where the web view's frame is already inset relative to the view controller's content view.

(-[WKWebView _computedObscuredInset]):
(-[WKWebView _computedContentInset]):

Unconditionalize a few codepaths that apply obscured and content insets by consulting
`-_scrollViewSystemContentInset`.

(-[WKWebView _computedUnobscuredSafeAreaInset]):

On watchOS, we augment safe area insets, such that they include `_contentInsetsFromSystemMinimumLayoutMargins`
as well.

(-[WKWebView activeViewLayoutSize:]):
(-[WKWebView _updateScrollViewContentInsetsIfNecessary]):

Add a helper method that (on watchOS only) updates WKScrollView's content scroll insets such that the web page
fits inside `-systemMinimumLayoutMargins`. See WKScrollView changes below for more details.

(-[WKWebView _updateVisibleContentRects]):
(-[WKWebView _setAvoidsUnsafeArea:]):

Update scroll view content insets on watchOS if the `viewport-fit` state changes. Additionally make sure that we
also update the active layout size if the insets actually change (without this tweak, when dynamically adding
`viewport-fit=cover`, we'll end up in a state where the content size is still narrow to account for the old
content scroll insets, but the new content scroll insets are set, so the web page apears misaligned relative to
the scroll view).

* UIProcess/ios/WKScrollView.h:
* UIProcess/ios/WKScrollView.mm:
(-[WKScrollView _setContentScrollInset:]):
(-[WKScrollView _setContentScrollInsetInternal:]):

Add an internal method for setting `-[UIScrollView _contentScrollInset]` that defers to the embedding client.
This means WKWebView clients that use `webView.scrollView.contentScrollInset = myInset;` will override the above
behavior in `-_updateScrollViewContentInsetsIfNecessary`, but otherwise, the content scroll insets will be
automatically computed and set in order to avoid minimum layout margins if needed.

Note that this also returns a `BOOL` indicating whether the inset was updated.

(-[WKScrollView _updateContentScrollInset]):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (279244 => 279245)


--- trunk/Source/WebKit/ChangeLog	2021-06-24 21:38:52 UTC (rev 279244)
+++ trunk/Source/WebKit/ChangeLog	2021-06-24 21:41:25 UTC (rev 279245)
@@ -1,3 +1,63 @@
+2021-06-24  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [watchOS] Automatically apply system minimum layout margins as scroll view content insets
+        https://bugs.webkit.org/show_bug.cgi?id=227178
+        rdar://76784095
+
+        Reviewed by Tim Horton.
+
+        Respect `-[UIViewController systemMinimumLayoutMargins]` on watchOS by treating them in a similar way as safe
+        area insets, such that we avoid laying out parts of the web page within these margins unless the page's meta
+        viewport specifies `viewport-fit=cover`. On watch, avoiding these layout margins is crucial.
+
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView _computedObscuredInsetForSafeBrowsingWarning]):
+        (-[WKWebView _contentInsetsFromSystemMinimumLayoutMargins]):
+
+        Add a helper method to compute the amount of additional content inset we need to apply to the scroll view in
+        order for content to fit within WKWebView's view controller's `-systemMinimumLayoutMargins`. Note that this
+        accounts for cases where the web view's frame is already inset relative to the view controller's content view.
+
+        (-[WKWebView _computedObscuredInset]):
+        (-[WKWebView _computedContentInset]):
+
+        Unconditionalize a few codepaths that apply obscured and content insets by consulting
+        `-_scrollViewSystemContentInset`.
+
+        (-[WKWebView _computedUnobscuredSafeAreaInset]):
+
+        On watchOS, we augment safe area insets, such that they include `_contentInsetsFromSystemMinimumLayoutMargins`
+        as well.
+
+        (-[WKWebView activeViewLayoutSize:]):
+        (-[WKWebView _updateScrollViewContentInsetsIfNecessary]):
+
+        Add a helper method that (on watchOS only) updates WKScrollView's content scroll insets such that the web page
+        fits inside `-systemMinimumLayoutMargins`. See WKScrollView changes below for more details.
+
+        (-[WKWebView _updateVisibleContentRects]):
+        (-[WKWebView _setAvoidsUnsafeArea:]):
+
+        Update scroll view content insets on watchOS if the `viewport-fit` state changes. Additionally make sure that we
+        also update the active layout size if the insets actually change (without this tweak, when dynamically adding
+        `viewport-fit=cover`, we'll end up in a state where the content size is still narrow to account for the old
+        content scroll insets, but the new content scroll insets are set, so the web page apears misaligned relative to
+        the scroll view).
+
+        * UIProcess/ios/WKScrollView.h:
+        * UIProcess/ios/WKScrollView.mm:
+        (-[WKScrollView _setContentScrollInset:]):
+        (-[WKScrollView _setContentScrollInsetInternal:]):
+
+        Add an internal method for setting `-[UIScrollView _contentScrollInset]` that defers to the embedding client.
+        This means WKWebView clients that use `webView.scrollView.contentScrollInset = myInset;` will override the above
+        behavior in `-_updateScrollViewContentInsetsIfNecessary`, but otherwise, the content scroll insets will be
+        automatically computed and set in order to avoid minimum layout margins if needed.
+
+        Note that this also returns a `BOOL` indicating whether the inset was updated.
+
+        (-[WKScrollView _updateContentScrollInset]):
+
 2021-06-24  Chris Dumez  <cdu...@apple.com>
 
         Improve release logging in WebProcessProxy

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (279244 => 279245)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2021-06-24 21:38:52 UTC (rev 279244)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2021-06-24 21:41:25 UTC (rev 279245)
@@ -580,11 +580,33 @@
     if (_haveSetObscuredInsets)
         return _obscuredInsets;
 
-#if PLATFORM(IOS)
     return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
-#else
+}
+
+- (UIEdgeInsets)_contentInsetsFromSystemMinimumLayoutMargins
+{
+    if (auto controller = [UIViewController _viewControllerForFullScreenPresentationFromView:self]) {
+        auto margins = controller.systemMinimumLayoutMargins;
+        auto insets = UIEdgeInsetsMake(margins.top, margins.leading, margins.bottom, margins.trailing);
+        if (_page && _page->userInterfaceLayoutDirection() == WebCore::UserInterfaceLayoutDirection::RTL)
+            std::swap(insets.left, insets.right);
+
+        if (auto view = controller.viewIfLoaded) {
+            auto adjustInsetEdge = [](CGFloat& insetEdge, CGFloat distanceFromEdge) {
+                insetEdge -= std::max<CGFloat>(0, distanceFromEdge);
+                insetEdge = std::max<CGFloat>(0, insetEdge);
+            };
+
+            auto viewBounds = view.bounds;
+            auto webViewBoundsInView = [self convertRect:self.bounds toView:view];
+            adjustInsetEdge(insets.top, CGRectGetMinY(webViewBoundsInView) - CGRectGetMinY(viewBounds));
+            adjustInsetEdge(insets.left, CGRectGetMinX(webViewBoundsInView) - CGRectGetMinX(viewBounds));
+            adjustInsetEdge(insets.bottom, CGRectGetMaxY(viewBounds) - CGRectGetMaxY(webViewBoundsInView));
+            adjustInsetEdge(insets.right, CGRectGetMaxX(viewBounds) - CGRectGetMaxX(webViewBoundsInView));
+        }
+        return insets;
+    }
     return UIEdgeInsetsZero;
-#endif
 }
 
 - (UIEdgeInsets)_computedObscuredInset
@@ -598,10 +620,8 @@
     if (_haveSetObscuredInsets)
         return _obscuredInsets;
 
-#if PLATFORM(IOS)
     if (self._safeAreaShouldAffectObscuredInsets)
         return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
-#endif
 
     return UIEdgeInsetsZero;
 }
@@ -613,10 +633,8 @@
 
     UIEdgeInsets insets = [_scrollView contentInset];
 
-#if PLATFORM(IOS)
     if (self._safeAreaShouldAffectObscuredInsets)
         insets = UIEdgeInsetsAdd(insets, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
-#endif
 
     return insets;
 }
@@ -626,10 +644,13 @@
     if (_haveSetUnobscuredSafeAreaInsets)
         return _unobscuredSafeAreaInsets;
 
-#if PLATFORM(IOS)
-    if (!self._safeAreaShouldAffectObscuredInsets)
-        return self.safeAreaInsets;
+    if (!self._safeAreaShouldAffectObscuredInsets) {
+        auto safeAreaInsets = self.safeAreaInsets;
+#if PLATFORM(WATCHOS)
+        safeAreaInsets = UIEdgeInsetsAdd(safeAreaInsets, self._contentInsetsFromSystemMinimumLayoutMargins, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
 #endif
+        return safeAreaInsets;
+    }
 
     return UIEdgeInsetsZero;
 }
@@ -1808,12 +1829,7 @@
     if (_viewLayoutSizeOverride)
         return WebCore::FloatSize(_viewLayoutSizeOverride.value());
 
-// FIXME: Likely we can remove this special case for watchOS and tvOS.
-#if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
     return WebCore::FloatSize(UIEdgeInsetsInsetRect(CGRectMake(0, 0, bounds.size.width, bounds.size.height), self._scrollViewSystemContentInset).size);
-#else
-    return WebCore::FloatSize { bounds.size };
-#endif
 }
 
 - (void)_dispatchSetViewLayoutSize:(WebCore::FloatSize)viewLayoutSize
@@ -1844,6 +1860,15 @@
     _lastSentDeviceOrientation = deviceOrientation;
 }
 
+- (BOOL)_updateScrollViewContentInsetsIfNecessary
+{
+#if PLATFORM(WATCHOS)
+    return [_scrollView _setContentScrollInsetInternal:self._safeAreaShouldAffectObscuredInsets ? self._contentInsetsFromSystemMinimumLayoutMargins : UIEdgeInsetsZero];
+#else
+    return NO;
+#endif
+}
+
 - (void)_frameOrBoundsChanged
 {
     CGRect bounds = self.bounds;
@@ -2108,6 +2133,8 @@
     _didDeferUpdateVisibleContentRectsForUnstableScrollView = NO;
     _didDeferUpdateVisibleContentRectsForAnyReason = NO;
 
+    [self _updateScrollViewContentInsetsIfNecessary];
+
     CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
 
     UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedObscuredInset];
@@ -2472,6 +2499,9 @@
 
     _avoidsUnsafeArea = avoidsUnsafeArea;
 
+    if ([self _updateScrollViewContentInsetsIfNecessary] && _dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing && !_viewLayoutSizeOverride)
+        [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
+
     [self _updateScrollViewInsetAdjustmentBehavior];
     [self _scheduleVisibleContentRectUpdate];
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKScrollView.h (279244 => 279245)


--- trunk/Source/WebKit/UIProcess/ios/WKScrollView.h	2021-06-24 21:38:52 UTC (rev 279244)
+++ trunk/Source/WebKit/UIProcess/ios/WKScrollView.h	2021-06-24 21:41:25 UTC (rev 279245)
@@ -36,6 +36,7 @@
 - (void)_setContentSizePreservingContentOffsetDuringRubberband:(CGSize)contentSize;
 - (void)_setScrollEnabledInternal:(BOOL)enabled;
 - (void)_setZoomEnabledInternal:(BOOL)enabled;
+- (BOOL)_setContentScrollInsetInternal:(UIEdgeInsets)insets;
 
 // FIXME: Likely we can remove this special case for watchOS and tvOS.
 #if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)

Modified: trunk/Source/WebKit/UIProcess/ios/WKScrollView.mm (279244 => 279245)


--- trunk/Source/WebKit/UIProcess/ios/WKScrollView.mm	2021-06-24 21:38:52 UTC (rev 279244)
+++ trunk/Source/WebKit/UIProcess/ios/WKScrollView.mm	2021-06-24 21:41:25 UTC (rev 279245)
@@ -136,6 +136,8 @@
     BOOL _scrollEnabledInternal;
     BOOL _zoomEnabledByClient;
     BOOL _zoomEnabledInternal;
+    std::optional<UIEdgeInsets> _contentScrollInsetFromClient;
+    std::optional<UIEdgeInsets> _contentScrollInsetInternal;
 }
 
 - (id)initWithFrame:(CGRect)frame
@@ -423,6 +425,35 @@
 
 #endif // PLATFORM(WATCHOS)
 
+- (void)_setContentScrollInset:(UIEdgeInsets)insets
+{
+    _contentScrollInsetFromClient = insets;
+    [self _updateContentScrollInset];
+}
+
+- (BOOL)_setContentScrollInsetInternal:(UIEdgeInsets)insets
+{
+    if (_contentScrollInsetFromClient)
+        return NO;
+
+    if (_contentScrollInsetInternal && UIEdgeInsetsEqualToEdgeInsets(*_contentScrollInsetInternal, insets))
+        return NO;
+
+    _contentScrollInsetInternal = insets;
+    [self _updateContentScrollInset];
+    return YES;
+}
+
+- (void)_updateContentScrollInset
+{
+    if (auto insets = _contentScrollInsetFromClient)
+        super.contentScrollInset = *insets;
+    else if (auto insets = _contentScrollInsetInternal)
+        super.contentScrollInset = *insets;
+    else
+        ASSERT_NOT_REACHED();
+}
+
 #if HAVE(PEPPER_UI_CORE)
 
 - (void)_configureDigitalCrownScrolling
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to