<select> menu on iPad causes shifting of hit-testing areas
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Aug 2016 18:02:03 +0000 (18:02 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 Aug 2016 18:02:03 +0000 (18:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=150079

Reviewed by Tim Horton.

Source/WebKit2:

-isAssistingNode (aka the poorly named isEditable) is input into WebPageProxy::computeCustomFixedPositionRect(),
so when it changes we have to update visible rects. We were doing this on focus, but failing
to do it on blur.

Added the ability to test by:
1. Making it possible to initiate an animated scroll in the UI process
2. Adding callbacks for starting and ending interaction with a form control. Sadly
the "ending interaction" for <select> popovers on iPad isn't late enough, since we
have no way currently to know when the dimming view behind the popover animates out,
so for now the test keeps trying to tap a button.

Test: fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _zoomToPoint:atScale:animated:]):
(-[WKWebView _scrollToRect:origin:minimumScrollDistance:]):
(-[WKWebView _scrollByContentOffset:]):
(-[WKWebView _zoomToFocusRect:selectionRect:fontSize:minimumScale:maximumScale:allowScaling:forceScroll:]):
(-[WKWebView didStartFormControlInteraction]):
(-[WKWebView didEndFormControlInteraction]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/ios/WKContentView.mm:
(-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setIsEditable:]):
(-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]):
(-[WKContentView _stopAssistingNode]):

Tools:

-isAssistingNode (aka the poorly named isEditable) is input into WebPageProxy::computeCustomFixedPositionRect(),
so when it changes we have to update visible rects. We were doing this on focus, but failing
to do it on blur.

Added the ability to test by:
1. Making it possible to initiate an animated scroll in the UI process
2. Adding callbacks for starting and ending interaction with a form control. Sadly
the "ending interaction" for <select> popovers on iPad isn't late enough, since we
have no way currently to know when the dimming view behind the popover animates out,
so for now the test keeps trying to tap a button.

* WebKitTestRunner/UIScriptContext/Bindings/UIScriptController.idl:
* WebKitTestRunner/UIScriptContext/UIScriptContext.h:
* WebKitTestRunner/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::setDidStartFormControlInteractionCallback):
(WTR::UIScriptController::didStartFormControlInteractionCallback):
(WTR::UIScriptController::setDidEndFormControlInteractionCallback):
(WTR::UIScriptController::didEndFormControlInteractionCallback):
(WTR::UIScriptController::scrollToOffset):
(WTR::UIScriptController::platformSetDidStartFormControlInteractionCallback):
(WTR::UIScriptController::platformSetDidEndFormControlInteractionCallback):
* WebKitTestRunner/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
(-[TestRunnerWKWebView dealloc]):
(-[TestRunnerWKWebView didStartFormControlInteraction]):
(-[TestRunnerWKWebView didEndFormControlInteraction]):
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::contentOffsetBoundedInValidRange):
(WTR::UIScriptController::scrollToOffset):
(WTR::UIScriptController::platformSetDidStartFormControlInteractionCallback):
(WTR::UIScriptController::platformSetDidEndFormControlInteractionCallback):

LayoutTests:

This test:
1. Scrolls in the ui process, so that position:fixed kicks in
2. Taps on a <select>, which disabled position:fixed behavior
3. Chooses a <select> option, dismissing the <select>, which restores position:fixed behavior
4. Tries to tap a button inside position:fixed.

* fast/forms/ios/ipad/unfocus-inside-fixed-hittest-expected.txt: Added.
* fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html: Added.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html [new file with mode: 0644]
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit2/UIProcess/ios/WKContentView.mm
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm
Tools/ChangeLog
Tools/WebKitTestRunner/UIScriptContext/Bindings/UIScriptController.idl
Tools/WebKitTestRunner/UIScriptContext/UIScriptContext.h
Tools/WebKitTestRunner/UIScriptContext/UIScriptController.cpp
Tools/WebKitTestRunner/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

