Frequent animation lags when interacting with Safari (sidebar, tab switching, etc.)
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Jul 2016 21:19:06 +0000 (21:19 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Jul 2016 21:19:06 +0000 (21:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160289
<rdar://problem/27553464>

Reviewed by Simon Fraser.

API Tests: WebKit2.AnimatedResizeDoesNotHang, WebKit2.ResizeWithHiddenContentDoesNotHang

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:]):
Avoid calling _endAnimatedResize when a commit comes in when we haven't yet received
a dynamic viewport update reply (and thus don't have a transaction ID to wait on).
Previously, in this case, _resizeAnimationTransformTransactionID would be 0,
and *any* commit would cause _endAnimatedResize to be called, causing us to always
fall into the worst-case sync wait.

* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::synchronizeDynamicViewportUpdate):
Make use of waitForDidUpdateViewState instead of having a separate waitForAndDispatchImmediately
here, because it knows to do things like dispatch the didUpdate message if it's still
pending. This also shortens the sync wait timeout from 1s to 500ms, which makes it
consistent with e.g. the newly-parented-view timeout duration, and should be nothing
but positive.

* Platform/IPC/Connection.cpp:
(IPC::Connection::timeoutRespectingIgnoreTimeoutsForTesting):
(IPC::Connection::waitForMessage):
(IPC::Connection::sendSyncMessageFromSecondaryThread):
(IPC::Connection::waitForSyncReply):
* Platform/IPC/Connection.h:
(IPC::Connection::ignoreTimeoutsForTesting):
* UIProcess/API/APIProcessPoolConfiguration.cpp:
(API::ProcessPoolConfiguration::copy):
* UIProcess/API/APIProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
(-[_WKProcessPoolConfiguration ignoreSynchronousMessagingTimeoutsForTesting]):
(-[_WKProcessPoolConfiguration setIgnoreSynchronousMessagingTimeoutsForTesting:]):
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::processDidFinishLaunching):
Add a mechanism for clients to cause all synchronous message timeouts to be effectively infinite.
The new API tests use this to ensure that the test will time out if they ever enter
the bad state (otherwise, the shorter sync wait timeout could make the test still pass).

* TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit2Cocoa/AnimatedResize.mm: Added.
(-[AnimatedResizeNavigationDelegate _webView:renderingProgressDidChange:]):
(-[AnimatedResizeWebView _endAnimatedResize]):
(animatedResizeWebView):
(TEST):
* TestWebKitAPI/Tests/WebKit2Cocoa/blinking-div.html: Added.
Add two tests that ensure that hidden-content and animated resizes
don't cause unnecessary synchronous waits. Only the hidden-content
one fails before my patch, but it seemed reasonable to add both anyway.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@203842 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebKit2/ChangeLog
Source/WebKit2/Platform/IPC/Connection.cpp
Source/WebKit2/Platform/IPC/Connection.h
Source/WebKit2/UIProcess/API/APIProcessPoolConfiguration.cpp
Source/WebKit2/UIProcess/API/APIProcessPoolConfiguration.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h
Source/WebKit2/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm
Source/WebKit2/UIProcess/WebProcessPool.cpp
Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/AnimatedResize.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/blinking-div.html [new file with mode: 0644]

