Skip to content

Commit

Permalink
Resizing a fullscreen element doesn't always resize children to the c…
Browse files Browse the repository at this point in the history
…orrect size

https://bugs.webkit.org/show_bug.cgi?id=258847
<radar://110880116>

Reviewed by Tim Horton.

During a window resize, [_contentView bounds] will not be up to date during the precommit
handler so it is incorrect to intersect it with the web view's bounds.

Additionally it makes fullscreen resizing look much smoother.

* Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _updateVisibleContentRects]):
Skip intersection if we just resized the WKWebView.

* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/ios/WKWebViewResize.mm: Added.
(-[NSObject swizzled_didUpdateVisibleRect:unobscuredRect:contentInsets:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:viewStability:enclosedInScrollableAncestorView:sendEvenIfUnchanged:]):
(setupWKContentViewSwizzle):
(operator==):
Required for EXPECT_EQ on CGRect.

(webViewHasExpectedBounds):
(TEST):
Add API test which ensures a content view with no other insets will have an
unobscuredRect which matches the enclosing WKWebView's bounds after setFrame: is called.

Tests both resizing the web view larger and smaller.

Canonical link: https://commits.webkit.org/266257@main
  • Loading branch information
mwyrzykowski committed Jul 24, 2023
1 parent b532c60 commit d7f28b8
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2547,7 +2547,8 @@ - (void)_updateVisibleContentRects
CGFloat scaleFactor = contentZoomScale(self);
CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, computedContentInsetUnadjustedForKeyboard);
WebCore::FloatRect unobscuredRectInContentCoordinates = WebCore::FloatRect(_perProcessState.frozenUnobscuredContentRect ? _perProcessState.frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()]);
unobscuredRectInContentCoordinates.intersect([self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
if (![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
unobscuredRectInContentCoordinates.intersect([self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);

auto contentInsets = [self currentlyVisibleContentInsetsWithScale:scaleFactor obscuredInsets:computedContentInsetUnadjustedForKeyboard];

Expand Down
4 changes: 4 additions & 0 deletions Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
07E1F6A21FFC44FA0096C7EC /* getDisplayMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 07E1F6A11FFC44F90096C7EC /* getDisplayMedia.html */; };
07E499911F9E56DF002F1EF3 /* GetUserMediaReprompt.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07E499901F9E56A1002F1EF3 /* GetUserMediaReprompt.mm */; };
07FC26E8267A735A002B1BEF /* audio-buffer-size.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1D5BE6AD2673EC5F00CB0B12 /* audio-buffer-size.html */; };
0DE559ED2A6B0AAF009AA320 /* WKWebViewResize.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0DE559E52A6B0AAF009AA320 /* WKWebViewResize.mm */; };
0E404A8C2166DE0A008271BA /* InjectedBundleNodeHandleIsSelectElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0E404A8A2166DDF8008271BA /* InjectedBundleNodeHandleIsSelectElement.mm */; };
0EBBCC661FFF9E0C00FA42AB /* pop-up-check.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0EBBCC651FFF9DCE00FA42AB /* pop-up-check.html */; };
0F139E771A423A5B00F590F5 /* WeakObjCPtr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E751A423A5300F590F5 /* WeakObjCPtr.mm */; };
Expand Down Expand Up @@ -1993,6 +1994,7 @@
07F4E92D20AF58D3002E3803 /* UserMediaSimulateFailedSandbox.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UserMediaSimulateFailedSandbox.mm; sourceTree = "<group>"; };
0BCD833414857CE400EA2003 /* HashMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HashMap.cpp; sourceTree = "<group>"; };
0BCD85691485C98B00EA2003 /* SetForScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SetForScope.cpp; sourceTree = "<group>"; };
0DE559E52A6B0AAF009AA320 /* WKWebViewResize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewResize.mm; sourceTree = "<group>"; };
0E404A8A2166DDF8008271BA /* InjectedBundleNodeHandleIsSelectElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InjectedBundleNodeHandleIsSelectElement.mm; sourceTree = "<group>"; };
0EBBCC651FFF9DCE00FA42AB /* pop-up-check.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "pop-up-check.html"; sourceTree = "<group>"; };
0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PlatformUtilitiesCocoa.mm; path = cocoa/PlatformUtilitiesCocoa.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4489,6 +4491,7 @@
514958BD1F7427AC00E87BAD /* WKWebViewAutofillTests.mm */,
1CACADA0230620AD0007D54C /* WKWebViewOpaque.mm */,
CD27A1C023C661ED006E11DD /* WKWebViewPausePlayingAudioTests.mm */,
0DE559E52A6B0AAF009AA320 /* WKWebViewResize.mm */,
);
path = ios;
sourceTree = "<group>";
Expand Down Expand Up @@ -6715,6 +6718,7 @@
F4FA91811E61849B007B8C1D /* WKWebViewMacEditingTests.mm in Sources */,
1CACADA1230620AE0007D54C /* WKWebViewOpaque.mm in Sources */,
CD27A1C123C661ED006E11DD /* WKWebViewPausePlayingAudioTests.mm in Sources */,
0DE559ED2A6B0AAF009AA320 /* WKWebViewResize.mm in Sources */,
E520A36B25AFB76C00526CB9 /* WKWebViewTitlebarSeparatorTests.mm in Sources */,
3545E58927E2AD1300F1910E /* WritingModeTests.cpp in Sources */,
C14D304624B4C3BA00480387 /* XPCEndpoint.mm in Sources */,
Expand Down
131 changes: 131 additions & 0 deletions Tools/TestWebKitAPI/Tests/ios/WKWebViewResize.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#import "config.h"