index 47f02cd..d0c96c4 100644 (file)
@@ -1,3 +1,19 @@
+2016-08-22  Simon Fraser  <simon.fraser@apple.com>
+
+        <select> menu on iPad causes shifting of hit-testing areas
+        https://bugs.webkit.org/show_bug.cgi?id=150079
+
+        Reviewed by Tim Horton.
+
+        This test:
+        1. Scrolls in the ui process, so that position:fixed kicks in
+        2. Taps on a <select>, which disabled position:fixed behavior
+        3. Chooses a <select> option, dismissing the <select>, which restores position:fixed behavior
+        4. Tries to tap a button inside position:fixed.
+
+        * fast/forms/ios/ipad/unfocus-inside-fixed-hittest-expected.txt: Added.
+        * fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html: Added.
+
 2016-08-22  Antti Koivisto  <antti@apple.com>
 
         Can't style descendants in shadow tree using the :host pseudo class
diff --git a/LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest-expected.txt b/LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest-expected.txt
new file mode 100644 (file)
index 0000000..0c31274
--- /dev/null
@@ -0,0 +1,5 @@
+This is the top
+
+Click Me
+June
+PASS: hit testing found #target after select interation
diff --git a/LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html b/LayoutTests/fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html
new file mode 100644 (file)
index 0000000..9e236b9
--- /dev/null
@@ -0,0 +1,137 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+    <style>
+        body {
+            height: 1500px;
+        }
+        #container {
+            position: fixed;
+            top: 10px;
+            left: 10px;
+            bottom: 30px;
+            right: 10px;
+            background-color: rgba(0, 0, 0, 0.5);
+            padding: 10px;
+            box-sizing: border-box;
+            border: 5px solid black;
+        }
+        
+        select {
+            display: block;
+            margin: 500px 200px 20px 20px;
+        }
+        
+        button {
+            display: block;
+        }
+        
+    </style>
+    <script src="../resources/zooming-test-utils.js"></script>
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        function getSingleTapUIScript(x, y)
+        {
+            return `
+                (function() {
+                    uiController.singleTapAtPoint(${x}, ${y}, function() {
+                        uiController.uiScriptComplete('');
+                    });
+                })();`
+        }
+
+        function getScrollDownUIScript(x, y)
+        {
+            return `
+                (function() {
+                    uiController.didEndScrollingCallback = function() {
+                        uiController.uiScriptComplete();
+                    };
+
+                    uiController.scrollToOffset(${x}, ${y});
+                })();`
+        }
+
+        function getTapOnSelectUIScript(x, y)
+        {
+            return `
+                (function() {
+                    uiController.didStartFormControlInteractionCallback = function() {
+                        uiController.selectFormAccessoryPickerRow(5);
+                    };
+
+                    uiController.didEndFormControlInteractionCallback = function() {
+                        uiController.uiScriptComplete();
+                    };
+
+                    uiController.singleTapAtPoint(${x}, ${y}, function() {
+                    });
+                })();`
+        }
+
+        var clicked = false;
+        function buttonClicked()
+        {
+            document.getElementById('result').textContent = 'PASS: hit testing found #target after select interation';
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+
+        function tryTapOnButton()
+        {
+            var point = getPointInsideElement(document.getElementById('target'), 10, 10);
+            testRunner.runUIScript(getSingleTapUIScript(point.x, point.y), function() {
+            });
+            
+            // We have to keep retrying, because the dimming view behind the popover animates out,
+            // and we currently have no callback when that animation completes.
+            window.setTimeout(tryTapOnButton, 100);
+        }
+
+        function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            testRunner.waitUntilDone();
+            testRunner.dumpAsText();
+
+            testRunner.runUIScript(getScrollDownUIScript(0, 500), function() {
+                var selectElement = document.getElementsByTagName('select')[0];
+                var point = getPointInsideElement(selectElement, 10, 10);
+                testRunner.runUIScript(getTapOnSelectUIScript(point.x, point.y), function() {
+                    document.getElementById('select-value').textContent = selectElement.value;
+                    tryTapOnButton();
+                });
+            });
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <p>This is the top</p>
+    <div id="container">
+        <button id="target" onclick="buttonClicked()">Click Me</button>
+        <select>
+            <option>January</option>
+            <option>February</option>
+            <option>March</option>
+            <option>April</option>
+            <option>May</option>
+            <option>June</option>
+            <option>July</option>
+            <option>August</option>
+            <option>September</option>
+        </select>
+        <span id="select-value">Value goes here</span>
+        <div id="result">FAIL: should have hit-tested and found #target element</div>
+    </div>
+    
+</div>
+</body>
+</html>
index ffff530..4074703 100644 (file)
@@ -1,3 +1,38 @@
+2016-08-22  Simon Fraser  <simon.fraser@apple.com>
+
+        <select> menu on iPad causes shifting of hit-testing areas
+        https://bugs.webkit.org/show_bug.cgi?id=150079
+
+        Reviewed by Tim Horton.
+
+        -isAssistingNode (aka the poorly named isEditable) is input into WebPageProxy::computeCustomFixedPositionRect(),
+        so when it changes we have to update visible rects. We were doing this on focus, but failing
+        to do it on blur.
+
+        Added the ability to test by:
+        1. Making it possible to initiate an animated scroll in the UI process
+        2. Adding callbacks for starting and ending interaction with a form control. Sadly
+        the "ending interaction" for <select> popovers on iPad isn't late enough, since we
+        have no way currently to know when the dimming view behind the popover animates out,
+        so for now the test keeps trying to tap a button.
+
+        Test: fast/forms/ios/ipad/unfocus-inside-fixed-hittest.html
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _zoomToPoint:atScale:animated:]):
+        (-[WKWebView _scrollToRect:origin:minimumScrollDistance:]):
+        (-[WKWebView _scrollByContentOffset:]):
+        (-[WKWebView _zoomToFocusRect:selectionRect:fontSize:minimumScale:maximumScale:allowScaling:forceScroll:]):
+        (-[WKWebView didStartFormControlInteraction]):
+        (-[WKWebView didEndFormControlInteraction]):
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setIsEditable:]):
+        (-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]):
+        (-[WKContentView _stopAssistingNode]):
+
 2016-08-22  Daniel Bates  <dabates@apple.com>
 
         [iOS] <a ping> and <area ping> tests time out