index 764b5a5..2708710 100644 (file)
@@ -1,3 +1,49 @@
+2016-07-28  Tim Horton  <timothy_horton@apple.com>
+
+        Frequent animation lags when interacting with Safari (sidebar, tab switching, etc.)
+        https://bugs.webkit.org/show_bug.cgi?id=160289
+        <rdar://problem/27553464>
+
+        Reviewed by Simon Fraser.
+
+        API Tests: WebKit2.AnimatedResizeDoesNotHang, WebKit2.ResizeWithHiddenContentDoesNotHang
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:]):
+        Avoid calling _endAnimatedResize when a commit comes in when we haven't yet received
+        a dynamic viewport update reply (and thus don't have a transaction ID to wait on).
+        Previously, in this case, _resizeAnimationTransformTransactionID would be 0,
+        and *any* commit would cause _endAnimatedResize to be called, causing us to always
+        fall into the worst-case sync wait.
+        
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::synchronizeDynamicViewportUpdate):
+        Make use of waitForDidUpdateViewState instead of having a separate waitForAndDispatchImmediately
+        here, because it knows to do things like dispatch the didUpdate message if it's still
+        pending. This also shortens the sync wait timeout from 1s to 500ms, which makes it
+        consistent with e.g. the newly-parented-view timeout duration, and should be nothing
+        but positive.
+
+        * Platform/IPC/Connection.cpp:
+        (IPC::Connection::timeoutRespectingIgnoreTimeoutsForTesting):
+        (IPC::Connection::waitForMessage):
+        (IPC::Connection::sendSyncMessageFromSecondaryThread):
+        (IPC::Connection::waitForSyncReply):
+        * Platform/IPC/Connection.h:
+        (IPC::Connection::ignoreTimeoutsForTesting):
+        * UIProcess/API/APIProcessPoolConfiguration.cpp:
+        (API::ProcessPoolConfiguration::copy):
+        * UIProcess/API/APIProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
+        (-[_WKProcessPoolConfiguration ignoreSynchronousMessagingTimeoutsForTesting]):
+        (-[_WKProcessPoolConfiguration setIgnoreSynchronousMessagingTimeoutsForTesting:]):
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::processDidFinishLaunching):
+        Add a mechanism for clients to cause all synchronous message timeouts to be effectively infinite.
+        The new API tests use this to ensure that the test will time out if they ever enter
+        the bad state (otherwise, the shorter sync wait timeout could make the test still pass).
+
 2016-07-27  Andy Estes  <aestes@apple.com>
 
         [iOS] Add WKUIDelegate SPI for specifying that an attachment list is from a managed source
index 2d7328b..262d5b7 100644 (file)
@@ -385,10 +385,17 @@ bool Connection::sendSyncReply(std::unique_ptr<MessageEncoder> encoder)
     return sendMessage(WTFMove(encoder));
 }
 
