Changeset 241282 in webkit


Ignore:
Timestamp:
Feb 11, 2019 3:24:23 PM (5 years ago)
Author:
dbates@webkit.org
Message:

[iOS] Mouse/Touch/Pointer events are missing modifier keys
https://bugs.webkit.org/show_bug.cgi?id=191446
<rdar://problem/45929460>

Reviewed by Tim Horton.

Source/WebCore:

Extract the modifier flags from the WebEvent. This code is only used by Legacy WebKit
on iOS and we will need to fix <rdar://problem/47929759> in order for modifier flags
to be passed to WebKit.

Tests: fast/events/touch/ios/mouse-events-with-modifiers.html

fast/events/touch/ios/pointer-events-with-modifiers.html
fast/events/touch/ios/touch-events-with-modifiers.html

  • platform/ios/PlatformEventFactoryIOS.mm:

(WebCore::PlatformMouseEventBuilder::PlatformMouseEventBuilder):

  • platform/ios/WebEvent.h:
  • platform/ios/WebEvent.mm:

(-[WebEvent initWithMouseEventType:timeStamp:location:]):
(-[WebEvent initWithMouseEventType:timeStamp:location:modifiers:]):

Source/WebKit:

Make use of UIKit SPI to retreive the modifier flags when dispatching mouse and touch events.
Add new WebKit SPI for iOS, -[WKNavigationAction modifierFlags], to retrieve the the modifier
flags held when a navigation action was initiated.

  • Platform/spi/ios/UIKitSPI.h: Expose SPI.
  • Shared/NativeWebTouchEvent.h: Re-arrange macro guards so that we can expose the helper function

WebKit::webEventModifierFlags(). This is a bit more involved that usual since this header is included
from both C++ and Objective-C source files. It only makes sense to expose this function when
compiling as part of an Objective-C source file.

  • Shared/ios/NativeWebTouchEventIOS.mm:

(WebKit::NativeWebTouchEvent::NativeWebTouchEvent): Modified to take the modifier flags held down
when the platform touch event was received and pass them through to the base constructor.
(WebKit::webEventModifierFlags): Added. Converts from the platform-speciifc UIKeyModifierFlags to
OptionSet<WebKit::WebEvent::Modifier>.

  • Shared/ios/WebIOSEventFactory.h:
  • Shared/ios/WebIOSEventFactory.mm:

(WebIOSEventFactory::toUIKeyModifierFlags): Added. Converts from OptionSet<WebKit::WebEvent::Modifier>
to the platform-specific UIKeyModifierFlags.

  • UIProcess/API/Cocoa/WKNavigationAction.mm:

(-[WKNavigationAction modifierFlags]): Added.

  • UIProcess/API/Cocoa/WKNavigationActionPrivate.h:
  • UIProcess/WebPageProxy.h:
  • UIProcess/ios/WKContentViewInteraction.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(gestureRecognizerModifierFlags): Added.

(-[WKContentView _webTouchEventsRecognized:]):
(-[WKContentView _highlightLongPressRecognized:]):
(-[WKContentView _twoFingerSingleTapGestureRecognized:]):
(-[WKContentView _singleTapCommited:]):
Pass modifier flags through.

(-[WKContentView _attemptClickAtLocation:modifierFlags:]): Added.
(-[WKContentView actionSheetAssistant:openElementAtLocation:]): This is invoked when a person opens a link
via the action sheet. We don't have access to the modifier flags to pass. It also seems like an implementation
detail that this action is implemented via mouse click and we should re-evaluate this decision in light of
the fact tht the action sheet is browser UI and we tend to be very reserved on what UI actions are visible
to the page. On Mac, opening a link via the context menu is not visible to the page, at least from a mouse
event perspective.
(webEventFlagsForUIKeyModifierFlags): Added.
(-[WKContentView _hoverGestureRecognizerChanged:]): Pass modifier flags through.
(-[WKContentView _attemptClickAtLocation:]): Deleted.

  • UIProcess/ios/WebPageProxyIOS.mm:

(WebKit::WebPageProxy::handleTwoFingerTapAtPoint):
(WebKit::WebPageProxy::commitPotentialTap):
(WebKit::WebPageProxy::handleTap):

  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/WebPage.messages.in:
  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::handleSyntheticClick):
(WebKit::WebPage::completePendingSyntheticClickForContentChangeObserver):
(WebKit::WebPage::completeSyntheticClick):
(WebKit::WebPage::handleTap):
(WebKit::WebPage::handleTwoFingerTapAtPoint):
(WebKit::WebPage::commitPotentialTap):
Pass modifier flags through.

Tools:

Add support infrastructure for testing touch and stylus taps when holding modifier keys.

  • DumpRenderTree/ios/UIScriptControllerIOS.mm:

(WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
(WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.

  • TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
  • TestRunnerShared/UIScriptContext/UIScriptController.cpp:

(WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
(WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.

  • TestRunnerShared/UIScriptContext/UIScriptController.h:
  • WebKitTestRunner/ios/UIScriptControllerIOS.mm:

(WTR::arrayLength):
(WTR::parseModifierArray):
(WTR::UIScriptController::singleTapAtPoint): Implemented in terms of singleTapAtPointWithModifiers().
(WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
(WTR::UIScriptController::stylusTapAtPoint): Implemented in terms of stylusTapAtPointWithModifiers().
(WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.

LayoutTests:

Refactor existing iOS key events tests to share code. Add new tests to ensure touch and mouse events
have accurate modifier key details.

  • fast/events/ios/key-events-meta-alt-combinations.html:
  • fast/events/ios/resources/key-tester.js:

(computeSubsets.compareByModifierOrder): Deleted.

  • fast/events/resources/compute-subsets.js: Added.

(computeSubsets.compareByOriginalArrayOrder):
(computeSubsets):

  • fast/events/touch/ios/mouse-events-with-modifiers-expected.txt: Added.
  • fast/events/touch/ios/mouse-events-with-modifiers.html: Added.
  • fast/events/touch/ios/pointer-events-with-modifiers-expected.txt: Added.
  • fast/events/touch/ios/pointer-events-with-modifiers.html: Added.
  • fast/events/touch/ios/touch-events-with-modifiers-expected.txt: Added.
  • fast/events/touch/ios/touch-events-with-modifiers.html: Added.
  • http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt: Update expected result

due to changes to ui-helper.js.

  • http/tests/security/anchor-download-block-crossorigin-expected.txt: Ditto.
  • platform/ios/TestExpectations:
  • resources/ui-helper.js:

(window.UIHelper.tapAt.return.new.Promise):
(window.UIHelper.tapAt):
(window.UIHelper.stylusTapAt.return.new.Promise):
(window.UIHelper.stylusTapAt):

Location:
trunk
Files:
7 added
32 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r241278 r241282  
     12019-02-11  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Mouse/Touch/Pointer events are missing modifier keys
     4        https://bugs.webkit.org/show_bug.cgi?id=191446
     5        <rdar://problem/45929460>
     6
     7        Reviewed by Tim Horton.
     8
     9        Refactor existing iOS key events tests to share code. Add new tests to ensure touch and mouse events
     10        have accurate modifier key details.
     11
     12        * fast/events/ios/key-events-meta-alt-combinations.html:
     13        * fast/events/ios/resources/key-tester.js:
     14        (computeSubsets.compareByModifierOrder): Deleted.
     15        * fast/events/resources/compute-subsets.js: Added.
     16        (computeSubsets.compareByOriginalArrayOrder):
     17        (computeSubsets):
     18        * fast/events/touch/ios/mouse-events-with-modifiers-expected.txt: Added.
     19        * fast/events/touch/ios/mouse-events-with-modifiers.html: Added.
     20        * fast/events/touch/ios/pointer-events-with-modifiers-expected.txt: Added.
     21        * fast/events/touch/ios/pointer-events-with-modifiers.html: Added.
     22        * fast/events/touch/ios/touch-events-with-modifiers-expected.txt: Added.
     23        * fast/events/touch/ios/touch-events-with-modifiers.html: Added.
     24        * http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt: Update expected result
     25        due to changes to ui-helper.js.
     26        * http/tests/security/anchor-download-block-crossorigin-expected.txt: Ditto.
     27        * platform/ios/TestExpectations:
     28        * resources/ui-helper.js:
     29        (window.UIHelper.tapAt.return.new.Promise):
     30        (window.UIHelper.tapAt):
     31        (window.UIHelper.stylusTapAt.return.new.Promise):
     32        (window.UIHelper.stylusTapAt):
     33
    1342019-02-11  Commit Queue  <commit-queue@webkit.org>
    235
  • trunk/LayoutTests/fast/events/ios/key-events-meta-alt-combinations.html

    r241278 r241282  
    33<head>
    44<script src="../../../resources/ui-helper.js"></script>
     5<script src="../resources/compute-subsets.js"></script>
    56<script src="resources/key-tester.js"></script>
    67<script>
    78const modiferKeySubsetsToTest = computeSubsets(["metaKey", "altKey"]);
    89for (const k of keysExcludingDeadAndSkippedKeys) {
    9     for (const modifiers of modiferKeySubsetsToTest)
     10    for (const modifiers of computeSubsets(modifierKeys))
    1011        tests.push(new KeyCommand(k, modifiers));
    1112}
  • trunk/LayoutTests/fast/events/ios/resources/key-tester.js

    r241278 r241282  
    5454}
    5555
    56 // This algorithm runs in O(2^N).
    57 function computeSubsets(anArray)
    58 {
    59     function compareByModifierOrder(a, b) {
    60         if (a.length < b.length)
    61             return -1;
    62         if (a.length > b.length)
    63             return 1;
    64         for (let i = 0; i < a.length; ++i) {
    65             let rankA = anArray.indexOf(a[i]);
    66             let rankB = anArray.indexOf(b[i]);
    67             if (rankA < rankB)
    68                 return -1;
    69             if (rankA > rankB)
    70                 return 1;
    71         }
    72         return 0;
    73     }
    74     let result = [];
    75     const numberOfNonEmptyPermutations = (1 << anArray.length) - 1;
    76     // For each ordinal 1, 2, ... numberOfNonEmptyPermutations we look at its binary representation
    77     // and generate a permutation that consists of the entries in anArray at the indices where there
    78     // is a one bit in the binary representation. For example, suppose anArray = ["metaKey", "altKey", "ctrlKey"].
    79     // To generate the 5th permutation we look at the binary representation of i = 5 => 0b101. And
    80     // compute the permutation to be [ anArray[0], anArray[2] ] = [ "metaKey", "ctrlKey" ] because
    81     // the 0th and 2nd bits are ones in the binary representation.
    82     for (let i = 1; i <= numberOfNonEmptyPermutations; ++i) {
    83         let temp = [];
    84         for (let bitmask = i, j = 0; bitmask; bitmask = Math.floor(bitmask / 2), ++j) {
    85             if (bitmask % 2)
    86                 temp.push(anArray[j]);
    87         }
    88         result.push(temp);
    89     }
    90     return result.sort(compareByModifierOrder);
    91 }
    92 
    9356const keys = new Set("abcdefghijklmnopqrstuvwxyz0123456789-=[]\\;',./".split(""));
    9457const deadKeys = new Set("`euin".split(""));
     
    11881
    11982const modifierKeys = ["metaKey", "altKey", "ctrlKey", "shiftKey"];
    120 const modiferKeySubsets = computeSubsets(modifierKeys);
    12183
    12284let tests = [];
  • trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt

    r241278 r241282  
    1 CONSOLE MESSAGE: line 107: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
    2 CONSOLE MESSAGE: line 107: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
    3 CONSOLE MESSAGE: line 107: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    4 CONSOLE MESSAGE: line 107: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    5 CONSOLE MESSAGE: line 107: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    6 CONSOLE MESSAGE: line 107: adddestination could not be converted to a valid HTTP-family URL.
    7 CONSOLE MESSAGE: line 107: adddestination could not be converted to a valid HTTP-family URL.
    8 CONSOLE MESSAGE: line 107: adddestination could not be converted to a valid HTTP-family URL.
    9 CONSOLE MESSAGE: line 107: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
    10 CONSOLE MESSAGE: line 107: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
     1CONSOLE MESSAGE: line 108: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
     2CONSOLE MESSAGE: line 108: adcampaignid must have a non-negative value less than 64 for Ad Click Attribution.
     3CONSOLE MESSAGE: line 108: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     4CONSOLE MESSAGE: line 108: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     5CONSOLE MESSAGE: line 108: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     6CONSOLE MESSAGE: line 108: adddestination could not be converted to a valid HTTP-family URL.
     7CONSOLE MESSAGE: line 108: adddestination could not be converted to a valid HTTP-family URL.
     8CONSOLE MESSAGE: line 108: adddestination could not be converted to a valid HTTP-family URL.
     9CONSOLE MESSAGE: line 108: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
     10CONSOLE MESSAGE: line 108: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
    1111Test for validity of ad click attribution attributes on anchor tags.
    1212
  • trunk/LayoutTests/http/tests/security/anchor-download-block-crossorigin-expected.txt

    r241278 r241282  
    1 CONSOLE MESSAGE: line 107: The download attribute on anchor was ignored because its href URL has a different security origin.
     1CONSOLE MESSAGE: line 108: The download attribute on anchor was ignored because its href URL has a different security origin.
    22Tests that the download attribute is ignored if the link is cross origin.
    33
  • trunk/LayoutTests/platform/ios/TestExpectations

    r241278 r241282  
    32113211webkit.org/b/153337 pageoverlay/overlay-large-document-scrolled.html [ Pass Failure ]
    32123212webkit.org/b/153337 pageoverlay/overlay-large-document.html [ Pass Failure ]
     3213
     3214# FIXME: Unskip the following test once we have the fix for <rdar://problem/45970040>.
     3215fast/events/touch/ios/touch-events-with-modifiers.html [ Skip ]
     3216fast/events/touch/ios/mouse-events-with-modifiers.html [ Skip ]
     3217fast/events/touch/ios/pointer-events-with-modifiers.html [ Skip ]
  • trunk/LayoutTests/resources/ui-helper.js

    r241278 r241282  
    3030    }
    3131
    32     static tapAt(x, y)
     32    static tapAt(x, y, modifiers=[])
    3333    {
    3434        console.assert(this.isIOS());
    3535
    3636        if (!this.isWebKit2()) {
     37            console.assert(!modifiers || !modifiers.length);
    3738            eventSender.addTouchPoint(x, y);
    3839            eventSender.touchStart();
     
    4445        return new Promise((resolve) => {
    4546            testRunner.runUIScript(`
    46                 uiController.singleTapAtPoint(${x}, ${y}, function() {
     47                uiController.singleTapAtPointWithModifiers(${x}, ${y}, ${JSON.stringify(modifiers)}, function() {
    4748                    uiController.uiScriptComplete();
    4849                });`, resolve);
     
    566567    }
    567568
    568     static stylusTapAt(x, y)
     569    static stylusTapAt(x, y, modifiers=[])
    569570    {
    570571        if (!this.isWebKit2())
     
    573574        return new Promise((resolve) => {
    574575            testRunner.runUIScript(`
    575                 uiController.stylusTapAtPoint(${x}, ${y}, 2, 1, 0.5, function() {
     576                uiController.stylusTapAtPointWithModifiers(${x}, ${y}, 2, 1, 0.5, ${JSON.stringify(modifiers)}, function() {
    576577                    uiController.uiScriptComplete();
    577578                });`, resolve);
  • trunk/Source/WebCore/ChangeLog

    r241281 r241282  
     12019-02-11  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Mouse/Touch/Pointer events are missing modifier keys
     4        https://bugs.webkit.org/show_bug.cgi?id=191446
     5        <rdar://problem/45929460>
     6
     7        Reviewed by Tim Horton.
     8
     9        Extract the modifier flags from the WebEvent. This code is only used by Legacy WebKit
     10        on iOS and we will need to fix <rdar://problem/47929759> in order for modifier flags
     11        to be passed to WebKit.
     12
     13        Tests: fast/events/touch/ios/mouse-events-with-modifiers.html
     14               fast/events/touch/ios/pointer-events-with-modifiers.html
     15               fast/events/touch/ios/touch-events-with-modifiers.html
     16
     17        * platform/ios/PlatformEventFactoryIOS.mm:
     18        (WebCore::PlatformMouseEventBuilder::PlatformMouseEventBuilder):
     19        * platform/ios/WebEvent.h:
     20        * platform/ios/WebEvent.mm:
     21        (-[WebEvent initWithMouseEventType:timeStamp:location:]):
     22        (-[WebEvent initWithMouseEventType:timeStamp:location:modifiers:]):
     23
    1242019-02-11  Jer Noble  <jer.noble@apple.com>
    225
  • trunk/Source/WebCore/platform/ios/PlatformEventFactoryIOS.mm

    r241278 r241282  
    9696        m_button = LeftButton; // This has always been the LeftButton on iOS.
    9797        m_clickCount = 1; // This has always been 1 on iOS.
     98        m_modifiers = modifiersForEvent(event);
    9899    }
    99100};
  • trunk/Source/WebCore/platform/ios/WebEvent.h

    r241278 r241282  
    140140}
    141141
     142// Deprecated. Remove once UIKit adopts -initWithMouseEventType taking modifiers.
    142143- (WebEvent *)initWithMouseEventType:(WebEventType)type
    143144                           timeStamp:(CFTimeInterval)timeStamp
    144145                            location:(CGPoint)point;
     146
     147- (WebEvent *)initWithMouseEventType:(WebEventType)type timeStamp:(CFTimeInterval)timeStamp location:(CGPoint)point modifiers:(WebEventFlags)modifiers;
    145148
    146149- (WebEvent *)initWithScrollWheelEventWithTimeStamp:(CFTimeInterval)timeStamp
  • trunk/Source/WebCore/platform/ios/WebEvent.mm

    r241278 r241282  
    5050@synthesize wasHandled = _wasHandled;
    5151
    52 - (WebEvent *)initWithMouseEventType:(WebEventType)type
    53                            timeStamp:(CFTimeInterval)timeStamp
    54                             location:(CGPoint)point
     52- (WebEvent *)initWithMouseEventType:(WebEventType)type timeStamp:(CFTimeInterval)timeStamp location:(CGPoint)point
     53{
     54    return [self initWithMouseEventType:type timeStamp:timeStamp location:point modifiers:0];
     55}
     56
     57- (WebEvent *)initWithMouseEventType:(WebEventType)type timeStamp:(CFTimeInterval)timeStamp location:(CGPoint)point modifiers:(WebEventFlags)modifiers
    5558{
    5659    self = [super init];
    5760    if (!self)
    5861        return nil;
    59    
    6062    _type = type;
    6163    _timestamp = timeStamp;
    62 
    6364    _locationInWindow = point;
    64    
     65    _modifierFlags = modifiers;
    6566    return self;
    6667}
  • trunk/Source/WebKit/ChangeLog

    r241281 r241282  
     12019-02-11  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Mouse/Touch/Pointer events are missing modifier keys
     4        https://bugs.webkit.org/show_bug.cgi?id=191446
     5        <rdar://problem/45929460>
     6
     7        Reviewed by Tim Horton.
     8
     9        Make use of UIKit SPI to retreive the modifier flags when dispatching mouse and touch events.
     10        Add new WebKit SPI for iOS, -[WKNavigationAction modifierFlags], to retrieve the the modifier
     11        flags held when a navigation action was initiated.
     12
     13        * Platform/spi/ios/UIKitSPI.h: Expose SPI.
     14        * Shared/NativeWebTouchEvent.h: Re-arrange macro guards so that we can expose the helper function
     15        WebKit::webEventModifierFlags(). This is a bit more involved that usual since this header is included
     16        from both C++ and Objective-C source files. It only makes sense to expose this function when
     17        compiling as part of an Objective-C source file.
     18        * Shared/ios/NativeWebTouchEventIOS.mm:
     19        (WebKit::NativeWebTouchEvent::NativeWebTouchEvent): Modified to take the modifier flags held down
     20        when the platform touch event was received and pass them through to the base constructor.
     21        (WebKit::webEventModifierFlags): Added. Converts from the platform-speciifc UIKeyModifierFlags to
     22        OptionSet<WebKit::WebEvent::Modifier>.
     23        * Shared/ios/WebIOSEventFactory.h:
     24        * Shared/ios/WebIOSEventFactory.mm:
     25        (WebIOSEventFactory::toUIKeyModifierFlags): Added. Converts from OptionSet<WebKit::WebEvent::Modifier>
     26        to the platform-specific UIKeyModifierFlags.
     27        * UIProcess/API/Cocoa/WKNavigationAction.mm:
     28        (-[WKNavigationAction modifierFlags]): Added.
     29        * UIProcess/API/Cocoa/WKNavigationActionPrivate.h:
     30        * UIProcess/WebPageProxy.h:
     31        * UIProcess/ios/WKContentViewInteraction.h:
     32        * UIProcess/ios/WKContentViewInteraction.mm:
     33        (gestureRecognizerModifierFlags): Added.
     34
     35        (-[WKContentView _webTouchEventsRecognized:]):
     36        (-[WKContentView _highlightLongPressRecognized:]):
     37        (-[WKContentView _twoFingerSingleTapGestureRecognized:]):
     38        (-[WKContentView _singleTapCommited:]):
     39        Pass modifier flags through.
     40
     41        (-[WKContentView _attemptClickAtLocation:modifierFlags:]): Added.
     42        (-[WKContentView actionSheetAssistant:openElementAtLocation:]): This is invoked when a person opens a link
     43        via the action sheet. We don't have access to the modifier flags to pass. It also seems like an implementation
     44        detail that this action is implemented via mouse click and we should re-evaluate this decision in light of
     45        the fact tht the action sheet is browser UI and we tend to be very reserved on what UI actions are visible
     46        to the page. On Mac, opening a link via the context menu is not visible to the page, at least from a mouse
     47        event perspective.
     48        (webEventFlagsForUIKeyModifierFlags): Added.
     49        (-[WKContentView _hoverGestureRecognizerChanged:]): Pass modifier flags through.
     50        (-[WKContentView _attemptClickAtLocation:]): Deleted.
     51        * UIProcess/ios/WebPageProxyIOS.mm:
     52        (WebKit::WebPageProxy::handleTwoFingerTapAtPoint):
     53        (WebKit::WebPageProxy::commitPotentialTap):
     54        (WebKit::WebPageProxy::handleTap):
     55        * WebProcess/WebPage/WebPage.h:
     56        * WebProcess/WebPage/WebPage.messages.in:
     57        * WebProcess/WebPage/ios/WebPageIOS.mm:
     58        (WebKit::WebPage::handleSyntheticClick):
     59        (WebKit::WebPage::completePendingSyntheticClickForContentChangeObserver):
     60        (WebKit::WebPage::completeSyntheticClick):
     61        (WebKit::WebPage::handleTap):
     62        (WebKit::WebPage::handleTwoFingerTapAtPoint):
     63        (WebKit::WebPage::commitPotentialTap):
     64        Pass modifier flags through.
     65
    1662019-02-11  Jer Noble  <jer.noble@apple.com>
    267
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r241278 r241282  
    10381038@end
    10391039
     1040@interface UIGestureRecognizer (Staging_45970040)
     1041@property (nonatomic, readonly, getter=_modifierFlags) UIKeyModifierFlags modifierFlags;
     1042@end
     1043
    10401044@interface UIPhysicalKeyboardEvent : UIPressesEvent
    10411045@end
  • trunk/Source/WebKit/Shared/NativeWebTouchEvent.h

    r241278 r241282  
    2424 */
    2525
    26 #ifndef NativeWebTouchEvent_h
    27 #define NativeWebTouchEvent_h
     26#pragma once
     27
     28#include "WebEvent.h"
    2829
    2930#if ENABLE(TOUCH_EVENTS)
    3031
    31 #include "WebEvent.h"
    32 
    3332#if PLATFORM(IOS_FAMILY)
     33#if defined(__OBJC__)
     34#include <UIKit/UIKit.h>
    3435struct _UIWebTouchEvent;
     36#endif
    3537#elif PLATFORM(GTK)
    3638#include <WebCore/GUniquePtrGtk.h>
     
    3941#endif
    4042
     43#endif // ENABLE(TOUCH_EVENTS)
     44
    4145namespace WebKit {
     46
     47#if ENABLE(TOUCH_EVENTS)
    4248
    4349class NativeWebTouchEvent : public WebTouchEvent {
    4450public:
    4551#if PLATFORM(IOS_FAMILY)
    46     explicit NativeWebTouchEvent(const _UIWebTouchEvent*);
     52#if defined(__OBJC__)
     53    explicit NativeWebTouchEvent(const _UIWebTouchEvent*, UIKeyModifierFlags);
     54#endif
    4755#elif PLATFORM(GTK)
    4856    NativeWebTouchEvent(GdkEvent*, Vector<WebPlatformTouchPoint>&&);
     
    5765
    5866private:
    59 #if PLATFORM(IOS_FAMILY)
     67#if PLATFORM(IOS_FAMILY) && defined(__OBJC__)
    6068    Vector<WebPlatformTouchPoint> extractWebTouchPoint(const _UIWebTouchEvent*);
    6169#endif
     
    6876};
    6977
    70 } // namespace WebKit
    71 
    7278#endif // ENABLE(TOUCH_EVENTS)
    7379
    74 #endif // NativeWebTouchEvent_h
     80#if PLATFORM(IOS_FAMILY) && defined(__OBJC__)
     81OptionSet<WebEvent::Modifier> webEventModifierFlags(UIKeyModifierFlags);
     82#endif
     83
     84} // namespace WebKit
  • trunk/Source/WebKit/Shared/ios/NativeWebTouchEventIOS.mm

    r241278 r241282  
    2727#import "NativeWebTouchEvent.h"
    2828
    29 #if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
     29#if PLATFORM(IOS_FAMILY)
    3030
    3131#import "UIKitSPI.h"
    32 #import "WebEvent.h"
    3332#import <UIKit/UITouch.h>
    3433#import <WebCore/IntPoint.h>
     
    3635
    3736namespace WebKit {
     37
     38#if ENABLE(TOUCH_EVENTS)
    3839
    3940static inline WebEvent::Type webEventTypeForUIWebTouchEventType(UIWebTouchEventType type)
     
    118119}
    119120
    120 NativeWebTouchEvent::NativeWebTouchEvent(const _UIWebTouchEvent* event)
     121NativeWebTouchEvent::NativeWebTouchEvent(const _UIWebTouchEvent* event, UIKeyModifierFlags flags)
    121122    : WebTouchEvent(
    122123        webEventTypeForUIWebTouchEventType(event->type),
    123         OptionSet<Modifier> { },
     124        webEventModifierFlags(flags),
    124125        WallTime::fromRawSeconds(event->timestamp),
    125126        extractWebTouchPoint(event),
     
    136137}
    137138
     139#endif // ENABLE(TOUCH_EVENTS)
     140
     141OptionSet<WebEvent::Modifier> webEventModifierFlags(UIKeyModifierFlags flags)
     142{
     143    OptionSet<WebEvent::Modifier> modifiers;
     144    if (flags & UIKeyModifierShift)
     145        modifiers.add(WebEvent::Modifier::ShiftKey);
     146    if (flags & UIKeyModifierControl)
     147        modifiers.add(WebEvent::Modifier::ControlKey);
     148    if (flags & UIKeyModifierAlternate)
     149        modifiers.add(WebEvent::Modifier::AltKey);
     150    if (flags & UIKeyModifierCommand)
     151        modifiers.add(WebEvent::Modifier::MetaKey);
     152    if (flags & UIKeyModifierAlphaShift)
     153        modifiers.add(WebEvent::Modifier::CapsLockKey);
     154    return modifiers;
     155}
     156
    138157} // namespace WebKit
    139158
    140 #endif // PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
     159#endif // PLATFORM(IOS_FAMILY)
  • trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.h

    r241278 r241282  
    2424 */
    2525
    26 #ifndef WebIOSEventFactory_h
    27 #define WebIOSEventFactory_h
     26#pragma once
    2827
    2928#if PLATFORM(IOS_FAMILY)
    3029
    3130#import "WebEvent.h"
     31#import <UIKit/UIKit.h>
    3232#import <WebCore/WebEvent.h>
    3333
     
    3636    static WebKit::WebKeyboardEvent createWebKeyboardEvent(::WebEvent *);
    3737    static WebKit::WebMouseEvent createWebMouseEvent(::WebEvent *);
     38
     39    static UIKeyModifierFlags toUIKeyModifierFlags(OptionSet<WebKit::WebEvent::Modifier>);
    3840};
    3941
    4042#endif // PLATFORM(IOS_FAMILY)
    41 
    42 #endif // WebIOSEventFactory_h
  • trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.mm

    r241278 r241282  
    3131#import <WebCore/KeyEventCodesIOS.h>
    3232#import <WebCore/PlatformEventFactoryIOS.h>
     33
     34UIKeyModifierFlags WebIOSEventFactory::toUIKeyModifierFlags(OptionSet<WebKit::WebEvent::Modifier> modifiers)
     35{
     36    UIKeyModifierFlags modifierFlags = 0;
     37    if (modifiers.contains(WebKit::WebEvent::Modifier::ShiftKey))
     38        modifierFlags |= UIKeyModifierShift;
     39    if (modifiers.contains(WebKit::WebEvent::Modifier::ControlKey))
     40        modifierFlags |= UIKeyModifierControl;
     41    if (modifiers.contains(WebKit::WebEvent::Modifier::AltKey))
     42        modifierFlags |= UIKeyModifierAlternate;
     43    if (modifiers.contains(WebKit::WebEvent::Modifier::MetaKey))
     44        modifierFlags |= UIKeyModifierCommand;
     45    if (modifiers.contains(WebKit::WebEvent::Modifier::CapsLockKey))
     46        modifierFlags |= UIKeyModifierAlphaShift;
     47    return modifierFlags;
     48}
    3349
    3450static OptionSet<WebKit::WebEvent::Modifier> modifiersForEvent(::WebEvent *event)
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKNavigationAction.mm

    r241278 r241282  
    3737#import <wtf/RetainPtr.h>
    3838
     39#if PLATFORM(IOS_FAMILY)
     40#import "WebIOSEventFactory.h"
     41#endif
     42
    3943@implementation WKNavigationAction
    4044
     
    128132
    129133#if PLATFORM(MAC)
     134
    130135- (NSEventModifierFlags)modifierFlags
    131136{
     
    137142    return WebKit::WebEventFactory::toNSButtonNumber(_navigationAction->mouseButton());
    138143}
     144
     145#else
     146
     147- (UIKeyModifierFlags)modifierFlags
     148{
     149    return WebIOSEventFactory::toUIKeyModifierFlags(_navigationAction->modifiers());
     150}
     151
    139152#endif
    140153
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKNavigationActionPrivate.h

    r241278 r241282  
    3232
    3333#if TARGET_OS_IPHONE
     34#include <UIKit/UIKit.h>
     35
    3436typedef NS_ENUM(NSInteger, WKSyntheticClickType) {
    3537    WKSyntheticClickTypeNoTap,
     
    5456@property (nonatomic, readonly) WKSyntheticClickType _syntheticClickType WK_API_AVAILABLE(ios(10.0));
    5557@property (nonatomic, readonly) CGPoint _clickLocationInRootViewCoordinates WK_API_AVAILABLE(ios(11.0));
     58
     59@property (nonatomic, readonly) UIKeyModifierFlags modifierFlags WK_API_AVAILABLE(ios(WK_IOS_TBA));
    5660#endif
    5761
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r241278 r241282  
    8181#include <WebCore/MediaPlaybackTargetContext.h>
    8282#include <WebCore/MediaProducer.h>
     83#include <WebCore/PlatformEvent.h>
    8384#include <WebCore/PlatformScreen.h>
    8485#include <WebCore/PointerID.h>
     
    673674    void contentSizeCategoryDidChange(const String& contentSizeCategory);
    674675    void getSelectionContext(WTF::Function<void(const String&, const String&, const String&, CallbackBase::Error)>&&);
    675     void handleTwoFingerTapAtPoint(const WebCore::IntPoint&, uint64_t requestID);
     676    void handleTwoFingerTapAtPoint(const WebCore::IntPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t requestID);
    676677    void handleStylusSingleTapAtPoint(const WebCore::IntPoint&, uint64_t requestID);
    677678    void setForceAlwaysUserScalable(bool);
     
    11721173
    11731174    void potentialTapAtPosition(const WebCore::FloatPoint&, uint64_t& requestID);
    1174     void commitPotentialTap(uint64_t layerTreeTransactionIdAtLastTouchStart);
     1175    void commitPotentialTap(OptionSet<WebKit::WebEvent::Modifier>, uint64_t layerTreeTransactionIdAtLastTouchStart);
    11751176    void cancelPotentialTap();
    11761177    void tapHighlightAtPosition(const WebCore::FloatPoint&, uint64_t& requestID);
    1177     void handleTap(const WebCore::FloatPoint&, uint64_t layerTreeTransactionIdAtLastTouchStart);
     1178    void handleTap(const WebCore::FloatPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t layerTreeTransactionIdAtLastTouchStart);
    11781179
    11791180    void inspectorNodeSearchMovedToPosition(const WebCore::FloatPoint&);
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r241278 r241282  
    407407- (BOOL)_interpretKeyEvent:(::WebEvent *)theEvent isCharEvent:(BOOL)isCharEvent;
    408408- (void)_positionInformationDidChange:(const WebKit::InteractionInformationAtPosition&)info;
    409 - (void)_attemptClickAtLocation:(CGPoint)location;
     409- (void)_attemptClickAtLocation:(CGPoint)location modifierFlags:(UIKeyModifierFlags)modifierFlags;
    410410- (void)_willStartScrollingOrZooming;
    411411- (void)_didScroll;
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r241278 r241282  
    11611161#endif
    11621162
     1163inline static UIKeyModifierFlags gestureRecognizerModifierFlags(UIGestureRecognizer *recognizer)
     1164{
     1165    return [recognizer respondsToSelector:@selector(_modifierFlags)] ? recognizer.modifierFlags : 0;
     1166}
     1167
    11631168- (void)_webTouchEventsRecognized:(UIWebTouchEventsGestureRecognizer *)gestureRecognizer
    11641169{
     
    11731178
    11741179#if ENABLE(TOUCH_EVENTS)
    1175     WebKit::NativeWebTouchEvent nativeWebTouchEvent(lastTouchEvent);
     1180    WebKit::NativeWebTouchEvent nativeWebTouchEvent { lastTouchEvent, gestureRecognizerModifierFlags(gestureRecognizer) };
    11761181    nativeWebTouchEvent.setCanPreventNativeGestures(!_canSendTouchEventsAsynchronously || [gestureRecognizer isDefaultPrevented]);
    11771182
     
    20562061    case UIGestureRecognizerStateEnded:
    20572062        if (_highlightLongPressCanClick && _positionInformation.isElement) {
    2058             [self _attemptClickAtLocation:[gestureRecognizer startPoint]];
     2063            [self _attemptClickAtLocation:gestureRecognizer.startPoint modifierFlags:gestureRecognizerModifierFlags(gestureRecognizer)];
    20592064            [self _finishInteraction];
    20602065        } else
     
    20752080    _isTapHighlightIDValid = YES;
    20762081    _isExpectingFastSingleTapCommit = YES;
    2077     _page->handleTwoFingerTapAtPoint(WebCore::roundedIntPoint(gestureRecognizer.centroid), ++_latestTapID);
     2082    _page->handleTwoFingerTapAtPoint(WebCore::roundedIntPoint(gestureRecognizer.centroid), WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), ++_latestTapID);
    20782083}
    20792084
     
    21972202
    21982203    [_inputPeripheral endEditing];
    2199     _page->commitPotentialTap(_layerTreeTransactionIdAtLastTouchStart);
     2204    _page->commitPotentialTap(WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), _layerTreeTransactionIdAtLastTouchStart);
    22002205
    22012206    if (!_isExpectingFastSingleTapCommit)
     
    22302235}
    22312236
    2232 - (void)_attemptClickAtLocation:(CGPoint)location
     2237- (void)_attemptClickAtLocation:(CGPoint)location modifierFlags:(UIKeyModifierFlags)modifierFlags
    22332238{
    22342239    if (![self isFirstResponder]) {
     
    22392244
    22402245    [_inputPeripheral endEditing];
    2241     _page->handleTap(location, _layerTreeTransactionIdAtLastTouchStart);
     2246    _page->handleTap(location, WebKit::webEventModifierFlags(modifierFlags), _layerTreeTransactionIdAtLastTouchStart);
    22422247}
    22432248
     
    54125417- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location
    54135418{
    5414     [self _attemptClickAtLocation:location];
     5419    [self _attemptClickAtLocation:location modifierFlags:0];
    54155420}
    54165421
     
    64196424}
    64206425
     6426static WebEventFlags webEventFlagsForUIKeyModifierFlags(UIKeyModifierFlags flags)
     6427{
     6428    WebEventFlags eventFlags = 0;
     6429    if (flags & UIKeyModifierShift)
     6430        eventFlags |= WebEventFlagMaskLeftShiftKey;
     6431    if (flags & UIKeyModifierControl)
     6432        eventFlags |= WebEventFlagMaskLeftControlKey;
     6433    if (flags & UIKeyModifierAlternate)
     6434        eventFlags |= WebEventFlagMaskLeftOptionKey;
     6435    if (flags & UIKeyModifierCommand)
     6436        eventFlags |= WebEventFlagMaskLeftCommandKey;
     6437    if (flags & UIKeyModifierAlphaShift)
     6438        eventFlags |= WebEventFlagMaskLeftCapsLockKey;
     6439    return eventFlags;
     6440}
     6441
    64216442- (void)_hoverGestureRecognizerChanged:(UIGestureRecognizer *)gestureRecognizer
    64226443{
     
    64406461    }
    64416462
    6442     auto event = adoptNS([[::WebEvent alloc] initWithMouseEventType:WebEventMouseMoved timeStamp:timestamp location:point]);
     6463    auto event = adoptNS([[::WebEvent alloc] initWithMouseEventType:WebEventMouseMoved timeStamp:timestamp location:point modifiers:webEventFlagsForUIKeyModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer))]);
    64436464    _page->handleMouseEvent(WebKit::NativeWebMouseEvent(event.get()));
    64446465}
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r241278 r241282  
    597597}
    598598
    599 void WebPageProxy::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, uint64_t requestID)
    600 {
    601     process().send(Messages::WebPage::HandleTwoFingerTapAtPoint(point, requestID), m_pageID);
     599void WebPageProxy::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, uint64_t requestID)
     600{
     601    process().send(Messages::WebPage::HandleTwoFingerTapAtPoint(point, modifiers, requestID), m_pageID);
    602602}
    603603
     
    828828}
    829829
    830 void WebPageProxy::commitPotentialTap(uint64_t layerTreeTransactionIdAtLastTouchStart)
    831 {
    832     process().send(Messages::WebPage::CommitPotentialTap(layerTreeTransactionIdAtLastTouchStart), m_pageID);
     830void WebPageProxy::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, uint64_t layerTreeTransactionIdAtLastTouchStart)
     831{
     832    process().send(Messages::WebPage::CommitPotentialTap(modifiers, layerTreeTransactionIdAtLastTouchStart), m_pageID);
    833833}
    834834
     
    843843}
    844844
    845 void WebPageProxy::handleTap(const FloatPoint& location, uint64_t layerTreeTransactionIdAtLastTouchStart)
    846 {
    847     process().send(Messages::WebPage::HandleTap(roundedIntPoint(location), layerTreeTransactionIdAtLastTouchStart), m_pageID);
     845void WebPageProxy::handleTap(const FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, uint64_t layerTreeTransactionIdAtLastTouchStart)
     846{
     847    process().send(Messages::WebPage::HandleTap(roundedIntPoint(location), modifiers, layerTreeTransactionIdAtLastTouchStart), m_pageID);
    848848}
    849849
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r241281 r241282  
    614614    bool hasStablePageScaleFactor() const { return m_hasStablePageScaleFactor; }
    615615
    616     void handleTap(const WebCore::IntPoint&, uint64_t lastLayerTreeTransactionId);
     616    void handleTap(const WebCore::IntPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t lastLayerTreeTransactionId);
    617617    void potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint&);
    618     void commitPotentialTap(uint64_t lastLayerTreeTransactionId);
     618    void commitPotentialTap(OptionSet<WebKit::WebEvent::Modifier>, uint64_t lastLayerTreeTransactionId);
    619619    void commitPotentialTapFailed();
    620620    void cancelPotentialTap();
     
    662662    void updateSelectionAppearance();
    663663    void getSelectionContext(CallbackID);
    664     void handleTwoFingerTapAtPoint(const WebCore::IntPoint&, uint64_t requestID);
     664    void handleTwoFingerTapAtPoint(const WebCore::IntPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t requestID);
    665665    void handleStylusSingleTapAtPoint(const WebCore::IntPoint&, uint64_t requestID);
    666666    void getRectsForGranularityWithSelectionOffset(uint32_t, int32_t, CallbackID);
     
    11851185    void getFocusedElementInformation(FocusedElementInformation&);
    11861186    void platformInitializeAccessibility();
    1187     void handleSyntheticClick(WebCore::Node* nodeRespondingToClick, const WebCore::FloatPoint& location);
    1188     void completeSyntheticClick(WebCore::Node* nodeRespondingToClick, const WebCore::FloatPoint& location, WebCore::SyntheticClickType);
     1187    void handleSyntheticClick(WebCore::Node* nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebKit::WebEvent::Modifier>);
     1188    void completeSyntheticClick(WebCore::Node* nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebKit::WebEvent::Modifier>, WebCore::SyntheticClickType);
    11891189    void sendTapHighlightForNodeIfNecessary(uint64_t requestID, WebCore::Node*);
    11901190    void resetTextAutosizing();
     
    17681768    WebCore::FloatPoint m_pendingSyntheticClickLocation;
    17691769    WebCore::FloatRect m_previousExposedContentRect;
     1770    OptionSet<WebKit::WebEvent::Modifier> m_pendingSyntheticClickModifiers;
    17701771    FocusedElementIdentifier m_currentFocusedElementIdentifier { 0 };
    17711772    Optional<DynamicViewportSizeUpdateID> m_pendingDynamicViewportSizeUpdateID;
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r241278 r241282  
    5252    DynamicViewportSizeUpdate(WebCore::FloatSize viewLayoutSize, WebCore::FloatSize maximumUnobscuredSize, WebCore::FloatRect targetExposedContentRect, WebCore::FloatRect targetUnobscuredRect, WebCore::FloatRect targetUnobscuredRectInScrollViewCoordinates, WebCore::RectEdges<float> targetUnobscuredSafeAreaInsets, double scale, int32_t deviceOrientation, uint64_t dynamicViewportSizeUpdateID)
    5353
    54     HandleTap(WebCore::IntPoint point, uint64_t lastLayerTreeTransactionId)
     54    HandleTap(WebCore::IntPoint point, OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
    5555    PotentialTapAtPosition(uint64_t requestID, WebCore::FloatPoint point)
    56     CommitPotentialTap(uint64_t lastLayerTreeTransactionId)
     56    CommitPotentialTap(OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
    5757    CancelPotentialTap()
    5858    TapHighlightAtPosition(uint64_t requestID, WebCore::FloatPoint point)
     
    9999    GetSelectionContext(WebKit::CallbackID callbackID)
    100100    SetAllowsMediaDocumentInlinePlayback(bool allows)
    101     HandleTwoFingerTapAtPoint(WebCore::IntPoint point, uint64_t requestID)
     101    HandleTwoFingerTapAtPoint(WebCore::IntPoint point, OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t requestID)
    102102    HandleStylusSingleTapAtPoint(WebCore::IntPoint point, uint64_t requestID)
    103103    SetForceAlwaysUserScalable(bool userScalable)
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r241281 r241282  
    528528}
    529529
    530 void WebPage::handleSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location)
     530void WebPage::handleSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers)
    531531{
    532532    IntPoint roundedAdjustedPoint = roundedIntPoint(location);
     
    537537    WKStartObservingDOMTimerScheduling();
    538538
    539     mainframe.eventHandler().mouseMoved(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, WebCore::NoTap));
     539    // FIXME: Pass caps lock state.
     540    bool shiftKey = modifiers.contains(WebEvent::Modifier::ShiftKey);
     541    bool ctrlKey = modifiers.contains(WebEvent::Modifier::ControlKey);
     542    bool altKey = modifiers.contains(WebEvent::Modifier::AltKey);
     543    bool metaKey = modifiers.contains(WebEvent::Modifier::MetaKey);
     544    mainframe.eventHandler().mouseMoved(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, WebCore::NoTap));
    540545    mainframe.document()->updateStyleIfNeeded();
    541546
     
    545550    m_pendingSyntheticClickNode = nullptr;
    546551    m_pendingSyntheticClickLocation = FloatPoint();
     552    m_pendingSyntheticClickModifiers = { };
    547553
    548554    if (m_isClosed)
     
    554560        LOG(ContentObservation, "handleSyntheticClick: Observed meaningful visible change -> hover.");
    555561        return;
    556     case WKContentIndeterminateChange:
     562    case WKContentIndeterminateChange: {
    557563        // Wait for callback to completePendingSyntheticClickForContentChangeObserver() to decide whether to send the click event.
    558564        m_pendingSyntheticClickNode = nodeRespondingToClick;
    559565        m_pendingSyntheticClickLocation = location;
     566        m_pendingSyntheticClickModifiers = modifiers;
    560567        LOG(ContentObservation, "handleSyntheticClick: Observed some change, but can't decide it yet -> wait.");
    561568        return;
    562     case WKContentNoChange:
     569    } case WKContentNoChange:
    563570        LOG(ContentObservation, "handleSyntheticClick: No change was observed -> click.");
    564         completeSyntheticClick(nodeRespondingToClick, location, WebCore::OneFingerTap);
     571        completeSyntheticClick(nodeRespondingToClick, location, modifiers, WebCore::OneFingerTap);
    565572        return;
    566573    }
     
    576583    if (WKObservedContentChange() == WKContentNoChange) {
    577584        LOG(ContentObservation, "No chage was observed -> click.");
    578         completeSyntheticClick(m_pendingSyntheticClickNode.get(), m_pendingSyntheticClickLocation, WebCore::OneFingerTap);
     585        completeSyntheticClick(m_pendingSyntheticClickNode.get(), m_pendingSyntheticClickLocation, m_pendingSyntheticClickModifiers, WebCore::OneFingerTap);
    579586    } else
    580587        LOG(ContentObservation, "Observed meaningful visible change -> hover.");
     
    582589    m_pendingSyntheticClickNode = nullptr;
    583590    m_pendingSyntheticClickLocation = FloatPoint();
    584 }
    585 
    586 void WebPage::completeSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location, SyntheticClickType syntheticClickType)
     591    m_pendingSyntheticClickModifiers = { };
     592}
     593
     594void WebPage::completeSyntheticClick(Node* nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, SyntheticClickType syntheticClickType)
    587595{
    588596    IntPoint roundedAdjustedPoint = roundedIntPoint(location);
     
    597605    m_lastInteractionLocation = roundedAdjustedPoint;
    598606
    599     tapWasHandled |= mainframe.eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
     607    // FIXME: Pass caps lock state.
     608    bool shiftKey = modifiers.contains(WebEvent::Modifier::ShiftKey);
     609    bool ctrlKey = modifiers.contains(WebEvent::Modifier::ControlKey);
     610    bool altKey = modifiers.contains(WebEvent::Modifier::AltKey);
     611    bool metaKey = modifiers.contains(WebEvent::Modifier::MetaKey);
     612
     613    tapWasHandled |= mainframe.eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
    600614    if (m_isClosed)
    601615        return;
    602616
    603     tapWasHandled |= mainframe.eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
     617    tapWasHandled |= mainframe.eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), WebCore::ForceAtClick, syntheticClickType));
    604618    if (m_isClosed)
    605619        return;
     
    621635}
    622636
    623 void WebPage::handleTap(const IntPoint& point, uint64_t lastLayerTreeTransactionId)
     637void WebPage::handleTap(const IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
    624638{
    625639    FloatPoint adjustedPoint;
     
    638652#endif
    639653    else
    640         handleSyntheticClick(nodeRespondingToClick, adjustedPoint);
     654        handleSyntheticClick(nodeRespondingToClick, adjustedPoint, modifiers);
    641655}
    642656
     
    733747}
    734748
    735 void WebPage::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, uint64_t requestID)
     749void WebPage::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t requestID)
    736750{
    737751    FloatPoint adjustedPoint;
     
    749763    } else
    750764#endif
    751         completeSyntheticClick(nodeRespondingToClick, adjustedPoint, WebCore::TwoFingerTap);
     765        completeSyntheticClick(nodeRespondingToClick, adjustedPoint, modifiers, WebCore::TwoFingerTap);
    752766}
    753767
     
    794808}
    795809
    796 void WebPage::commitPotentialTap(uint64_t lastLayerTreeTransactionId)
     810void WebPage::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
    797811{
    798812    if (!m_potentialTapNode || (!m_potentialTapNode->renderer() && !is<HTMLAreaElement>(m_potentialTapNode.get()))) {
     
    818832        } else
    819833#endif
    820             handleSyntheticClick(nodeRespondingToClick, adjustedPoint);
     834            handleSyntheticClick(nodeRespondingToClick, adjustedPoint, modifiers);
    821835    } else
    822836        commitPotentialTapFailed();
  • trunk/Tools/ChangeLog

    r241278 r241282  
     12019-02-11  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Mouse/Touch/Pointer events are missing modifier keys
     4        https://bugs.webkit.org/show_bug.cgi?id=191446
     5        <rdar://problem/45929460>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add support infrastructure for testing touch and stylus taps when holding modifier keys.
     10
     11        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
     12        (WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
     13        (WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.
     14        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
     15        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
     16        (WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
     17        (WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.
     18        * TestRunnerShared/UIScriptContext/UIScriptController.h:
     19        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
     20        (WTR::arrayLength):
     21        (WTR::parseModifierArray):
     22        (WTR::UIScriptController::singleTapAtPoint): Implemented in terms of singleTapAtPointWithModifiers().
     23        (WTR::UIScriptController::singleTapAtPointWithModifiers): Added.
     24        (WTR::UIScriptController::stylusTapAtPoint): Implemented in terms of stylusTapAtPointWithModifiers().
     25        (WTR::UIScriptController::stylusTapAtPointWithModifiers): Added.
     26
    1272019-02-11  Commit Queue  <commit-queue@webkit.org>
    228
  • trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm

    r241278 r241282  
    116116}
    117117
     118void UIScriptController::singleTapAtPointWithModifiers(long x, long y, JSValueRef modifierArray, JSValueRef callback)
     119{
     120}
     121
    118122void UIScriptController::doubleTapAtPoint(long x, long y, JSValueRef callback)
    119123{
     
    141145
    142146void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
     147{
     148}
     149
     150void UIScriptController::stylusTapAtPointWithModifiers(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef modifierArray, JSValueRef callback)
    143151{
    144152}
  • trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl

    r241278 r241282  
    5757    void liftUpAtPoint(long x, long y, long touchCount, object callback);
    5858    void singleTapAtPoint(long x, long y, object callback);
     59    void singleTapAtPointWithModifiers(long x, long y, object modifierArray, object callback);
    5960    void doubleTapAtPoint(long x, long y, object callback);
    6061    void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, object callback);
     
    6667    void stylusUpAtPoint(long x, long y, object callback);
    6768    void stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object callback);
     69    void stylusTapAtPointWithModifiers(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object modifierArray, object callback);
    6870
    6971    void enterText(DOMString text);
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp

    r241278 r241282  
    266266}
    267267
     268void UIScriptController::singleTapAtPointWithModifiers(long x, long y, JSValueRef modifierArray, JSValueRef callback)
     269{
     270}
     271
    268272void UIScriptController::doubleTapAtPoint(long x, long y, JSValueRef)
    269273{
     
    294298}
    295299
     300void UIScriptController::stylusTapAtPointWithModifiers(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef modifierArray, JSValueRef callback)
     301{
     302}
     303
    296304void UIScriptController::sendEventStream(JSStringRef eventsJSON, JSValueRef callback)
    297305{
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h

    r241278 r241282  
    7979    void liftUpAtPoint(long x, long y, long touchCount, JSValueRef callback);
    8080    void singleTapAtPoint(long x, long y, JSValueRef callback);
     81    void singleTapAtPointWithModifiers(long x, long y, JSValueRef modifierArray, JSValueRef callback);
    8182    void doubleTapAtPoint(long x, long y, JSValueRef callback);
    8283    void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback);
     
    8687    void stylusUpAtPoint(long x, long y, JSValueRef callback);
    8788    void stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback);
     89    void stylusTapAtPointWithModifiers(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef modifierArray, JSValueRef callback);
    8890
    8991    void longPressAtPoint(long x, long y, JSValueRef callback);
  • trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

    r241278 r241282  
    6464    };
    6565}
    66    
    67 void UIScriptController::checkForOutstandingCallbacks()
    68 {
    69     if (![[HIDEventGenerator sharedHIDEventGenerator] checkForOutstandingCallbacks])
    70         [NSException raise:@"WebKitTestRunnerTestProblem" format:@"The test completed before all synthesized events had been handled. Perhaps you're calling notifyDone() too early?"];
    71 }
    72 
    73 void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
    74 {
    75     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    76 
    77     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    78     [webView _doAfterNextPresentationUpdate:^{
    79         if (!m_context)
    80             return;
    81         m_context->asyncTaskComplete(callbackID);
    82     }];
    83 }
    84 
    85 void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
    86 {
    87     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    88 
    89     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    90     [webView _doAfterNextStablePresentationUpdate:^() {
    91         if (m_context)
    92             m_context->asyncTaskComplete(callbackID);
    93     }];
    94 }
    95 
    96 void UIScriptController::doAfterVisibleContentRectUpdate(JSValueRef callback)
    97 {
    98     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    99 
    100     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    101     [webView _doAfterNextVisibleContentRectUpdate:^ {
    102         if (!m_context)
    103             return;
    104         m_context->asyncTaskComplete(callbackID);
    105     }];
    106 }
    107 
    108 void UIScriptController::zoomToScale(double scale, JSValueRef callback)
    109 {
    110     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    111 
    112     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    113 
    114     [webView zoomToScale:scale animated:YES completionHandler:^{
    115         if (!m_context)
    116             return;
    117         m_context->asyncTaskComplete(callbackID);
    118     }];
    119 }
    120 
    121 void UIScriptController::retrieveSpeakSelectionContent(JSValueRef callback)
    122 {
    123     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    124    
    125     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    126    
    127     [webView accessibilityRetrieveSpeakSelectionContentWithCompletionHandler:^() {
    128         if (!m_context)
    129             return;
    130         m_context->asyncTaskComplete(callbackID);
    131     }];
    132 }
    133 
    134 JSRetainPtr<JSStringRef> UIScriptController::accessibilitySpeakSelectionContent() const
    135 {
    136     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    137     return JSStringCreateWithCFString((CFStringRef)webView.accessibilitySpeakSelectionContent);
    138 }
    139 
    140 void UIScriptController::simulateAccessibilitySettingsChangeNotification(JSValueRef callback)
    141 {
    142     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    143 
    144     auto* webView = TestController::singleton().mainWebView()->platformView();
    145     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    146     [center postNotificationName:UIAccessibilityInvertColorsStatusDidChangeNotification object:webView];
    147 
    148     [webView _doAfterNextPresentationUpdate: ^{
    149         if (!m_context)
    150             return;
    151         m_context->asyncTaskComplete(callbackID);
    152     }];
    153 }
    154 
    155 double UIScriptController::zoomScale() const
    156 {
    157     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    158     return webView.scrollView.zoomScale;
    159 }
    160 
    161 static CGPoint globalToContentCoordinates(TestRunnerWKWebView *webView, long x, long y)
    162 {
    163     CGPoint point = CGPointMake(x, y);
    164     point = [webView _convertPointFromContentsToView:point];
    165     point = [webView convertPoint:point toView:nil];
    166     point = [webView.window convertPoint:point toWindow:nil];
    167     return point;
    168 }
    169 
    170 void UIScriptController::touchDownAtPoint(long x, long y, long touchCount, JSValueRef callback)
    171 {
    172     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    173 
    174     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    175     [[HIDEventGenerator sharedHIDEventGenerator] touchDown:location touchCount:touchCount completionBlock:^{
    176         if (!m_context)
    177             return;
    178         m_context->asyncTaskComplete(callbackID);
    179     }];
    180 }
    181 
    182 void UIScriptController::liftUpAtPoint(long x, long y, long touchCount, JSValueRef callback)
    183 {
    184     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    185    
    186     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    187     [[HIDEventGenerator sharedHIDEventGenerator] liftUp:location touchCount:touchCount completionBlock:^{
    188         if (!m_context)
    189             return;
    190         m_context->asyncTaskComplete(callbackID);
    191     }];
    192 }
    193 
    194 void UIScriptController::singleTapAtPoint(long x, long y, JSValueRef callback)
    195 {
    196     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    197 
    198     [[HIDEventGenerator sharedHIDEventGenerator] tap:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
    199         if (!m_context)
    200             return;
    201         m_context->asyncTaskComplete(callbackID);
    202     }];
    203 }
    204 
    205 void UIScriptController::doubleTapAtPoint(long x, long y, JSValueRef callback)
    206 {
    207     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    208 
    209     [[HIDEventGenerator sharedHIDEventGenerator] doubleTap:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
    210         if (!m_context)
    211             return;
    212         m_context->asyncTaskComplete(callbackID);
    213     }];
    214 }
    215 
    216 void UIScriptController::stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
    217 {
    218     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    219 
    220     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    221     [[HIDEventGenerator sharedHIDEventGenerator] stylusDownAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
    222         if (!m_context)
    223             return;
    224         m_context->asyncTaskComplete(callbackID);
    225     }];
    226 }
    227 
    228 void UIScriptController::stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
    229 {
    230     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    231 
    232     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    233     [[HIDEventGenerator sharedHIDEventGenerator] stylusMoveToPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
    234         if (!m_context)
    235             return;
    236         m_context->asyncTaskComplete(callbackID);
    237     }];
    238 }
    239 
    240 void UIScriptController::stylusUpAtPoint(long x, long y, JSValueRef callback)
    241 {
    242     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    243 
    244     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    245     [[HIDEventGenerator sharedHIDEventGenerator] stylusUpAtPoint:location completionBlock:^{
    246         if (!m_context)
    247             return;
    248         m_context->asyncTaskComplete(callbackID);
    249     }];
    250 }
    251 
    252 void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
    253 {
    254     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    255 
    256     auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
    257     [[HIDEventGenerator sharedHIDEventGenerator] stylusTapAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
    258         if (!m_context)
    259             return;
    260         m_context->asyncTaskComplete(callbackID);
    261     }];
    262 }
    263    
    264 void convertCoordinates(NSMutableDictionary *event)
    265 {
    266     if (event[HIDEventTouchesKey]) {
    267         for (NSMutableDictionary *touch in event[HIDEventTouchesKey]) {
    268             auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), (long)[touch[HIDEventXKey] doubleValue], (long)[touch[HIDEventYKey]doubleValue]);
    269             touch[HIDEventXKey] = @(location.x);
    270             touch[HIDEventYKey] = @(location.y);
    271         }
    272     }
    273 }
    274 
    275 void UIScriptController::sendEventStream(JSStringRef eventsJSON, JSValueRef callback)
    276 {
    277     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    278 
    279     String jsonString = eventsJSON->string();
    280     auto eventInfo = dynamic_objc_cast<NSDictionary>([NSJSONSerialization JSONObjectWithData:[(NSString *)jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves error:nil]);
    281    
    282     for (NSMutableDictionary *event in eventInfo[TopLevelEventInfoKey]) {
    283         if (![event[HIDEventCoordinateSpaceKey] isEqualToString:HIDEventCoordinateSpaceTypeContent])
    284             continue;
    285        
    286         if (event[HIDEventStartEventKey])
    287             convertCoordinates(event[HIDEventStartEventKey]);
    288        
    289         if (event[HIDEventEndEventKey])
    290             convertCoordinates(event[HIDEventEndEventKey]);
    291        
    292         if (event[HIDEventTouchesKey])
    293             convertCoordinates(event);
    294     }
    295    
    296     if (!eventInfo || ![eventInfo isKindOfClass:[NSDictionary class]]) {
    297         WTFLogAlways("JSON is not convertible to a dictionary");
    298         return;
    299     }
    300    
    301     [[HIDEventGenerator sharedHIDEventGenerator] sendEventStream:eventInfo completionBlock:^{
    302         if (!m_context)
    303             return;
    304         m_context->asyncTaskComplete(callbackID);
    305     }];
    306 }
    307 
    308 void UIScriptController::dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback)
    309 {
    310     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    311 
    312     CGPoint startPoint = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), startX, startY);
    313     CGPoint endPoint = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), endX, endY);
    314    
    315     [[HIDEventGenerator sharedHIDEventGenerator] dragWithStartPoint:startPoint endPoint:endPoint duration:durationSeconds completionBlock:^{
    316         if (!m_context)
    317             return;
    318         m_context->asyncTaskComplete(callbackID);
    319     }];
    320 }
    321    
    322 void UIScriptController::longPressAtPoint(long x, long y, JSValueRef callback)
    323 {
    324     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    325    
    326     [[HIDEventGenerator sharedHIDEventGenerator] longPress:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
    327         if (!m_context)
    328             return;
    329         m_context->asyncTaskComplete(callbackID);
    330     }];
    331 }
    332 
    333 void UIScriptController::enterText(JSStringRef text)
    334 {
    335     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
    336     auto textAsCFString = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, text));
    337     [webView _simulateTextEntered:(NSString *)textAsCFString.get()];
    338 }
    339 
    340 void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
    341 {
    342     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
    343 
    344     // Assumes that the keyboard is already shown.
    345     [[HIDEventGenerator sharedHIDEventGenerator] keyPress:toWTFString(toWK(character)) completionBlock:^{
    346         if (!m_context)
    347             return;
    348         m_context->asyncTaskComplete(callbackID);
    349     }];
    350 }
    35166
    35267static unsigned arrayLength(JSContextRef context, JSObjectRef array)
     
    388103}
    389104
     105void UIScriptController::checkForOutstandingCallbacks()
     106{
     107    if (![[HIDEventGenerator sharedHIDEventGenerator] checkForOutstandingCallbacks])
     108        [NSException raise:@"WebKitTestRunnerTestProblem" format:@"The test completed before all synthesized events had been handled. Perhaps you're calling notifyDone() too early?"];
     109}
     110
     111void UIScriptController::doAfterPresentationUpdate(JSValueRef callback)
     112{
     113    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     114
     115    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     116    [webView _doAfterNextPresentationUpdate:^{
     117        if (!m_context)
     118            return;
     119        m_context->asyncTaskComplete(callbackID);
     120    }];
     121}
     122
     123void UIScriptController::doAfterNextStablePresentationUpdate(JSValueRef callback)
     124{
     125    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     126
     127    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     128    [webView _doAfterNextStablePresentationUpdate:^() {
     129        if (m_context)
     130            m_context->asyncTaskComplete(callbackID);
     131    }];
     132}
     133
     134void UIScriptController::doAfterVisibleContentRectUpdate(JSValueRef callback)
     135{
     136    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     137
     138    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     139    [webView _doAfterNextVisibleContentRectUpdate:^ {
     140        if (!m_context)
     141            return;
     142        m_context->asyncTaskComplete(callbackID);
     143    }];
     144}
     145
     146void UIScriptController::zoomToScale(double scale, JSValueRef callback)
     147{
     148    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     149
     150    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     151
     152    [webView zoomToScale:scale animated:YES completionHandler:^{
     153        if (!m_context)
     154            return;
     155        m_context->asyncTaskComplete(callbackID);
     156    }];
     157}
     158
     159void UIScriptController::retrieveSpeakSelectionContent(JSValueRef callback)
     160{
     161    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     162   
     163    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     164   
     165    [webView accessibilityRetrieveSpeakSelectionContentWithCompletionHandler:^() {
     166        if (!m_context)
     167            return;
     168        m_context->asyncTaskComplete(callbackID);
     169    }];
     170}
     171
     172JSRetainPtr<JSStringRef> UIScriptController::accessibilitySpeakSelectionContent() const
     173{
     174    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     175    return JSStringCreateWithCFString((CFStringRef)webView.accessibilitySpeakSelectionContent);
     176}
     177
     178void UIScriptController::simulateAccessibilitySettingsChangeNotification(JSValueRef callback)
     179{
     180    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     181
     182    auto* webView = TestController::singleton().mainWebView()->platformView();
     183    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
     184    [center postNotificationName:UIAccessibilityInvertColorsStatusDidChangeNotification object:webView];
     185
     186    [webView _doAfterNextPresentationUpdate: ^{
     187        if (!m_context)
     188            return;
     189        m_context->asyncTaskComplete(callbackID);
     190    }];
     191}
     192
     193double UIScriptController::zoomScale() const
     194{
     195    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     196    return webView.scrollView.zoomScale;
     197}
     198
     199static CGPoint globalToContentCoordinates(TestRunnerWKWebView *webView, long x, long y)
     200{
     201    CGPoint point = CGPointMake(x, y);
     202    point = [webView _convertPointFromContentsToView:point];
     203    point = [webView convertPoint:point toView:nil];
     204    point = [webView.window convertPoint:point toWindow:nil];
     205    return point;
     206}
     207
     208void UIScriptController::touchDownAtPoint(long x, long y, long touchCount, JSValueRef callback)
     209{
     210    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     211
     212    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     213    [[HIDEventGenerator sharedHIDEventGenerator] touchDown:location touchCount:touchCount completionBlock:^{
     214        if (!m_context)
     215            return;
     216        m_context->asyncTaskComplete(callbackID);
     217    }];
     218}
     219
     220void UIScriptController::liftUpAtPoint(long x, long y, long touchCount, JSValueRef callback)
     221{
     222    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     223   
     224    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     225    [[HIDEventGenerator sharedHIDEventGenerator] liftUp:location touchCount:touchCount completionBlock:^{
     226        if (!m_context)
     227            return;
     228        m_context->asyncTaskComplete(callbackID);
     229    }];
     230}
     231
     232void UIScriptController::singleTapAtPoint(long x, long y, JSValueRef callback)
     233{
     234    singleTapAtPointWithModifiers(x, y, nullptr, callback);
     235}
     236
     237void UIScriptController::singleTapAtPointWithModifiers(long x, long y, JSValueRef modifierArray, JSValueRef callback)
     238{
     239    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     240
     241    auto modifierFlags = parseModifierArray(m_context->jsContext(), modifierArray);
     242    for (auto& modifierFlag : modifierFlags)
     243        [[HIDEventGenerator sharedHIDEventGenerator] keyDown:modifierFlag];
     244
     245    [[HIDEventGenerator sharedHIDEventGenerator] tap:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
     246        if (!m_context)
     247            return;
     248        for (size_t i = modifierFlags.size(); i; ) {
     249            --i;
     250            [[HIDEventGenerator sharedHIDEventGenerator] keyUp:modifierFlags[i]];
     251        }
     252        [[HIDEventGenerator sharedHIDEventGenerator] sendMarkerHIDEventWithCompletionBlock:^{
     253            if (!m_context)
     254                return;
     255            m_context->asyncTaskComplete(callbackID);
     256        }];
     257    }];
     258}
     259
     260void UIScriptController::doubleTapAtPoint(long x, long y, JSValueRef callback)
     261{
     262    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     263
     264    [[HIDEventGenerator sharedHIDEventGenerator] doubleTap:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
     265        if (!m_context)
     266            return;
     267        m_context->asyncTaskComplete(callbackID);
     268    }];
     269}
     270
     271void UIScriptController::stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
     272{
     273    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     274
     275    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     276    [[HIDEventGenerator sharedHIDEventGenerator] stylusDownAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
     277        if (!m_context)
     278            return;
     279        m_context->asyncTaskComplete(callbackID);
     280    }];
     281}
     282
     283void UIScriptController::stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
     284{
     285    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     286
     287    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     288    [[HIDEventGenerator sharedHIDEventGenerator] stylusMoveToPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
     289        if (!m_context)
     290            return;
     291        m_context->asyncTaskComplete(callbackID);
     292    }];
     293}
     294
     295void UIScriptController::stylusUpAtPoint(long x, long y, JSValueRef callback)
     296{
     297    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     298
     299    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     300    [[HIDEventGenerator sharedHIDEventGenerator] stylusUpAtPoint:location completionBlock:^{
     301        if (!m_context)
     302            return;
     303        m_context->asyncTaskComplete(callbackID);
     304    }];
     305}
     306
     307void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
     308{
     309    stylusTapAtPointWithModifiers(x, y, azimuthAngle, altitudeAngle, pressure, nullptr, callback);
     310}
     311
     312void UIScriptController::stylusTapAtPointWithModifiers(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef modifierArray, JSValueRef callback)
     313{
     314    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     315
     316    auto modifierFlags = parseModifierArray(m_context->jsContext(), modifierArray);
     317    for (auto& modifierFlag : modifierFlags)
     318        [[HIDEventGenerator sharedHIDEventGenerator] keyDown:modifierFlag];
     319
     320    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
     321    [[HIDEventGenerator sharedHIDEventGenerator] stylusTapAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
     322        if (!m_context)
     323            return;
     324        for (size_t i = modifierFlags.size(); i; ) {
     325            --i;
     326            [[HIDEventGenerator sharedHIDEventGenerator] keyUp:modifierFlags[i]];
     327        }
     328        [[HIDEventGenerator sharedHIDEventGenerator] sendMarkerHIDEventWithCompletionBlock:^{
     329            if (!m_context)
     330                return;
     331            m_context->asyncTaskComplete(callbackID);
     332        }];
     333    }];
     334}
     335
     336void convertCoordinates(NSMutableDictionary *event)
     337{
     338    if (event[HIDEventTouchesKey]) {
     339        for (NSMutableDictionary *touch in event[HIDEventTouchesKey]) {
     340            auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), (long)[touch[HIDEventXKey] doubleValue], (long)[touch[HIDEventYKey]doubleValue]);
     341            touch[HIDEventXKey] = @(location.x);
     342            touch[HIDEventYKey] = @(location.y);
     343        }
     344    }
     345}
     346
     347void UIScriptController::sendEventStream(JSStringRef eventsJSON, JSValueRef callback)
     348{
     349    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     350
     351    String jsonString = eventsJSON->string();
     352    auto eventInfo = dynamic_objc_cast<NSDictionary>([NSJSONSerialization JSONObjectWithData:[(NSString *)jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves error:nil]);
     353   
     354    for (NSMutableDictionary *event in eventInfo[TopLevelEventInfoKey]) {
     355        if (![event[HIDEventCoordinateSpaceKey] isEqualToString:HIDEventCoordinateSpaceTypeContent])
     356            continue;
     357       
     358        if (event[HIDEventStartEventKey])
     359            convertCoordinates(event[HIDEventStartEventKey]);
     360       
     361        if (event[HIDEventEndEventKey])
     362            convertCoordinates(event[HIDEventEndEventKey]);
     363       
     364        if (event[HIDEventTouchesKey])
     365            convertCoordinates(event);
     366    }
     367   
     368    if (!eventInfo || ![eventInfo isKindOfClass:[NSDictionary class]]) {
     369        WTFLogAlways("JSON is not convertible to a dictionary");
     370        return;
     371    }
     372   
     373    [[HIDEventGenerator sharedHIDEventGenerator] sendEventStream:eventInfo completionBlock:^{
     374        if (!m_context)
     375            return;
     376        m_context->asyncTaskComplete(callbackID);
     377    }];
     378}
     379
     380void UIScriptController::dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback)
     381{
     382    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     383
     384    CGPoint startPoint = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), startX, startY);
     385    CGPoint endPoint = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), endX, endY);
     386   
     387    [[HIDEventGenerator sharedHIDEventGenerator] dragWithStartPoint:startPoint endPoint:endPoint duration:durationSeconds completionBlock:^{
     388        if (!m_context)
     389            return;
     390        m_context->asyncTaskComplete(callbackID);
     391    }];
     392}
     393   
     394void UIScriptController::longPressAtPoint(long x, long y, JSValueRef callback)
     395{
     396    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     397   
     398    [[HIDEventGenerator sharedHIDEventGenerator] longPress:globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y) completionBlock:^{
     399        if (!m_context)
     400            return;
     401        m_context->asyncTaskComplete(callbackID);
     402    }];
     403}
     404
     405void UIScriptController::enterText(JSStringRef text)
     406{
     407    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
     408    auto textAsCFString = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, text));
     409    [webView _simulateTextEntered:(NSString *)textAsCFString.get()];
     410}
     411
     412void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
     413{
     414    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
     415
     416    // Assumes that the keyboard is already shown.
     417    [[HIDEventGenerator sharedHIDEventGenerator] keyPress:toWTFString(toWK(character)) completionBlock:^{
     418        if (!m_context)
     419            return;
     420        m_context->asyncTaskComplete(callbackID);
     421    }];
     422}
     423
    390424static UIPhysicalKeyboardEvent *createUIPhysicalKeyboardEvent(NSString *hidInputString, NSString *uiEventInputString, UIKeyModifierFlags modifierFlags, UIKeyboardInputFlags inputFlags, bool isKeyDown)
    391425{
Note: See TracChangeset for help on using the changeset viewer.