#if PLATFORM(IOS_FAMILY)

#import "Test.h"
#import "TestWKWebView.h"
#import <objc/runtime.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/Assertions.h>
#import <wtf/OptionSet.h>

namespace WebKit {
enum class ViewStabilityFlag : uint8_t;
}

namespace {
CGRect _mostRecentUnobscuredRect;
}

@interface NSObject ()

- (void)didUpdateVisibleRect:(CGRect)visibleRect
unobscuredRect:(CGRect)unobscuredRect
contentInsets:(UIEdgeInsets)contentInsets
unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
obscuredInsets:(UIEdgeInsets)obscuredInsets
unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
inputViewBounds:(CGRect)inputViewBounds
scale:(CGFloat)scale minimumScale:(CGFloat)minimumScale
viewStability:(OptionSet<WebKit::ViewStabilityFlag>)viewStability
enclosedInScrollableAncestorView:(BOOL)enclosedInScrollableAncestorView
sendEvenIfUnchanged:(BOOL)sendEvenIfUnchanged;

@end

@implementation NSObject (WKContentViewSwizzleForTesting)

- (void)swizzled_didUpdateVisibleRect:(CGRect)visibleRect
unobscuredRect:(CGRect)unobscuredRect
contentInsets:(UIEdgeInsets)contentInsets
unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
obscuredInsets:(UIEdgeInsets)obscuredInsets
unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
inputViewBounds:(CGRect)inputViewBounds
scale:(CGFloat)scale minimumScale:(CGFloat)minimumScale
viewStability:(OptionSet<WebKit::ViewStabilityFlag>)viewStability
enclosedInScrollableAncestorView:(BOOL)enclosedInScrollableAncestorView
sendEvenIfUnchanged:(BOOL)sendEvenIfUnchanged {
_mostRecentUnobscuredRect = unobscuredRect;
[self swizzled_didUpdateVisibleRect:visibleRect unobscuredRect:unobscuredRect contentInsets:contentInsets unobscuredRectInScrollViewCoordinates:unobscuredRectInScrollViewCoordinates obscuredInsets:obscuredInsets unobscuredSafeAreaInsets:unobscuredSafeAreaInsets inputViewBounds:inputViewBounds scale:scale minimumScale:minimumScale viewStability:viewStability enclosedInScrollableAncestorView:enclosedInScrollableAncestorView sendEvenIfUnchanged:sendEvenIfUnchanged];
}

@end

static void setupWKContentViewSwizzle()
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class1 = NSClassFromString(@"WKContentView");

SEL originalSelector = @selector(didUpdateVisibleRect:unobscuredRect:contentInsets:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:viewStability:enclosedInScrollableAncestorView:sendEvenIfUnchanged:);
SEL swizzledSelector = @selector(swizzled_didUpdateVisibleRect:unobscuredRect:contentInsets:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:viewStability:enclosedInScrollableAncestorView:sendEvenIfUnchanged:);

Method originalMethod = class_getInstanceMethod(class1, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class1, swizzledSelector);

IMP originalImp = method_getImplementation(originalMethod);
IMP swizzledImp = method_getImplementation(swizzledMethod);

class_replaceMethod(class1, swizzledSelector, originalImp, method_getTypeEncoding(originalMethod));
class_replaceMethod(class1, originalSelector, swizzledImp, method_getTypeEncoding(swizzledMethod));
});
}


static bool operator==(const CGRect a, const CGRect b)
{
return CGRectEqualToRect(a, b);
}

static void webViewHasExpectedBounds(TestWKWebView *webView, CGRect expectedBounds)
{
[CATransaction addCommitHandler:[webView, expectedBounds] {
EXPECT_EQ([webView bounds], expectedBounds);
EXPECT_EQ(_mostRecentUnobscuredRect, expectedBounds);

} forPhase:kCATransactionPhasePreCommit];
}

TEST(WKWebView, EnsureUnobscuredContentRectMatchesWebViewBounds)
{
setupWKContentViewSwizzle();

CGRect initialFrame = CGRectMake(0, 0, 800, 600);
CGRect largerFrame = CGRectMake(0, 0, 1200, 800);
CGRect smallerFrame = CGRectMake(0, 0, 400, 300);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:initialFrame]);
[webView setFrame:largerFrame];

webViewHasExpectedBounds(webView.get(), largerFrame);

[webView setFrame:smallerFrame];
webViewHasExpectedBounds(webView.get(), smallerFrame);
}

#endif

0 comments on commit d7f28b8

Please sign in to comment.