Make it possible to test non-stable-state scrolling on iOS
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Nov 2016 19:02:04 +0000 (19:02 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Nov 2016 19:02:04 +0000 (19:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164905

Reviewed by Dan Bernstein.

Source/WebKit2:

Make it possible for an iOS layout test to force every visible rect update
from the UI process to the web process to be in a non-stable state. This emulates
what happens during panning and zooming interactions, and allows us to test the
scrolling and layer trees in these transient states.

This is done by adding a testing property on WKWebView, _stableStateOverride, whose
getters and setters are overridden in TestRunnerWKWebView. When non-nil, we _updateVisibleContentRectAfterScrollInView
consults the NSNumber's boolValue and uses it to override the stable state.

To facilitate this kind of testing, also add immediateScrollToOffset() and
immediateZoomToScale() to UIScriptController. Finally, add doAfterPresentationUpdate()
which provides a callback that fires after one trip to the web process and back.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _updateVisibleContentRectAfterScrollInView:]):
(-[WKWebView _stableStateOverride]):
(-[WKWebView _setStableStateOverride:]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:

Tools:

Make it possible for an iOS layout test to force every visible rect update
from the UI process to the web process to be in a non-stable state. This emulates
what happens during panning and zooming interactions, and allows us to test the
scrolling and layer trees in these transient states.

This is done by adding a testing property on WKWebView, _stableStateOverride, whose
getters and setters are overridden in TestRunnerWKWebView. When non-nil, we _updateVisibleContentRectAfterScrollInView
consults the NSNumber's boolValue and uses it to override the stable state.

To facilitate this kind of testing, also add immediateScrollToOffset() and
immediateZoomToScale() to UIScriptController. Finally, add doAfterPresentationUpdate()
which provides a callback that fires after one trip to the web process and back.

Add code generation support for optional boolean attributes in IDL.

Align CodeGeneratorDumpRenderTree.pm and CodeGeneratorTestRunner.pm.

* DumpRenderTree/Bindings/CodeGeneratorDumpRenderTree.pm:
(_generateImplementationFile):
(_platformTypeConstructor):
(_returnExpression):
* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::doAfterPresentationUpdate):
(WTR::contentOffsetBoundedInValidRange):
(WTR::UIScriptController::scrollToOffset):
(WTR::UIScriptController::immediateScrollToOffset):
(WTR::UIScriptController::immediateZoomToScale):
(WTR::UIScriptController::stableStateOverride):
(WTR::UIScriptController::setStableStateOverride):
* DumpRenderTree/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::doAfterPresentationUpdate):
* TestRunnerShared/Bindings/JSWrappable.h:
(WTR::JSValueMakeBooleanOrNull):
(WTR::JSValueToNullableBoolean):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::immediateScrollToOffset):
(WTR::UIScriptController::immediateZoomToScale):
(WTR::UIScriptController::stableStateOverride):
(WTR::UIScriptController::setStableStateOverride):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm:
(_classRefGetter):
(_platformTypeConstructor):
(_returnExpression):
* WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
(-[TestRunnerWKWebView dealloc]):
(-[TestRunnerWKWebView _stableStateOverride]):
(-[TestRunnerWKWebView _setStableStateOverride:]):
* WebKitTestRunner/ios/TestControllerIOS.mm:
(WTR::TestController::platformResetStateToConsistentValues):
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::doAsyncTask):
(WTR::UIScriptController::doAfterPresentationUpdate):
(WTR::UIScriptController::zoomToScale):
(WTR::UIScriptController::immediateScrollToOffset):
(WTR::UIScriptController::immediateZoomToScale):
(WTR::UIScriptController::stableStateOverride):
(WTR::UIScriptController::setStableStateOverride):
* WebKitTestRunner/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::doAfterPresentationUpdate):

LayoutTests:

* scrollingcoordinator/ios/non-stable-viewport-scroll-expected.txt: Added.
* scrollingcoordinator/ios/non-stable-viewport-scroll.html: Added.

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll-expected.txt [new file with mode: 0644]
LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll.html [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h
Tools/ChangeLog
Tools/DumpRenderTree/Bindings/CodeGeneratorDumpRenderTree.pm
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/DumpRenderTree/mac/UIScriptControllerMac.mm
Tools/TestRunnerShared/Bindings/JSWrappable.h
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm
Tools/WebKitTestRunner/ios/TestControllerIOS.mm
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm
Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm

index ec3c4cd..118b4f0 100644 (file)
@@ -1,3 +1,13 @@
+2016-11-18  Simon Fraser  <simon.fraser@apple.com>
+
+        Make it possible to test non-stable-state scrolling on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=164905
+
+        Reviewed by Dan Bernstein.
+
+        * scrollingcoordinator/ios/non-stable-viewport-scroll-expected.txt: Added.
+        * scrollingcoordinator/ios/non-stable-viewport-scroll.html: Added.
+
 2016-11-19  Joanmarie Diggs  <jdiggs@igalia.com>
 
         AX: [ATK] Expose aria-busy via ATK_STATE_BUSY
diff --git a/LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll-expected.txt b/LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll-expected.txt
new file mode 100644 (file)
index 0000000..acebafe
--- /dev/null
@@ -0,0 +1,33 @@
+fixed
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 320.00 5013.00)
+  (visible rect 0.00, 1000.00 106.67 x 189.33)
+  (coverage rect 0.00, 1000.00 106.67 x 189.33)
+  (intersects coverage rect 1)
+  (contentsScale 6.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 320.00 5013.00)
+      (contentsOpaque 1)
+      (visible rect 0.00, 1000.00 106.67 x 189.33)
+      (coverage rect 0.00, 957.33 192.00 x 274.67)
+      (intersects coverage rect 1)
+      (contentsScale 6.00)
+      (children 1
+        (GraphicsLayer
+          (position 10.00 0.00)
+          (bounds 100.00 568.00)
+          (usingTiledLayer 1)
+          (contentsOpaque 1)
+          (drawsContent 1)
+          (visible rect 0.00, 0.00 100.00 x 568.00)
+          (coverage rect -10.00, 0.00 320.00 x 568.00)
+          (intersects coverage rect 1)
+          (contentsScale 6.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll.html b/LayoutTests/scrollingcoordinator/ios/non-stable-viewport-scroll.html
new file mode 100644 (file)
index 0000000..b25336f
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0">
+    <style>
+        body {
+            height: 5000px;
+            margin: 0;
+        }
+        
+        .fixed {
+            position: fixed;
+            top: 0;
+            left: 10px;
+            height: 100%;
+            width: 100px;
+            background-color: blue;
+        }
+    </style>
+    <script>
+        if (window.testRunner) {
+            testRunner.waitUntilDone();
+            testRunner.dumpAsText();
+        }
+
+        function getUnstableScrollScript(x, y)
+        {
+            return `(function() {
+                uiController.stableStateOverride = false;
+                uiController.immediateZoomToScale(3);
+                uiController.immediateScrollToOffset(0, 3000);
+                
+                uiController.doAfterPresentationUpdate(function() {
+                    uiController.uiScriptComplete();
+                });
+                
+            })();`;
+        }
+
+        function doTest()
+        {
+            if (!testRunner.runUIScript)
+                return
+
+            testRunner.runUIScript(getUnstableScrollScript(), function() {
+                document.getElementById('layers').textContent = internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_VISIBLE_RECTS);
+                testRunner.notifyDone();
+            });
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+<div class="fixed">fixed</div>
+<pre id="layers"></pre>
+</body>
+</html>
index 12a5a28..03b093e 100644 (file)
@@ -1,5 +1,31 @@
 2016-11-18  Simon Fraser  <simon.fraser@apple.com>
 
+        Make it possible to test non-stable-state scrolling on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=164905
+
+        Reviewed by Dan Bernstein.
+
+        Make it possible for an iOS layout test to force every visible rect update
+        from the UI process to the web process to be in a non-stable state. This emulates
+        what happens during panning and zooming interactions, and allows us to test the
+        scrolling and layer trees in these transient states.
+
+        This is done by adding a testing property on WKWebView, _stableStateOverride, whose
+        getters and setters are overridden in TestRunnerWKWebView. When non-nil, we _updateVisibleContentRectAfterScrollInView
+        consults the NSNumber's boolValue and uses it to override the stable state.
+
+        To facilitate this kind of testing, also add immediateScrollToOffset() and
+        immediateZoomToScale() to UIScriptController. Finally, add doAfterPresentationUpdate()
+        which provides a callback that fires after one trip to the web process and back.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _updateVisibleContentRectAfterScrollInView:]):
+        (-[WKWebView _stableStateOverride]):
+        (-[WKWebView _setStableStateOverride:]):
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+
+2016-11-18  Simon Fraser  <simon.fraser@apple.com>
+
         [iOS WK2] Eliminate a source of flakiness in layout tests by forcing WebPage into "responsive" mode for all tests, with an internals override
         https://bugs.webkit.org/show_bug.cgi?id=164980
 
index d74d42c..994584b 100644 (file)
@@ -2071,6 +2071,9 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     if (isStableState && [scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
         isStableState = ![scrollView performSelector:@selector(_isInterruptingDeceleration)];
 
+    if (NSNumber *stableOverride = self._stableStateOverride)
+        isStableState = stableOverride.boolValue;
+
     [self _updateContentRectsWithState:isStableState];
 }
 
@@ -4731,6 +4734,12 @@ static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISe
     return coordinator->scrollingTreeAsText();
 }
 
+- (NSNumber *)_stableStateOverride
+{
+    // For subclasses to override.
+    return nil;
+}
+
 #endif // PLATFORM(IOS)
 
 #if PLATFORM(MAC)
index abec4d0..a4a8b51 100644 (file)
@@ -290,7 +290,9 @@ typedef NS_ENUM(NSInteger, _WKImmediateActionType) {
 
 @property (nonatomic, readonly) NSString *_scrollingTreeAsText WK_API_AVAILABLE(ios(WK_IOS_TBA));
 
-#endif
+@property (nonatomic, readonly) NSNumber *_stableStateOverride WK_API_AVAILABLE(ios(WK_IOS_TBA));
+
+#endif // TARGET_OS_IPHONE
 
 #if !TARGET_OS_IPHONE
 @property (nonatomic, readonly) BOOL _hasActiveVideoForControlsManager WK_API_AVAILABLE(macosx(10.12));
index 61890ac..08c0846 100644 (file)
@@ -1,3 +1,72 @@
+2016-11-18  Simon Fraser  <simon.fraser@apple.com>
+
+        Make it possible to test non-stable-state scrolling on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=164905
+
+        Reviewed by Dan Bernstein.
+
+        Make it possible for an iOS layout test to force every visible rect update
+        from the UI process to the web process to be in a non-stable state. This emulates
+        what happens during panning and zooming interactions, and allows us to test the
+        scrolling and layer trees in these transient states.
+        
+        This is done by adding a testing property on WKWebView, _stableStateOverride, whose
+        getters and setters are overridden in TestRunnerWKWebView. When non-nil, we _updateVisibleContentRectAfterScrollInView
+        consults the NSNumber's boolValue and uses it to override the stable state.
+        
+        To facilitate this kind of testing, also add immediateScrollToOffset() and
+        immediateZoomToScale() to UIScriptController. Finally, add doAfterPresentationUpdate()
+        which provides a callback that fires after one trip to the web process and back.
+        
+        Add code generation support for optional boolean attributes in IDL.
+
+        Align CodeGeneratorDumpRenderTree.pm and CodeGeneratorTestRunner.pm.
+
+        * DumpRenderTree/Bindings/CodeGeneratorDumpRenderTree.pm:
+        (_generateImplementationFile):
+        (_platformTypeConstructor):
+        (_returnExpression):
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::doAfterPresentationUpdate):
+        (WTR::contentOffsetBoundedInValidRange):
+        (WTR::UIScriptController::scrollToOffset):
+        (WTR::UIScriptController::immediateScrollToOffset):
+        (WTR::UIScriptController::immediateZoomToScale):
+        (WTR::UIScriptController::stableStateOverride):
+        (WTR::UIScriptController::setStableStateOverride):
+        * DumpRenderTree/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::doAfterPresentationUpdate):
+        * TestRunnerShared/Bindings/JSWrappable.h:
+        (WTR::JSValueMakeBooleanOrNull):
+        (WTR::JSValueToNullableBoolean):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::immediateScrollToOffset):
+        (WTR::UIScriptController::immediateZoomToScale):
+        (WTR::UIScriptController::stableStateOverride):
+        (WTR::UIScriptController::setStableStateOverride):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm:
+        (_classRefGetter):
+        (_platformTypeConstructor):
+        (_returnExpression):
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+        (-[TestRunnerWKWebView dealloc]):
+        (-[TestRunnerWKWebView _stableStateOverride]):
+        (-[TestRunnerWKWebView _setStableStateOverride:]):
+        * WebKitTestRunner/ios/TestControllerIOS.mm:
+        (WTR::TestController::platformResetStateToConsistentValues):
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::doAsyncTask):
+        (WTR::UIScriptController::doAfterPresentationUpdate):
+        (WTR::UIScriptController::zoomToScale):
+        (WTR::UIScriptController::immediateScrollToOffset):
+        (WTR::UIScriptController::immediateZoomToScale):
+        (WTR::UIScriptController::stableStateOverride):
+        (WTR::UIScriptController::setStableStateOverride):
+        * WebKitTestRunner/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::doAfterPresentationUpdate):
+
 2016-11-19  Joanmarie Diggs  <jdiggs@igalia.com>
 
         AX: [ATK] Expose aria-busy via ATK_STATE_BUSY