index 70dbd89..a228a19 100644 (file)
@@ -1433,6 +1433,8 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
     if (scale != zoomScale)
         _page->willStartUserTriggeredZooming();
 
+    LOG_WITH_STREAM(VisibleRects, stream << "_zoomToPoint:" << point << " scale: " << scale << " duration:" << duration);
+
     [_scrollView _zoomToCenter:point scale:scale duration:duration];
 }
 
@@ -1533,6 +1535,8 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
 
     [_contentView willStartZoomOrScroll];
 
+    LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta);
+
     [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES];
     return true;
 }
@@ -1549,6 +1553,9 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     if (CGPointEqualToPoint(boundedOffset, currentOffset))
         return;
     [_contentView willStartZoomOrScroll];
+
+    LOG_WITH_STREAM(VisibleRects, stream << "_scrollByContentOffset: scrolling to " << boundedOffset);
+
     [_scrollView setContentOffset:boundedOffset animated:YES];
 }
 
@@ -1674,6 +1681,8 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     if (scale != contentZoomScale(self))
         _page->willStartUserTriggeredZooming();
 
+    LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale);
+
     // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
     newCenter.scale(1 / scale, 1 / scale);
     [_scrollView _zoomToCenter:newCenter
@@ -4527,6 +4536,16 @@ static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISe
     [_contentView selectFormAccessoryPickerRow:rowIndex];
 }
 
+- (void)didStartFormControlInteraction
+{
+    // For subclasses to override.
+}
+
+- (void)didEndFormControlInteraction
+{
+    // For subclasses to override.
+}
+
 #endif // PLATFORM(IOS)
 
 #if PLATFORM(MAC)
