<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 47f02cd07ee60efe74ff7c0be21e59fb7eec0af9..d0c96c4d4336260b9c8eaab595ea389b8744c166 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 ffff530295c440af4157b54e37a351bb18c08cc8..4074703b429b2c86fb2712bcf40d4f94e55eb8b0 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 70dbd89afb981f09c76489c6d06253e7b6849e35..a228a19e15bf95082c9c943ee0e0def0cf066d9d 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 5589a42528600a614008ba468bc7560db588930c..47a533797d0317601781b2fe5193c20ed07fd641 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 8d0b9089c83356fb14d7a6fc7b626313add2035c..1e89cb9d4a193c98b89f355aca37a2ea9de0dac4 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 c3b0f017032c1c8b9a1893e72d3a21cb95a189ee..33f312add8869e9e561e649b4bfae0e5d81cda1f 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 5e7c2351895f6efad6f7b2de5a892a49c585f489..28e22ed4cb88997eaeaf67a3eff60e4a1bf28073 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 1eb810d470f6203c59c1beac061b89655d9a2926..13df6e8f86b4344cf0a10c04b2478b574793e47c 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 b17c696c0e5c7b074ef0032039989ed16b12cf91..94bae943fe085920c3badc4ba14067849fb7de12 100644 (file)
@@ -47,6 +47,8 @@ typedef enum  {
     CallbackTypeDidShowKeyboard,
     CallbackTypeDidHideKeyboard,
     CallbackTypeDidEndScrolling,
+    CallbackTypeDidStartFormControlInteraction,
+    CallbackTypeDidEndFormControlInteraction,
     CallbackTypeNonPersistent = firstNonPersistentCallbackID
 } CallbackType;
 
index 1bf44cb07867da1c51bf0e4dbcd0894db18fb532..1005cbb1fca8829d0d3edf1ddd7f9f78896f9643 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 07717ae7a771e28bcc867c8b71c3e9c35be405bd..d4516c357db330f38c7a9b3671506c0701f3ec6d 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 276cc69809626ef34f0e810a995fea7b039eed47..0f28c28aaa4c3faa084adcf1a8729b998db20039 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 2950910ae404dd0362df606b9f1db7b71729bab2..fa7a2efe72a5aa8103ec5223807df95443a2ba77 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 b479b9336f406f86408eca222b52cc8eefd2cd3a..c5c619d377a633c97a3d56ae1f93c3404ea90c5f 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();