+std::chrono::milliseconds Connection::timeoutRespectingIgnoreTimeoutsForTesting(std::chrono::milliseconds timeout) const
+{
+    return m_ignoreTimeoutsForTesting ? std::chrono::milliseconds::max() : timeout;
+}
+
 std::unique_ptr<MessageDecoder> Connection::waitForMessage(StringReference messageReceiverName, StringReference messageName, uint64_t destinationID, std::chrono::milliseconds timeout, unsigned waitForMessageFlags)
 {
     ASSERT(RunLoop::isMain());
 
+    timeout = timeoutRespectingIgnoreTimeoutsForTesting(timeout);
+
     bool hasIncomingSynchronousMessage = false;
 
     // First, check if this message is already in the incoming messages queue.
@@ -532,6 +539,7 @@ std::unique_ptr<MessageDecoder> Connection::sendSyncMessageFromSecondaryThread(u
 
     sendMessage(WTFMove(encoder), 0, true);
 
+    timeout = timeoutRespectingIgnoreTimeoutsForTesting(timeout);
     pendingReply.semaphore.wait(currentTime() + (timeout.count() / 1000.0));
 
     // Finally, pop the pending sync reply information.
@@ -546,6 +554,7 @@ std::unique_ptr<MessageDecoder> Connection::sendSyncMessageFromSecondaryThread(u
 
 std::unique_ptr<MessageDecoder> Connection::waitForSyncReply(uint64_t syncRequestID, std::chrono::milliseconds timeout, unsigned syncSendFlags)
 {
+    timeout = timeoutRespectingIgnoreTimeoutsForTesting(timeout);
     double absoluteTime = currentTime() + (timeout.count() / 1000.0);
 
     willSendSyncMessage(syncSendFlags);
index f3fc9b2..93fc64a 100644 (file)
@@ -205,6 +205,8 @@ public:
 
     void allowFullySynchronousModeForTesting() { m_fullySynchronousModeIsAllowedForTesting = true; }
 
+    void ignoreTimeoutsForTesting() { m_ignoreTimeoutsForTesting = true; }
+
 private:
     Connection(Identifier, bool isServer, Client&);
     void platformInitialize(Identifier);
@@ -239,6 +241,8 @@ private:
 
     void willSendSyncMessage(unsigned syncSendFlags);
     void didReceiveSyncReply(unsigned syncSendFlags);
+
+    std::chrono::milliseconds timeoutRespectingIgnoreTimeoutsForTesting(std::chrono::milliseconds) const;
     
     Client* m_client;
     bool m_isServer;
@@ -258,6 +262,7 @@ private:
     unsigned m_inDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount;
     unsigned m_inDispatchMessageMarkedToUseFullySynchronousModeForTesting { 0 };
     bool m_fullySynchronousModeIsAllowedForTesting { false };
+    bool m_ignoreTimeoutsForTesting { false };
     bool m_didReceiveInvalidMessage;
 
     // Incoming messages.
index b582205..1cb0deb 100644 (file)
@@ -109,6 +109,7 @@ Ref<ProcessPoolConfiguration> ProcessPoolConfiguration::copy()
     copy->m_cachePartitionedURLSchemes = this->m_cachePartitionedURLSchemes;
     copy->m_alwaysRevalidatedURLSchemes = this->m_alwaysRevalidatedURLSchemes;
     copy->m_fullySynchronousModeIsAllowedForTesting = this->m_fullySynchronousModeIsAllowedForTesting;
+    copy->m_ignoreSynchronousMessagingTimeoutsForTesting = this->m_ignoreSynchronousMessagingTimeoutsForTesting;
     copy->m_overrideLanguages = this->m_overrideLanguages;
     
     return copy;
index f09d48b..96e5475 100644 (file)
@@ -96,6 +96,9 @@ public:
     bool fullySynchronousModeIsAllowedForTesting() const { return m_fullySynchronousModeIsAllowedForTesting; }
     void setFullySynchronousModeIsAllowedForTesting(bool allowed) { m_fullySynchronousModeIsAllowedForTesting = allowed; }
 
+    bool ignoreSynchronousMessagingTimeoutsForTesting() const { return m_ignoreSynchronousMessagingTimeoutsForTesting; }
+    void setIgnoreSynchronousMessagingTimeoutsForTesting(bool allowed) { m_ignoreSynchronousMessagingTimeoutsForTesting = allowed; }
+
     const Vector<WTF::String>& overrideLanguages() const { return m_overrideLanguages; }
     void setOverrideLanguages(Vector<WTF::String>&& languages) { m_overrideLanguages = WTFMove(languages); }
 
@@ -120,6 +123,7 @@ private:
     Vector<WTF::String> m_cachePartitionedURLSchemes;
     Vector<WTF::String> m_alwaysRevalidatedURLSchemes;
     bool m_fullySynchronousModeIsAllowedForTesting { false };
+    bool m_ignoreSynchronousMessagingTimeoutsForTesting { false };
     Vector<WTF::String> m_overrideLanguages;
 };
 
index 3dc6ef9..a7c9030 100644 (file)
@@ -235,7 +235,7 @@ WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
     uint64_t _firstPaintAfterCommitLoadTransactionID;
     DynamicViewportUpdateMode _dynamicViewportUpdateMode;
     CATransform3D _resizeAnimationTransformAdjustments;
-    uint64_t _resizeAnimationTransformTransactionID;
+    Optional<uint64_t> _resizeAnimationTransformTransactionID;
     RetainPtr<UIView> _resizeAnimationView;
     CGFloat _lastAdjustmentForScroller;
     Optional<CGRect> _frozenVisibleContentRect;
@@ -1236,7 +1236,8 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
         return;
 
     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing) {
-        if (layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID) {
+        if (_resizeAnimationTransformTransactionID && layerTreeTransaction.transactionID() >= _resizeAnimationTransformTransactionID.value()) {
+            _resizeAnimationTransformTransactionID = Nullopt;
             [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
             if (_dynamicViewportUpdateMode == DynamicViewportUpdateMode::ResizingWithDocumentHidden) {
                 [_contentView setHidden:NO];
index 334bc9c..c7b374b 100644 (file)
@@ -37,6 +37,7 @@ WK_CLASS_AVAILABLE(macosx(10.10), ios(8.0))
 @property (nonatomic, copy) NSArray *cachePartitionedURLSchemes;
 @property (nonatomic, copy) NSArray<NSString *> *alwaysRevalidatedURLSchemes WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic) BOOL diskCacheSpeculativeValidationEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic) BOOL ignoreSynchronousMessagingTimeoutsForTesting WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @end
 
index 151107f..299c92f 100644 (file)
     _processPoolConfiguration->setDiskCacheSpeculativeValidationEnabled(enabled);
 }
 
+- (BOOL)ignoreSynchronousMessagingTimeoutsForTesting
+{
+    return _processPoolConfiguration->ignoreSynchronousMessagingTimeoutsForTesting();
+}
+
+- (void)setIgnoreSynchronousMessagingTimeoutsForTesting:(BOOL)ignoreSynchronousMessagingTimeoutsForTesting
+{
+    _processPoolConfiguration->setIgnoreSynchronousMessagingTimeoutsForTesting(ignoreSynchronousMessagingTimeoutsForTesting);
+}
+
 - (NSArray *)cachePartitionedURLSchemes
 {
     auto schemes = _processPoolConfiguration->cachePartitionedURLSchemes();
index fc1f8d6..5d71c0d 100644 (file)
@@ -730,6 +730,9 @@ void WebProcessPool::processDidFinishLaunching(WebProcessProxy* process)
     if (m_configuration->fullySynchronousModeIsAllowedForTesting())
         process->connection()->allowFullySynchronousModeForTesting();
 
+    if (m_configuration->ignoreSynchronousMessagingTimeoutsForTesting())
+        process->connection()->ignoreTimeoutsForTesting();
+
     m_connectionClient.didCreateConnection(this, process->webConnection());
 }
 
index a02b2c7..930ed9f 100644 (file)
@@ -301,9 +301,9 @@ void WebPageProxy::synchronizeDynamicViewportUpdate()
     }
 
     // If m_dynamicViewportSizeUpdateWaitingForTarget is false, we are waiting for the next valid frame with the hope it is the one for the new target.
-    // If m_dynamicViewportSizeUpdateWaitingForTarget is still true, this is a desesperate attempt to get the valid frame before finishing the animation.
+    // If m_dynamicViewportSizeUpdateWaitingForTarget is still true, this is a desperate attempt to get the valid frame before finishing the animation.
     if (m_dynamicViewportSizeUpdateWaitingForLayerTreeCommit)
-        m_process->connection()->waitForAndDispatchImmediately<Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree>(m_pageID, std::chrono::seconds(1), IPC::InterruptWaitingIfSyncMessageArrives);
+        m_drawingArea->waitForDidUpdateViewState();
 
     m_dynamicViewportSizeUpdateWaitingForTarget = false;
     m_dynamicViewportSizeUpdateWaitingForLayerTreeCommit = false;
index 85d950d..cca4cd4 100644 (file)
@@ -1,3 +1,23 @@
+2016-07-28  Tim Horton  <timothy_horton@apple.com>
+
+        Frequent animation lags when interacting with Safari (sidebar, tab switching, etc.)
+        https://bugs.webkit.org/show_bug.cgi?id=160289
+        <rdar://problem/27553464>
+
+        Reviewed by Simon Fraser.
+
+        * TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/AnimatedResize.mm: Added.
+        (-[AnimatedResizeNavigationDelegate _webView:renderingProgressDidChange:]):
+        (-[AnimatedResizeWebView _endAnimatedResize]):
+        (animatedResizeWebView):
+        (TEST):
+        * TestWebKitAPI/Tests/WebKit2Cocoa/blinking-div.html: Added.
+        Add two tests that ensure that hidden-content and animated resizes
+        don't cause unnecessary synchronous waits. Only the hidden-content
+        one fails before my patch, but it seemed reasonable to add both anyway.
+
 2016-07-28  David Kilzer  <ddkilzer@apple.com>
 
         webkit-patch upload --suggest-reviewers shouldn't break in the presence of deleted or moved files
index 8218a37..ab32d82 100644 (file)
@@ -32,6 +32,6 @@ OTHER_LDFLAGS = $(inherited) $(UNEXPORTED_SYMBOL_LDFLAGS) -lgtest -force_load $(
 OTHER_LDFLAGS_PLATFORM[sdk=macosx*] = -framework Cocoa -framework Carbon;
 
 // FIXME: This should not be built on iOS. Instead we should create and use a TestWebKitAPI application.
-OTHER_LDFLAGS_PLATFORM[sdk=iphone*] = -framework WebCore -framework CoreGraphics;
+OTHER_LDFLAGS_PLATFORM[sdk=iphone*] = -framework WebCore -framework CoreGraphics -framework UIKit;
 
 LD_RUNPATH_SEARCH_PATHS = "@loader_path";
index 2e97864..8c186e6 100644 (file)
                2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D51A0C51C8BF00400765C45 /* DOMHTMLVideoElementWrapper.mm */; };
                2DC4CF771D2D9DD800ECCC94 /* DataDetection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DC4CF761D2D9DD800ECCC94 /* DataDetection.mm */; };
                2DD7D3AF178227B30026E1E3 /* lots-of-text-vertical-lr.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2DD7D3AE178227AC0026E1E3 /* lots-of-text-vertical-lr.html */; };
+               2DE71AFE1D49C0BD00904094 /* AnimatedResize.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DE71AFD1D49C0BD00904094 /* AnimatedResize.mm */; };
+               2DE71B001D49C3ED00904094 /* blinking-div.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2DE71AFF1D49C2F000904094 /* blinking-div.html */; };
                2E14A5291D3FE96B0010F35B /* autoplaying-video-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E14A5281D3FE8B80010F35B /* autoplaying-video-with-audio.html */; };
                2E1B7B001D41ABA7007558B4 /* large-video-seek-after-ending.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1B7AFF1D41A95F007558B4 /* large-video-seek-after-ending.html */; };
                2E1B7B021D41B1B9007558B4 /* large-video-hides-controls-after-seek-to-end.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1B7B011D41B1B3007558B4 /* large-video-hides-controls-after-seek-to-end.html */; };