index 5589a42..47a5337 100644 (file)
@@ -264,6 +264,9 @@ typedef NS_ENUM(NSInteger, _WKImmediateActionType) {
 - (void)dismissFormAccessoryView WK_API_AVAILABLE(ios(WK_IOS_TBA));
 - (void)selectFormAccessoryPickerRow:(int)rowIndex WK_API_AVAILABLE(ios(WK_IOS_TBA));
 
+- (void)didStartFormControlInteraction WK_API_AVAILABLE(ios(WK_IOS_TBA));
+- (void)didEndFormControlInteraction WK_API_AVAILABLE(ios(WK_IOS_TBA));
+
 #endif
 
 #if !TARGET_OS_IPHONE
index 8d0b908..1e89cb9 100644 (file)
@@ -31,6 +31,7 @@
 #import "APIPageConfiguration.h"
 #import "AccessibilityIOS.h"
 #import "ApplicationStateTracker.h"
+#import "Logging.h"
 #import "PageClientImplIOS.h"
 #import "PrintInfo.h"
 #import "RemoteLayerTreeDrawingAreaProxy.h"
@@ -58,6 +59,7 @@
 #import <WebCore/NotImplemented.h>
 #import <WebCore/PlatformScreen.h>
 #import <WebCore/QuartzCoreSPI.h>
+#import <WebCore/TextStream.h>
 #import <wtf/CurrentTime.h>
 #import <wtf/RetainPtr.h>
 
@@ -377,6 +379,8 @@ private:
 
     FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredRect, zoomScale, WebPageProxy::UnobscuredRectConstraint::ConstrainedToDocumentRect);
 
+    LOG_WITH_STREAM(VisibleRects, stream << "didUpdateVisibleRect: visibleRect:" << visibleRect << " unobscuredRect:" << unobscuredRect << " fixedPositionRectForLayout:" << fixedPositionRectForLayout);
+
     VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
         visibleRect,
         unobscuredRect,
index c3b0f01..33f312a 100644 (file)
@@ -748,6 +748,15 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     return _isEditable;
 }
 
+- (BOOL)setIsEditable:(BOOL)isEditable
+{
+    if (isEditable == _isEditable)
+        return NO;
+
+    _isEditable = isEditable;
+    return YES;
+}
+
 - (BOOL)canBecomeFirstResponder
 {
     if (_resigningFirstResponder)
@@ -3579,7 +3588,7 @@ static bool isAssistableInputType(InputType type)
     if (_assistedNodeInformation.elementType == information.elementType && _assistedNodeInformation.elementRect == information.elementRect)
         return;
 
-    _isEditable = YES;
+    BOOL editableChanged = [self setIsEditable:YES];
     _assistedNodeInformation = information;
     _inputPeripheral = nil;
     _traits = nil;
@@ -3600,7 +3609,8 @@ static bool isAssistableInputType(InputType type)
         break;
     }
     
-    if (information.insideFixedPosition)
+    // The custom fixed position rect behavior is affected by -isAssistingNode, so if that changes we need to recompute rects.
+    if (editableChanged)
         [_webView _updateVisibleContentRects];
     
     [self _displayFormNodeInputView];
@@ -3612,13 +3622,17 @@ static bool isAssistableInputType(InputType type)
         _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get() userObject:userObject]);
         [inputDelegate _webView:_webView didStartInputSession:_formInputSession.get()];
     }
+    
+    [_webView didStartFormControlInteraction];
 }
 
 - (void)_stopAssistingNode
 {
     [_formInputSession invalidate];
     _formInputSession = nil;
-    _isEditable = NO;
+
+    BOOL editableChanged = [self setIsEditable:NO];
+
     _assistedNodeInformation.elementType = InputType::None;
     _inputPeripheral = nil;
 
@@ -3628,6 +3642,12 @@ static bool isAssistableInputType(InputType type)
     [self _updateAccessory];
     // The name is misleading, but this actually clears the selection views and removes any selection.
     [_webSelectionAssistant resignedFirstResponder];
+
+    // The custom fixed position rect behavior is affected by -isAssistingNode, so if that changes we need to recompute rects.
+    if (editableChanged)
+        [_webView _updateVisibleContentRects];
+
+    [_webView didEndFormControlInteraction];
 }
 
 - (void)_selectionChanged