index 5f1c59e..d39d68a 100644 (file)
@@ -244,7 +244,7 @@ JSClassRef ${className}::${classRefGetter}()
     static JSClassRef jsClass;
     if (!jsClass) {
         JSClassDefinition definition = kJSClassDefinitionEmpty;
-        definition.className = "${type}";
+        definition.className = "@{[$type->name]}";
         definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
         definition.staticValues = staticValues();
         definition.staticFunctions = staticFunctions();
@@ -431,7 +431,8 @@ sub _platformTypeConstructor
 {
     my ($self, $type, $argumentName) = @_;
 
-    return "JSValueToBoolean(context, $argumentName)" if $type eq "boolean";
+    return "JSValueToNullableBoolean(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
+    return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
     return "$argumentName" if $type->name eq "object";
     return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
     return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($type);
@@ -471,6 +472,7 @@ sub _returnExpression
     my ($self, $returnType, $expression) = @_;
 
     return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
+    return "JSValueMakeBooleanOrNull(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
     return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
     return "${expression}" if $returnType->name eq "object";
     return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
index 3fcfe49..c6ca0d5 100644 (file)
@@ -49,6 +49,11 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
+{
+    return doAsyncTask(callback);
+}
+
 void UIScriptController::zoomToScale(double scale, JSValueRef callback)
 {
     RefPtr<UIScriptController> protectedThis(this);
@@ -145,8 +150,35 @@ JSObjectRef UIScriptController::contentsOfUserInterfaceItem(JSStringRef interfac
     return nullptr;
 }
 
-void UIScriptController::scrollToOffset(long, long)
+static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
+{
+    UIEdgeInsets contentInsets = scrollView.contentInset;
+    CGSize contentSize = scrollView.contentSize;
+    CGSize scrollViewSize = scrollView.bounds.size;
+
+    CGFloat maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
+    contentOffset.x = std::min(maxHorizontalOffset, contentOffset.x);
+    contentOffset.x = std::max(-contentInsets.left, contentOffset.x);
+
+    CGFloat maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
+    contentOffset.y = std::min(maxVerticalOffset, contentOffset.y);
+    contentOffset.y = std::max(-contentInsets.top, contentOffset.y);
+    return contentOffset;
+}
+
+void UIScriptController::scrollToOffset(long x, long y)
+{
+    [gWebScrollView setContentOffset:contentOffsetBoundedInValidRange(gWebScrollView, CGPointMake(x, y)) animated:YES];
+}
+
+void UIScriptController::immediateScrollToOffset(long x, long y)
 {
+    [gWebScrollView setContentOffset:contentOffsetBoundedInValidRange(gWebScrollView, CGPointMake(x, y)) animated:NO];
+}
+
+void UIScriptController::immediateZoomToScale(double scale)
+{
+    [gWebScrollView setZoomScale:scale animated:NO];
 }
 
 void UIScriptController::keyboardAccessoryBarNext()
@@ -167,6 +199,16 @@ double UIScriptController::maximumZoomScale() const
     return gWebScrollView.maximumZoomScale;
 }
 
+Optional<bool> UIScriptController::stableStateOverride() const
+{
+    return Nullopt;
+}
+
+void UIScriptController::setStableStateOverride(Optional<bool>)
+{
+}
+
+
 JSObjectRef UIScriptController::contentVisibleRect() const
 {
     CGRect contentVisibleRect = [gWebBrowserView documentVisibleRect];
index 55db4ee..97cc6a6 100644 (file)
@@ -46,6 +46,11 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
+{
+    return doAsyncTask(callback);
+}
+
 void UIScriptController::insertText(JSStringRef, int, int)
 {
 }
index 5ec7197..15b390d 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef JSWrappable_h
-#define JSWrappable_h
+#pragma once
 
 #include <JavaScriptCore/JavaScript.h>
+#include <wtf/Optional.h>
 #include <wtf/RefCounted.h>
 
 namespace WTR {
@@ -37,11 +37,19 @@ public:
     virtual JSClassRef wrapperClass() = 0;
 };
 
+inline JSValueRef JSValueMakeBooleanOrNull(JSContextRef context, Optional<bool> value)
+{
+    return value ? JSValueMakeBoolean(context, value.value()) : JSValueMakeNull(context);
+}
+
+inline Optional<bool> JSValueToNullableBoolean(JSContextRef context, JSValueRef value)
+{
+    return JSValueIsUndefined(context, value) || JSValueIsNull(context, value) ? Nullopt : Optional<bool>(JSValueToBoolean(context, value));
+}
+
 inline JSValueRef JSValueMakeStringOrNull(JSContextRef context, JSStringRef stringOrNull)
 {
     return stringOrNull ? JSValueMakeString(context, stringOrNull) : JSValueMakeNull(context);
 }
 
 } // namespace WTR
-
-#endif // JSWrappable_h
index 0ed40c0..26a25fa 100644 (file)
@@ -26,8 +26,7 @@
 interface UIScriptController {
 
     void doAsyncTask(object callback); // Used to test the harness.
-
-    void zoomToScale(double scale, object callback);
+    void doAfterPresentationUpdate(object callback); // Call the callback after sending a message to the WebProcess and receiving a subsequent update.
 
     void simulateAccessibilitySettingsChangeNotification(object callback);
 
@@ -146,14 +145,23 @@ interface UIScriptController {
     attribute object willBeginZoomingCallback;
     attribute object didEndZoomingCallback;
 
+    void zoomToScale(double scale, object callback);
+
     void scrollToOffset(long x, long y); // Initiate an animated scroll in the UI process.
     attribute object didEndScrollingCallback;
 
+    void immediateScrollToOffset(long x, long y); // Set the scroll position in the UI process without animation.
+    void immediateZoomToScale(double scale); // Set the zoom scale in the UI process without animation.
+
     // View state
     readonly attribute double zoomScale;
     readonly attribute double minimumZoomScale;
     readonly attribute double maximumZoomScale;
 
+    // Overides the "in stable state" behavior of WKWebView (only applies to iOS)
+    // When false, content rect updates to the web process have inStableState=false, as if a scroll or zoom were in progress.
+    attribute boolean? stableStateOverride;
+
     readonly attribute object contentVisibleRect; // Returned object has 'left', 'top', 'width', 'height' properties.
 
     readonly attribute object selectionRangeViewRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
index 27df59e..9ace202 100644 (file)
@@ -60,6 +60,10 @@ void UIScriptController::doAsyncTask(JSValueRef)
 void simulateAccessibilitySettingsChangeNotification(JSValueRef)
 {
 }
+
+void UIScriptController::doAfterPresentationUpdate(JSValueRef)
+{
+}
 #endif
 
 void UIScriptController::setDidStartFormControlInteractionCallback(JSValueRef callback)
@@ -253,6 +257,14 @@ void UIScriptController::scrollToOffset(long x, long y)
 {
 }
 
+void UIScriptController::immediateScrollToOffset(long x, long y)
+{
+}
+
+void UIScriptController::immediateZoomToScale(double scale)
+{
+}
+
 void UIScriptController::keyboardAccessoryBarNext()
 {
 }
@@ -276,6 +288,15 @@ double UIScriptController::maximumZoomScale() const
     return 1;
 }
 
+Optional<bool> UIScriptController::stableStateOverride() const
+{
+    return Nullopt;
+}
+
+void UIScriptController::setStableStateOverride(Optional<bool>)
+{
+}
+
 JSObjectRef UIScriptController::contentVisibleRect() const
 {
     return nullptr;
index e8d0f8c..afc8cfa 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "JSWrappable.h"
 #include <JavaScriptCore/JSRetainPtr.h>
+#include <wtf/Optional.h>
 #include <wtf/Ref.h>
 
 namespace WebCore {
@@ -50,6 +51,8 @@ public:
     void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
     
     void doAsyncTask(JSValueRef callback);
+    void doAfterPresentationUpdate(JSValueRef callback);
+
     void zoomToScale(double scale, JSValueRef callback);
 
     void simulateAccessibilitySettingsChangeNotification(JSValueRef callback);
@@ -85,6 +88,9 @@ public:
     
     void scrollToOffset(long x, long y);
 
+    void immediateScrollToOffset(long x, long y);
+    void immediateZoomToScale(double scale);
+
     void setDidStartFormControlInteractionCallback(JSValueRef);
     JSValueRef didStartFormControlInteractionCallback() const;
 
@@ -115,6 +121,9 @@ public:
     double zoomScale() const;
     double minimumZoomScale() const;
     double maximumZoomScale() const;
+    
+    Optional<bool> stableStateOverride() const;
+    void setStableStateOverride(Optional<bool>);
 
     JSObjectRef contentVisibleRect() const;
     
index cd9ed3f..98d37da 100644 (file)
@@ -77,6 +77,7 @@ sub _className
 sub _classRefGetter
 {
     my ($self, $type) = @_;
+
     return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($type)) . "Class";
 }
 
@@ -444,6 +445,7 @@ sub _platformTypeConstructor
 {
     my ($self, $type, $argumentName) = @_;
 
+    return "JSValueToNullableBoolean(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
     return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
     return "$argumentName" if $type->name eq "object";
     return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
@@ -484,6 +486,7 @@ sub _returnExpression
     my ($self, $returnType, $expression) = @_;
 
     return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
+    return "JSValueMakeBooleanOrNull(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
     return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
     return "${expression}" if $returnType->name eq "object";
     return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
index e5d9b4a..078a652 100644 (file)
 @property (nonatomic, copy) void (^didHideKeyboardCallback)(void);
 @property (nonatomic, copy) void (^didEndScrollingCallback)(void);
 
-- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration;
-- (void)dealloc;
-
 - (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler;
 #endif
 
+@property (nonatomic, retain, setter=_setStableStateOverride:) NSNumber *_stableStateOverride;
+
 @end
 
 #endif // #if WK_API_ENABLED
index 9699747..8d348b6 100644 (file)
 
 #if WK_API_ENABLED
 
-@interface TestRunnerWKWebView ()
+@interface TestRunnerWKWebView () {
+    RetainPtr<NSNumber *> _stableStateOverride;
+}
+
 @property (nonatomic, copy) void (^zoomToScaleCompletionHandler)(void);
 @property (nonatomic, copy) void (^showKeyboardCompletionHandler)(void);
 @property (nonatomic) BOOL isShowingKeyboard;
+
 @end
 
 @implementation TestRunnerWKWebView
     if (self.didEndScrollingCallback)
         self.didEndScrollingCallback();
 }
+
+- (NSNumber *)_stableStateOverride
+{
+    return _stableStateOverride.get();
+}
+
+- (void)_setStableStateOverride:(NSNumber *)overrideBoolean
+{
+    _stableStateOverride = overrideBoolean;
+}
+
 #endif
 
 @end
index 452c410..d85a6d7 100644 (file)
@@ -86,6 +86,7 @@ void TestController::platformResetStateToConsistentValues()
     cocoaResetStateToConsistentValues();
 
     if (PlatformWebView* webView = mainWebView()) {
+        webView->platformView()._stableStateOverride = nil;
         UIScrollView *scrollView = webView->platformView().scrollView;
         [scrollView setZoomScale:1 animated:NO];
         [scrollView setContentOffset:CGPointZero];
index 994b6da..faf7037 100644 (file)
@@ -55,6 +55,18 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+    [webView _doAfterNextPresentationUpdate:^{
+        if (!m_context)
+            return;
+        m_context->asyncTaskComplete(callbackID);
+    }];
+}
+
 void UIScriptController::zoomToScale(double scale, JSValueRef callback)
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
@@ -357,6 +369,18 @@ void UIScriptController::scrollToOffset(long x, long y)
     [webView.scrollView setContentOffset:contentOffsetBoundedInValidRange(webView.scrollView, CGPointMake(x, y)) animated:YES];
 }
 
+void UIScriptController::immediateScrollToOffset(long x, long y)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    [webView.scrollView setContentOffset:contentOffsetBoundedInValidRange(webView.scrollView, CGPointMake(x, y)) animated:NO];
+}
+
+void UIScriptController::immediateZoomToScale(double scale)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    [webView.scrollView setZoomScale:scale animated:NO];
+}
+
 void UIScriptController::keyboardAccessoryBarNext()
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
@@ -381,6 +405,24 @@ double UIScriptController::maximumZoomScale() const
     return webView.scrollView.maximumZoomScale;
 }
 
+Optional<bool> UIScriptController::stableStateOverride() const
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    if (webView._stableStateOverride)
+        return webView._stableStateOverride.boolValue;
+
+    return Nullopt;
+}
+
+void UIScriptController::setStableStateOverride(Optional<bool> overrideValue)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    if (overrideValue)
+        webView._stableStateOverride = @(overrideValue.value());
+    else
+        webView._stableStateOverride = nil;
+}
+
 JSObjectRef UIScriptController::contentVisibleRect() const
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
index 79be0c2..d503b4a 100644 (file)
@@ -56,6 +56,11 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
+{
+    return doAsyncTask(callback);
+}
+
 void UIScriptController::insertText(JSStringRef text, int location, int length)
 {
 #if WK_API_ENABLED