-               2E1DFDF11D42E1E400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */; };
                2E1DFDED1D42A51100714A00 /* large-videos-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDEC1D42A41C00714A00 /* large-videos-with-audio.html */; };
                2E1DFDEF1D42A6F200714A00 /* large-videos-with-audio-autoplay.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDEE1D42A6EB00714A00 /* large-videos-with-audio-autoplay.html */; };
+               2E1DFDF11D42E1E400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */; };
                2E7765CD16C4D80A00BA2BB1 /* mainIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */; };
                2E7765CF16C4D81100BA2BB1 /* mainMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CE16C4D81100BA2BB1 /* mainMac.mm */; };
                33BE5AF9137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33BE5AF8137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp */; };
                                2E1DFDEF1D42A6F200714A00 /* large-videos-with-audio-autoplay.html in Copy Resources */,
                                2E1DFDED1D42A51100714A00 /* large-videos-with-audio.html in Copy Resources */,
                                5C9E59411D3EB5AC00E3C62E /* ApplicationCache.db in Copy Resources */,
+                               2DE71B001D49C3ED00904094 /* blinking-div.html in Copy Resources */,
                                5C9E59421D3EB5AC00E3C62E /* ApplicationCache.db-shm in Copy Resources */,
                                5C9E59431D3EB5AC00E3C62E /* ApplicationCache.db-wal in Copy Resources */,
                                2E14A5291D3FE96B0010F35B /* autoplaying-video-with-audio.html in Copy Resources */,
                2DD355351BD08378005DF4A7 /* AutoLayoutIntegration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoLayoutIntegration.mm; sourceTree = "<group>"; };
                2DD7D3A9178205D00026E1E3 /* ResizeReversePaginatedWebView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResizeReversePaginatedWebView.cpp; sourceTree = "<group>"; };
                2DD7D3AE178227AC0026E1E3 /* lots-of-text-vertical-lr.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "lots-of-text-vertical-lr.html"; sourceTree = "<group>"; };
+               2DE71AFD1D49C0BD00904094 /* AnimatedResize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AnimatedResize.mm; sourceTree = "<group>"; };
+               2DE71AFF1D49C2F000904094 /* blinking-div.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "blinking-div.html"; sourceTree = "<group>"; };
                2E14A5281D3FE8B80010F35B /* autoplaying-video-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "autoplaying-video-with-audio.html"; sourceTree = "<group>"; };
                2E1B7AFF1D41A95F007558B4 /* large-video-seek-after-ending.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-seek-after-ending.html"; sourceTree = "<group>"; };
                2E1B7B011D41B1B3007558B4 /* large-video-hides-controls-after-seek-to-end.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-hides-controls-after-seek-to-end.html"; sourceTree = "<group>"; };
-               2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-seek-to-beginning-and-play-after-ending.html"; sourceTree = "<group>"; };
                2E1DFDEC1D42A41C00714A00 /* large-videos-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-with-audio.html"; sourceTree = "<group>"; };
                2E1DFDEE1D42A6EB00714A00 /* large-videos-with-audio-autoplay.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-with-audio-autoplay.html"; sourceTree = "<group>"; };
+               2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-seek-to-beginning-and-play-after-ending.html"; sourceTree = "<group>"; };
                2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainIOS.mm; sourceTree = "<group>"; };
                2E7765CE16C4D81100BA2BB1 /* mainMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainMac.mm; sourceTree = "<group>"; };
                333B9CE11277F23100FEFCE3 /* PreventEmptyUserAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PreventEmptyUserAgent.cpp; sourceTree = "<group>"; };
                                A16F66B81C40E9E100BD4D24 /* Resources */,
                                7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */,
                                A1DF74301C41B65800A2F4D0 /* AlwaysRevalidatedURLSchemes.mm */,