index 5e7c235..28e22ed 100644 (file)
@@ -1,3 +1,43 @@
+2016-08-22  Simon Fraser  <simon.fraser@apple.com>
+
+        <select> menu on iPad causes shifting of hit-testing areas
+        https://bugs.webkit.org/show_bug.cgi?id=150079
+
+        Reviewed by Tim Horton.
+
+        -isAssistingNode (aka the poorly named isEditable) is input into WebPageProxy::computeCustomFixedPositionRect(),
+        so when it changes we have to update visible rects. We were doing this on focus, but failing
+        to do it on blur.
+
+        Added the ability to test by:
+        1. Making it possible to initiate an animated scroll in the UI process
+        2. Adding callbacks for starting and ending interaction with a form control. Sadly
+        the "ending interaction" for <select> popovers on iPad isn't late enough, since we
+        have no way currently to know when the dimming view behind the popover animates out,
+        so for now the test keeps trying to tap a button.
+
+        * WebKitTestRunner/UIScriptContext/Bindings/UIScriptController.idl:
+        * WebKitTestRunner/UIScriptContext/UIScriptContext.h:
+        * WebKitTestRunner/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::setDidStartFormControlInteractionCallback):
+        (WTR::UIScriptController::didStartFormControlInteractionCallback):
+        (WTR::UIScriptController::setDidEndFormControlInteractionCallback):
+        (WTR::UIScriptController::didEndFormControlInteractionCallback):
+        (WTR::UIScriptController::scrollToOffset):
+        (WTR::UIScriptController::platformSetDidStartFormControlInteractionCallback):
+        (WTR::UIScriptController::platformSetDidEndFormControlInteractionCallback):
+        * WebKitTestRunner/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+        (-[TestRunnerWKWebView dealloc]):
+        (-[TestRunnerWKWebView didStartFormControlInteraction]):
+        (-[TestRunnerWKWebView didEndFormControlInteraction]):
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::contentOffsetBoundedInValidRange):
+        (WTR::UIScriptController::scrollToOffset):
+        (WTR::UIScriptController::platformSetDidStartFormControlInteractionCallback):
+        (WTR::UIScriptController::platformSetDidEndFormControlInteractionCallback):
+
 2016-08-22  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [EFL][GTK] Script process-linux-coredump is not needed, switch back to use the kernel core dumper instead.
index 1eb810d..13df6e8 100644 (file)
@@ -45,6 +45,10 @@ interface UIScriptController {
     // Equivalent of pressing the Done button in the form accessory bar.
     void dismissFormAccessoryView();
 
+    // Form control handling
+    attribute object didStartFormControlInteractionCallback;
+    attribute object didEndFormControlInteractionCallback;
+
     // <select> picker
     void selectFormAccessoryPickerRow(long rowIndex);
 
@@ -58,6 +62,7 @@ interface UIScriptController {
     attribute object willBeginZoomingCallback;
     attribute object didEndZoomingCallback;
 
+    void scrollToOffset(long x, long y); // Initiate an animated scroll in the UI process.
     attribute object didEndScrollingCallback;
 
     // View state
index b17c696..94bae94 100644 (file)
@@ -47,6 +47,8 @@ typedef enum  {
     CallbackTypeDidShowKeyboard,
     CallbackTypeDidHideKeyboard,
     CallbackTypeDidEndScrolling,
+    CallbackTypeDidStartFormControlInteraction,
+    CallbackTypeDidEndFormControlInteraction,
     CallbackTypeNonPersistent = firstNonPersistentCallbackID
 } CallbackType;
 
index 1bf44cb..1005cbb 100644 (file)
@@ -58,6 +58,28 @@ void UIScriptController::doAsyncTask(JSValueRef)
 }
 #endif
 
+void UIScriptController::setDidStartFormControlInteractionCallback(JSValueRef callback)
+{
+    m_context->registerCallback(callback, CallbackTypeDidStartFormControlInteraction);
+    platformSetDidStartFormControlInteractionCallback();
+}
+
+JSValueRef UIScriptController::didStartFormControlInteractionCallback() const
+{
+    return m_context->callbackWithID(CallbackTypeDidStartFormControlInteraction);
+}
+
+void UIScriptController::setDidEndFormControlInteractionCallback(JSValueRef callback)
+{
+    m_context->registerCallback(callback, CallbackTypeDidEndFormControlInteraction);
+    platformSetDidEndFormControlInteractionCallback();
+}
+
+JSValueRef UIScriptController::didEndFormControlInteractionCallback() const
+{
+    return m_context->callbackWithID(CallbackTypeDidEndFormControlInteraction);
+}
+
 void UIScriptController::setWillBeginZoomingCallback(JSValueRef callback)
 {
     m_context->registerCallback(callback, CallbackTypeWillBeginZooming);
@@ -158,6 +180,10 @@ void UIScriptController::selectFormAccessoryPickerRow(long)
 {
 }
 
+void UIScriptController::scrollToOffset(long x, long y)
+{
+}
+
 void UIScriptController::keyboardAccessoryBarNext()
 {
 }
@@ -186,6 +212,14 @@ JSObjectRef UIScriptController::contentVisibleRect() const
     return nullptr;
 }
 
+void UIScriptController::platformSetDidStartFormControlInteractionCallback()
+{
+}
+
+void UIScriptController::platformSetDidEndFormControlInteractionCallback()
+{
+}
+
 void UIScriptController::platformSetWillBeginZoomingCallback()
 {
 }
index 07717ae..d4516c3 100644 (file)
@@ -62,6 +62,14 @@ public:
     
     void dismissFormAccessoryView();
     void selectFormAccessoryPickerRow(long);
+    
+    void scrollToOffset(long x, long y);
+
+    void setDidStartFormControlInteractionCallback(JSValueRef);
+    JSValueRef didStartFormControlInteractionCallback() const;
+
+    void setDidEndFormControlInteractionCallback(JSValueRef);
+    JSValueRef didEndFormControlInteractionCallback() const;
 
     void setWillBeginZoomingCallback(JSValueRef);
     JSValueRef willBeginZoomingCallback() const;
@@ -88,7 +96,9 @@ public:
 
 private:
     UIScriptController(UIScriptContext&);
-    
+
+    void platformSetDidStartFormControlInteractionCallback();
+    void platformSetDidEndFormControlInteractionCallback();
     void platformSetWillBeginZoomingCallback();
     void platformSetDidEndZoomingCallback();
     void platformSetDidShowKeyboardCallback();
index 276cc69..0f28c28 100644 (file)
@@ -31,6 +31,8 @@
 
 #if PLATFORM(IOS)
 
+@property (nonatomic, copy) void (^didStartFormControlInteractionCallback)(void);
+@property (nonatomic, copy) void (^didEndFormControlInteractionCallback)(void);
 @property (nonatomic, copy) void (^willBeginZoomingCallback)(void);
 @property (nonatomic, copy) void (^didEndZoomingCallback)(void);
 @property (nonatomic, copy) void (^didShowKeyboardCallback)(void);
index 2950910..fa7a2ef 100644 (file)
@@ -73,6 +73,8 @@
 {
     [[NSNotificationCenter defaultCenter] removeObserver:self];
 
+    self.didStartFormControlInteractionCallback = nil;
+    self.didEndFormControlInteractionCallback = nil;
     self.willBeginZoomingCallback = nil;
     self.didEndZoomingCallback = nil;
     self.didShowKeyboardCallback = nil;
     [super dealloc];
 }
 
+- (void)didStartFormControlInteraction
+{
+    if (self.didStartFormControlInteractionCallback)
+        self.didStartFormControlInteractionCallback();
+}
+
+- (void)didEndFormControlInteraction
+{
+    if (self.didEndFormControlInteractionCallback)
+        self.didEndFormControlInteractionCallback();
+}
+
 - (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler
 {
     ASSERT(!self.zoomToScaleCompletionHandler);
index b479b93..c5c619d 100644 (file)
@@ -187,6 +187,28 @@ void UIScriptController::selectFormAccessoryPickerRow(long rowIndex)
     [webView selectFormAccessoryPickerRow:rowIndex];
 }
 
+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)
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    [webView.scrollView setContentOffset:contentOffsetBoundedInValidRange(webView.scrollView, CGPointMake(x, y)) animated:YES];
+}
+
 void UIScriptController::keyboardAccessoryBarNext()
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
@@ -221,6 +243,26 @@ JSObjectRef UIScriptController::contentVisibleRect() const
     return m_context->objectFromRect(wkRect);
 }
 
+void UIScriptController::platformSetDidStartFormControlInteractionCallback()
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    webView.didStartFormControlInteractionCallback = ^{
+        if (!m_context)
+            return;
+        m_context->fireCallback(CallbackTypeDidStartFormControlInteraction);
+    };
+}
+
+void UIScriptController::platformSetDidEndFormControlInteractionCallback()
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    webView.didEndFormControlInteractionCallback = ^{
+        if (!m_context)
+            return;
+        m_context->fireCallback(CallbackTypeDidEndFormControlInteraction);
+    };
+}
+
 void UIScriptController::platformSetWillBeginZoomingCallback()
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();