+                               2DE71AFD1D49C0BD00904094 /* AnimatedResize.mm */,
                                2DD355351BD08378005DF4A7 /* AutoLayoutIntegration.mm */,
                                A13EBBAC1B87436F00097110 /* BundleParameters.mm */,
                                A13EBBAE1B87436F00097110 /* BundleParametersPlugIn.mm */,
                                5C9E59401D3EB1DE00E3C62E /* ApplicationCache.db-wal */,
                                2E14A5281D3FE8B80010F35B /* autoplaying-video-with-audio.html */,
                                93CFA8661CEB9DE1000565A8 /* autofocused-text-input.html */,
+                               2DE71AFF1D49C2F000904094 /* blinking-div.html */,
                                A16F66B91C40EA2000BD4D24 /* ContentFiltering.html */,
                                5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */,
                                5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */,
                                7CCE7F241A411AF600447C4C /* Navigation.mm in Sources */,
                                A14FC5881B8991BF00D107EB /* ContentFiltering.mm in Sources */,
                                7CCE7F021A411AE600447C4C /* NewFirstVisuallyNonEmptyLayout.cpp in Sources */,
+                               2DE71AFE1D49C0BD00904094 /* AnimatedResize.mm in Sources */,
                                51A587861D273AA9004BA9AF /* IndexedDBDatabaseProcessKill.mm in Sources */,
                                7CCE7F031A411AE600447C4C /* NewFirstVisuallyNonEmptyLayoutFails.cpp in Sources */,
                                7CCE7F041A411AE600447C4C /* NewFirstVisuallyNonEmptyLayoutForImages.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/AnimatedResize.mm b/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/AnimatedResize.mm
new file mode 100644 (file)
index 0000000..b45b0e6
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "config.h"
+
+#import "PlatformUtilities.h"
+#import <WebKit/WKNavigationDelegatePrivate.h>
+#import <WebKit/WKPreferences.h>
+#import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKWebView.h>
+#import <WebKit/WKWebViewConfiguration.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/_WKProcessPoolConfiguration.h>
+#import <wtf/RetainPtr.h>
+
+#if WK_API_ENABLED && PLATFORM(IOS)
+
+static bool didLayout;
+static bool didEndAnimatedResize;
+
+@interface AnimatedResizeNavigationDelegate : NSObject <WKNavigationDelegate>
+@end
+
+@implementation AnimatedResizeNavigationDelegate
+
+- (void)_webView:(WKWebView *)webView renderingProgressDidChange:(_WKRenderingProgressEvents)progressEvents
+{
+    if (progressEvents & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
+        didLayout = true;
+}
+
+@end
+
+@interface AnimatedResizeWebView : WKWebView
+
+@end
+
+@implementation AnimatedResizeWebView
+
+- (void)_endAnimatedResize
+{
+    [super _endAnimatedResize];
+
+    didEndAnimatedResize = true;
+}
+
+@end
+
+static RetainPtr<WKWebView> animatedResizeWebView()
+{
+    RetainPtr<_WKProcessPoolConfiguration> processPoolConfiguration = [[_WKProcessPoolConfiguration alloc] init];
+    [processPoolConfiguration setIgnoreSynchronousMessagingTimeoutsForTesting:YES];
+    RetainPtr<WKProcessPool> processPool = [[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()];
+
+    RetainPtr<WKWebViewConfiguration> webViewConfiguration = [[WKWebViewConfiguration alloc] init];
+    [webViewConfiguration setProcessPool:processPool.get()];
+
+    RetainPtr<WKWebView> webView = adoptNS([[AnimatedResizeWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+
+    AnimatedResizeNavigationDelegate *navigationDelegate = [[AnimatedResizeNavigationDelegate alloc] init];
+    [webView setNavigationDelegate:navigationDelegate];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"blinking-div" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
+    [webView loadRequest:request];
+
+    return webView;
+}
+
+TEST(WebKit2, ResizeWithHiddenContentDoesNotHang)
+{
+    auto webView = animatedResizeWebView();
+    RetainPtr<UIWindow> window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
+    [window addSubview:webView.get()];
+    [window setHidden:NO];
+
+    TestWebKitAPI::Util::run(&didLayout);
+    didLayout = false;
+
+    for (unsigned i = 0; i < 50; i++) {
+        [webView _resizeWhileHidingContentWithUpdates:^{
+            [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
+        }];
+
+        TestWebKitAPI::Util::run(&didEndAnimatedResize);
+        didEndAnimatedResize = false;
+    }
+}
+
+TEST(WebKit2, AnimatedResizeDoesNotHang)
+{
+    auto webView = animatedResizeWebView();
+    RetainPtr<UIWindow> window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
+    [window addSubview:webView.get()];
+    [window setHidden:NO];
+
+    TestWebKitAPI::Util::run(&didLayout);
+    didLayout = false;
+
+    for (unsigned i = 0; i < 50; i++) {
+        [webView _beginAnimatedResizeWithUpdates:^{
+            [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
+        }];
+
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [webView _endAnimatedResize];
+        });
+
+        TestWebKitAPI::Util::run(&didEndAnimatedResize);
+        didEndAnimatedResize = false;
+    }
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/blinking-div.html b/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/blinking-div.html
new file mode 100644 (file)
index 0000000..e0f27aa
--- /dev/null
@@ -0,0 +1,23 @@
+<html>
+    <script>
+window.onload = function () {
+    setInterval(blink, 0);
+}
+
+var a = 0;
+
+function blink() {
+    document.getElementById("blink").style.backgroundColor = a ? "blue" : "green";
+    a = !a;
+}
+    </script>
+    <style>
+#blink {
+    width: 200px; height: 200px;
+    display: inline-block;
+}
+    </style>
+    <body>
+        <div id="blink"></div>
+    </body>
+</html>