Changeset 241971 in webkit


Ignore:
Timestamp:
Feb 22, 2019 4:48:16 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Callout menu overlaps in-page controls when editing a comment in github.com's issue tracker
https://bugs.webkit.org/show_bug.cgi?id=194873
<rdar://problem/46701974>

Reviewed by Tim Horton.

Source/WebKit:

On the topic of supporting web-based rich text editors on iOS, one problematic area has always been handling
conflicts between platform UI (i.e., the system callout menu) and in-page text editing controls. This issue
comes up in websites that don't use the "hidden contenteditable" approach to rich text editing, but also show
additional controls in a toolbar or contextual menu above the selection. In these cases, what often happens is
that system controls overlap controls in the page.

Luckily, the iOS callout menu (i.e. the private UICalloutBar) is capable of presenting with a list of "evasion
rects" to avoid; if the callout bar would normally intersect with one of these rects, then a different
orientation that does not intersect with one of these rects is chosen instead. Currently, the only rect added
here by UIKit when presenting the callout menu is the bounding rect of the on-screen keyboard, but after
<rdar://problem/48128337>, we now have a generalized mechanism for offering additional evasion rects before
UIKit presents the callout menu.

This patch adopts the mechanism introduced in <rdar://problem/48128337>, and introduces a heuristic for
determining the approximate location of controls in the page which might overlap the callout menu. This
heuristic works by hit-testing for clickable (but non-editable) nodes above the bounds of the selection, which
are additionally not hit-tested by advancing outwards from any of the other edges of the selection bounds.
Additionally, any hit-tested nodes whose bounding rects are very large (relative to the content view size) are
ignored (this deals with scenarios where the body or a large container element has a click handler). We then add
the bounding rects of each of the nodes that fit this criteria to the list of rects for UIKit to avoid when
presenting the system callout menu.

The result is that WebKit will, by default, avoid overlapping anything that looks like controls in the page when
showing a callout menu in editable content. In practice, this fixes overlapping controls on most websites that
roll their own context menu or toolbar in their rich text editor.

Test: editing/selection/ios/avoid-showing-callout-menu-over-controls.html

  • Platform/spi/ios/UIKitSPI.h:
  • UIProcess/WebPageProxy.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView requestAutocorrectionRectsForString:withCompletionHandler:]):
(-[WKContentView requestRectsToEvadeForSelectionCommandsWithCompletionHandler:]):
(-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):

Drive-by: handle null completion handler arguments more gracefully, by raising an NSException and bailing before
attempting to invoke a nil block.

  • UIProcess/ios/WebPageProxyIOS.mm:

(WebKit::WebPageProxy::requestEvasionRectsAboveSelection):

See above for more detail.

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

(WebKit::WebPage::requestEvasionRectsAboveSelection):

Tools:

Add a couple of UIScriptController methods to make callout menu testing on iOS easier (see below).

  • DumpRenderTree/ios/UIScriptControllerIOS.mm:

(WTR::UIScriptController::menuRect const):
(WTR::UIScriptController::isShowingMenu const):

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

(WTR::UIScriptController::menuRect const):

Add a function to query the bounds of the callout menu in content coordinates.

(WTR::UIScriptController::isShowingMenu const):

Add a function to query whether the callout menu is shown (i.e., has finished its appearance animation).

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

(WTR::UIScriptController::rectForMenuAction const):
(WTR::UIScriptController::menuRect const):
(WTR::UIScriptController::isShowingMenu const):
(WTR::findViewInHierarchyOfType): Deleted.

LayoutTests:

Add a test to ensure that the we dodge clickable elements when showing the callout bar.

  • editing/selection/ios/avoid-showing-callout-menu-over-controls-expected.txt: Added.
  • editing/selection/ios/avoid-showing-callout-menu-over-controls.html: Added.
  • resources/ui-helper.js:

(window.UIHelper.waitForMenuToShow.return.new.Promise):
(window.UIHelper.waitForMenuToShow):
(window.UIHelper.menuRect):
(window.UIHelper):

Location:
trunk
Files:
2 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r241949 r241971  
     12019-02-22  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Callout menu overlaps in-page controls when editing a comment in github.com's issue tracker
     4        https://bugs.webkit.org/show_bug.cgi?id=194873
     5        <rdar://problem/46701974>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add a test to ensure that the we dodge clickable elements when showing the callout bar.
     10
     11        * editing/selection/ios/avoid-showing-callout-menu-over-controls-expected.txt: Added.
     12        * editing/selection/ios/avoid-showing-callout-menu-over-controls.html: Added.
     13        * resources/ui-helper.js:
     14        (window.UIHelper.waitForMenuToShow.return.new.Promise):
     15        (window.UIHelper.waitForMenuToShow):
     16        (window.UIHelper.menuRect):
     17        (window.UIHelper):
     18
    1192019-02-22  Wenson Hsieh  <wenson_hsieh@apple.com>
    220
  • trunk/LayoutTests/resources/ui-helper.js

    r241719 r241971  
    666666        return new Promise(resolve => testRunner.runUIScript(script, result => resolve(JSON.parse(result))));
    667667    }
     668
     669    static waitForMenuToShow()
     670    {
     671        return new Promise(resolve => {
     672            testRunner.runUIScript(`
     673                (function() {
     674                    if (!uiController.isShowingMenu)
     675                        uiController.didShowMenuCallback = () => uiController.uiScriptComplete();
     676                    else
     677                        uiController.uiScriptComplete();
     678                })()`, resolve);
     679        });
     680    }
     681
     682    static menuRect()
     683    {
     684        return new Promise(resolve => {
     685            testRunner.runUIScript("JSON.stringify(uiController.menuRect)", result => resolve(JSON.parse(result)));
     686        });
     687    }
    668688}
  • trunk/Source/WebKit/ChangeLog

    r241969 r241971  
     12019-02-22  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Callout menu overlaps in-page controls when editing a comment in github.com's issue tracker
     4        https://bugs.webkit.org/show_bug.cgi?id=194873
     5        <rdar://problem/46701974>
     6
     7        Reviewed by Tim Horton.
     8
     9        On the topic of supporting web-based rich text editors on iOS, one problematic area has always been handling
     10        conflicts between platform UI (i.e., the system callout menu) and in-page text editing controls. This issue
     11        comes up in websites that don't use the "hidden contenteditable" approach to rich text editing, but also show
     12        additional controls in a toolbar or contextual menu above the selection. In these cases, what often happens is
     13        that system controls overlap controls in the page.
     14
     15        Luckily, the iOS callout menu (i.e. the private UICalloutBar) is capable of presenting with a list of "evasion
     16        rects" to avoid; if the callout bar would normally intersect with one of these rects, then a different
     17        orientation that does not intersect with one of these rects is chosen instead. Currently, the only rect added
     18        here by UIKit when presenting the callout menu is the bounding rect of the on-screen keyboard, but after
     19        <rdar://problem/48128337>, we now have a generalized mechanism for offering additional evasion rects before
     20        UIKit presents the callout menu.
     21
     22        This patch adopts the mechanism introduced in <rdar://problem/48128337>, and introduces a heuristic for
     23        determining the approximate location of controls in the page which might overlap the callout menu. This
     24        heuristic works by hit-testing for clickable (but non-editable) nodes above the bounds of the selection, which
     25        are additionally not hit-tested by advancing outwards from any of the other edges of the selection bounds.
     26        Additionally, any hit-tested nodes whose bounding rects are very large (relative to the content view size) are
     27        ignored (this deals with scenarios where the body or a large container element has a click handler). We then add
     28        the bounding rects of each of the nodes that fit this criteria to the list of rects for UIKit to avoid when
     29        presenting the system callout menu.
     30
     31        The result is that WebKit will, by default, avoid overlapping anything that looks like controls in the page when
     32        showing a callout menu in editable content. In practice, this fixes overlapping controls on most websites that
     33        roll their own context menu or toolbar in their rich text editor.
     34
     35        Test: editing/selection/ios/avoid-showing-callout-menu-over-controls.html
     36
     37        * Platform/spi/ios/UIKitSPI.h:
     38        * UIProcess/WebPageProxy.h:
     39        * UIProcess/ios/WKContentViewInteraction.mm:
     40        (-[WKContentView requestAutocorrectionRectsForString:withCompletionHandler:]):
     41        (-[WKContentView requestRectsToEvadeForSelectionCommandsWithCompletionHandler:]):
     42        (-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):
     43
     44        Drive-by: handle null completion handler arguments more gracefully, by raising an NSException and bailing before
     45        attempting to invoke a nil block.
     46
     47        * UIProcess/ios/WebPageProxyIOS.mm:
     48        (WebKit::WebPageProxy::requestEvasionRectsAboveSelection):
     49
     50        See above for more detail.
     51
     52        * WebProcess/WebPage/WebPage.h:
     53        * WebProcess/WebPage/WebPage.messages.in:
     54        * WebProcess/WebPage/ios/WebPageIOS.mm:
     55        (WebKit::WebPage::requestEvasionRectsAboveSelection):
     56
    1572019-02-22  Simon Fraser  <simon.fraser@apple.com>
    258
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r241865 r241971  
    989989
    990990@interface UICalloutBar : UIView
     991+ (UICalloutBar *)activeCalloutBar;
    991992+ (void)fadeSharedCalloutBar;
    992993@end
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r241963 r241971  
    688688    void hardwareKeyboardAvailabilityChanged();
    689689    bool isScrollingOrZooming() const { return m_isScrollingOrZooming; }
     690    void requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&&);
    690691#if ENABLE(DATA_INTERACTION)
    691692    void didHandleDragStartRequest(bool started);
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r241900 r241971  
    33543354- (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler
    33553355{
     3356    if (!completionHandler) {
     3357        [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__];
     3358        return;
     3359    }
     3360
    33563361    if (!input || ![input length]) {
    3357         if (completionHandler)
    3358             completionHandler(nil);
     3362        completionHandler(nil);
    33593363        return;
    33603364    }
     
    33743378        view->_autocorrectionData.textLastRect = lastRect;
    33753379
    3376         if (completion)
    3377             completion(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
     3380        completion(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
     3381    });
     3382}
     3383
     3384- (void)requestRectsToEvadeForSelectionCommandsWithCompletionHandler:(void(^)(NSArray<NSValue *> *rects))completionHandler
     3385{
     3386    if (!completionHandler) {
     3387        [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__];
     3388        return;
     3389    }
     3390
     3391    if ([self _shouldSuppressSelectionCommands] || _webView._editable) {
     3392        completionHandler(@[ ]);
     3393        return;
     3394    }
     3395
     3396    if (_focusedElementInformation.elementType != WebKit::InputType::ContentEditable && _focusedElementInformation.elementType != WebKit::InputType::TextArea) {
     3397        completionHandler(@[ ]);
     3398        return;
     3399    }
     3400
     3401    // Give the page some time to present custom editing UI before attempting to detect and evade it.
     3402    auto delayBeforeShowingCalloutBar = (0.25_s).nanoseconds();
     3403    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayBeforeShowingCalloutBar), dispatch_get_main_queue(), [completion = makeBlockPtr(completionHandler), weakSelf = WeakObjCPtr<WKContentView>(self)] () mutable {
     3404        if (!weakSelf) {
     3405            completion(@[ ]);
     3406            return;
     3407        }
     3408
     3409        auto strongSelf = weakSelf.get();
     3410        if (!strongSelf->_page) {
     3411            completion(@[ ]);
     3412            return;
     3413        }
     3414
     3415        strongSelf->_page->requestEvasionRectsAboveSelection([completion = WTFMove(completion)] (auto& rects) {
     3416            auto rectsAsValues = adoptNS([[NSMutableArray alloc] initWithCapacity:rects.size()]);
     3417            for (auto& floatRect : rects)
     3418                [rectsAsValues addObject:[NSValue valueWithCGRect:floatRect]];
     3419            completion(rectsAsValues.get());
     3420        });
    33783421    });
    33793422}
     
    35383581- (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler
    35393582{
    3540     if (!completionHandler)
    3541         return;
     3583    if (!completionHandler) {
     3584        [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__];
     3585        return;
     3586    }
    35423587
    35433588#if USE(UIKIT_KEYBOARD_ADDITIONS)
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r241963 r241971  
    3030
    3131#import "APIUIClient.h"
     32#import "Connection.h"
    3233#import "DataReference.h"
    3334#import "EditingRange.h"
     
    11131114}
    11141115
     1116void WebPageProxy::requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&& callback)
     1117{
     1118    if (!isValid()) {
     1119        callback({ });
     1120        return;
     1121    }
     1122
     1123    m_process->connection()->sendWithAsyncReply(Messages::WebPage::RequestEvasionRectsAboveSelection(), WTFMove(callback), m_pageID);
     1124}
     1125
    11151126#if ENABLE(DATA_INTERACTION)
    11161127
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r241721 r241971  
    665665    void startAutoscrollAtPosition(const WebCore::FloatPoint&);
    666666    void cancelAutoscroll();
     667    void requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&&);
    667668
    668669    void contentSizeCategoryDidChange(const String&);
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r241282 r241971  
    8181    RequestAutocorrectionContext(WebKit::CallbackID callbackID)
    8282    AutocorrectionContextSync() -> (struct WebKit::WebAutocorrectionContext context) Delayed
     83    RequestEvasionRectsAboveSelection() -> (Vector<WebCore::FloatRect> rects) Async
    8384    GetPositionInformation(struct WebKit::InteractionInformationRequest request) -> (struct WebKit::InteractionInformationAtPosition information) Delayed
    8485    RequestPositionInformation(struct WebKit::InteractionInformationRequest request)
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r241934 r241971  
    15051505}
    15061506
     1507void WebPage::requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<FloatRect>&)>&& reply)
     1508{
     1509    auto& frame = m_page->focusController().focusedOrMainFrame();
     1510    auto frameView = makeRefPtr(frame.view());
     1511    if (!frameView) {
     1512        reply({ });
     1513        return;
     1514    }
     1515
     1516    auto& selection = frame.selection().selection();
     1517    if (selection.isNone()) {
     1518        reply({ });
     1519        return;
     1520    }
     1521
     1522    auto selectedRange = selection.toNormalizedRange();
     1523    if (!selectedRange) {
     1524        reply({ });
     1525        return;
     1526    }
     1527
     1528    if (!m_focusedElement || !m_focusedElement->renderer() || m_focusedElement->renderer()->isTransparentOrFullyClippedRespectingParentFrames()) {
     1529        reply({ });
     1530        return;
     1531    }
     1532
     1533    float scaleFactor = pageScaleFactor();
     1534    const double factorOfContentArea = 0.5;
     1535    auto unobscuredContentArea = m_page->mainFrame().view()->unobscuredContentRect().area();
     1536    if (unobscuredContentArea.hasOverflowed()) {
     1537        reply({ });
     1538        return;
     1539    }
     1540
     1541    double contextMenuAreaLimit = factorOfContentArea * scaleFactor * unobscuredContentArea.unsafeGet();
     1542
     1543    FloatRect selectionBoundsInRootViewCoordinates;
     1544    if (selection.isRange())
     1545        selectionBoundsInRootViewCoordinates = frameView->contentsToRootView(selectedRange->absoluteBoundingBox());
     1546    else
     1547        selectionBoundsInRootViewCoordinates = frameView->contentsToRootView(frame.selection().absoluteCaretBounds());
     1548
     1549    auto centerOfTargetBounds = selectionBoundsInRootViewCoordinates.center();
     1550    FloatPoint centerTopInRootViewCoordinates { centerOfTargetBounds.x(), selectionBoundsInRootViewCoordinates.y() };
     1551
     1552    auto clickableNonEditableNode = [&] (const FloatPoint& locationInRootViewCoordinates) -> Node* {
     1553        FloatPoint adjustedPoint;
     1554        auto* hitNode = m_page->mainFrame().nodeRespondingToClickEvents(locationInRootViewCoordinates, adjustedPoint);
     1555        if (!hitNode || is<HTMLBodyElement>(hitNode) || is<Document>(hitNode) || hitNode->hasEditableStyle())
     1556            return nullptr;
     1557
     1558        return hitNode;
     1559    };
     1560
     1561    // This heuristic attempts to find a list of rects to avoid when showing the callout menu on iOS.
     1562    // First, hit-test several points above the bounds of the selection rect in search of clickable nodes that are not editable.
     1563    // Secondly, hit-test several points around the edges of the selection rect and exclude any nodes found in the first round of
     1564    // hit-testing if these nodes are also reachable by moving outwards from the left, right, or bottom edges of the selection.
     1565    // Additionally, exclude any hit-tested nodes that are either very large relative to the size of the root view, or completely
     1566    // encompass the selection bounds. The resulting rects are the bounds of these hit-tested nodes in root view coordinates.
     1567    HashSet<Ref<Node>> hitTestedNodes;
     1568    Vector<FloatRect> rectsToAvoidInRootViewCoordinates;
     1569    const Vector<FloatPoint, 5> offsetsForHitTesting {{ -30, -50 }, { 30, -50 }, { -60, -35 }, { 60, -35 }, { 0, -20 }};
     1570    for (auto offset : offsetsForHitTesting) {
     1571        offset.scale(1 / scaleFactor);
     1572        if (auto* hitNode = clickableNonEditableNode(centerTopInRootViewCoordinates + offset))
     1573            hitTestedNodes.add(*hitNode);
     1574    }
     1575
     1576    const float marginForHitTestingSurroundingNodes = 80 / scaleFactor;
     1577    Vector<FloatPoint, 3> exclusionHitTestLocations {
     1578        { selectionBoundsInRootViewCoordinates.x() - marginForHitTestingSurroundingNodes, centerOfTargetBounds.y() },
     1579        { centerOfTargetBounds.x(), selectionBoundsInRootViewCoordinates.maxY() + marginForHitTestingSurroundingNodes },
     1580        { selectionBoundsInRootViewCoordinates.maxX() + marginForHitTestingSurroundingNodes, centerOfTargetBounds.y() }
     1581    };
     1582
     1583    for (auto& location : exclusionHitTestLocations) {
     1584        if (auto* nodeToExclude = clickableNonEditableNode(location))
     1585            hitTestedNodes.remove(*nodeToExclude);
     1586    }
     1587
     1588    for (auto& node : hitTestedNodes) {
     1589        auto frameView = makeRefPtr(node->document().view());
     1590        auto* renderer = node->renderer();
     1591        if (!renderer || !frameView)
     1592            continue;
     1593
     1594        auto bounds = frameView->contentsToRootView(renderer->absoluteBoundingBoxRect());
     1595        auto area = bounds.area();
     1596        if (area.hasOverflowed() || area.unsafeGet() > contextMenuAreaLimit)
     1597            continue;
     1598
     1599        if (bounds.contains(enclosingIntRect(selectionBoundsInRootViewCoordinates)))
     1600            continue;
     1601
     1602        rectsToAvoidInRootViewCoordinates.append(WTFMove(bounds));
     1603    }
     1604
     1605    reply(WTFMove(rectsToAvoidInRootViewCoordinates));
     1606}
     1607
    15071608void WebPage::getRectsForGranularityWithSelectionOffset(uint32_t granularity, int32_t offset, CallbackID callbackID)
    15081609{
  • trunk/Tools/ChangeLog

    r241963 r241971  
     12019-02-22  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Callout menu overlaps in-page controls when editing a comment in github.com's issue tracker
     4        https://bugs.webkit.org/show_bug.cgi?id=194873
     5        <rdar://problem/46701974>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add a couple of UIScriptController methods to make callout menu testing on iOS easier (see below).
     10
     11        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
     12        (WTR::UIScriptController::menuRect const):
     13        (WTR::UIScriptController::isShowingMenu const):
     14        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
     15        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
     16        (WTR::UIScriptController::menuRect const):
     17
     18        Add a function to query the bounds of the callout menu in content coordinates.
     19
     20        (WTR::UIScriptController::isShowingMenu const):
     21
     22        Add a function to query whether the callout menu is shown (i.e., has finished its appearance animation).
     23
     24        * TestRunnerShared/UIScriptContext/UIScriptController.h:
     25        * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
     26        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
     27        (WTR::UIScriptController::rectForMenuAction const):
     28        (WTR::UIScriptController::menuRect const):
     29        (WTR::UIScriptController::isShowingMenu const):
     30        (WTR::findViewInHierarchyOfType): Deleted.
     31
    1322019-02-22  Chris Dumez  <cdumez@apple.com>
    233
  • trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm

    r241322 r241971  
    338338}
    339339
     340JSObjectRef UIScriptController::menuRect() const
     341{
     342    return nullptr;
     343}
     344
     345bool UIScriptController::isShowingMenu() const
     346{
     347    return false;
     348}
     349
    340350void UIScriptController::platformSetDidEndScrollingCallback()
    341351{
  • trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl

    r241322 r241971  
    226226    attribute object didShowMenuCallback;
    227227    attribute object didHideMenuCallback;
     228    readonly attribute boolean isShowingMenu;
     229    readonly attribute object menuRect;
    228230    object rectForMenuAction(DOMString action);
    229231
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp

    r241322 r241971  
    533533}
    534534
     535JSObjectRef UIScriptController::menuRect() const
     536{
     537    return nullptr;
     538}
     539
    535540JSObjectRef UIScriptController::rectForMenuAction(JSStringRef) const
    536541{
    537542    return nullptr;
     543}
     544
     545bool UIScriptController::isShowingMenu() const
     546{
     547    return false;
    538548}
    539549
  • trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h

    r241322 r241971  
    165165    JSValueRef didShowMenuCallback() const;
    166166
     167    bool isShowingMenu() const;
    167168    JSObjectRef rectForMenuAction(JSStringRef action) const;
     169    JSObjectRef menuRect() const;
    168170
    169171    void setDidEndScrollingCallback(JSValueRef);
  • trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h

    r241322 r241971  
    5757
    5858@property (nonatomic, readonly, getter=isShowingKeyboard) BOOL showingKeyboard;
     59@property (nonatomic, readonly, getter=isShowingMenu) BOOL showingMenu;
    5960@property (nonatomic, assign) BOOL usesSafariLikeRotation;
    6061@property (nonatomic, readonly, getter=isInteractingWithFormControl) BOOL interactingWithFormControl;
  • trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

    r241573 r241971  
    118118}
    119119
    120 static UIView *findViewInHierarchyOfType(UIView *view, Class viewClass)
    121 {
    122     __block RetainPtr<UIView> foundView;
    123     forEachViewInHierarchy(view, ^(UIView *subview, BOOL *stop) {
    124         if (![subview isKindOfClass:viewClass])
    125             return;
    126 
    127         foundView = subview;
    128         *stop = YES;
    129     });
    130     return foundView.autorelease();
    131 }
    132 
    133120static NSArray<UIView *> *findAllViewsInHierarchyOfType(UIView *view, Class viewClass)
    134121{
     
    884871    UIWindow *windowForButton = nil;
    885872    UIButton *buttonForAction = nil;
    886     for (UIWindow *window in UIApplication.sharedApplication.windows) {
    887         if (![window isKindOfClass:UITextEffectsWindow.class])
     873    UIView *calloutBar = UICalloutBar.activeCalloutBar;
     874    if (!calloutBar.window)
     875        return nullptr;
     876
     877    for (UIButton *button in findAllViewsInHierarchyOfType(calloutBar, UIButton.class)) {
     878        NSString *buttonTitle = [button titleForState:UIControlStateNormal];
     879        if (!buttonTitle.length)
    888880            continue;
    889881
    890         UIView *calloutBar = findViewInHierarchyOfType(window, UICalloutBar.class);
    891         if (!calloutBar)
     882        if (![buttonTitle isEqualToString:(__bridge NSString *)action.get()])
    892883            continue;
    893884
    894         for (UIButton *button in findAllViewsInHierarchyOfType(calloutBar, UIButton.class)) {
    895             NSString *buttonTitle = [button titleForState:UIControlStateNormal];
    896             if (!buttonTitle.length)
    897                 continue;
    898 
    899             if (![buttonTitle isEqualToString:(__bridge NSString *)action.get()])
    900                 continue;
    901 
    902             buttonForAction = button;
    903             windowForButton = window;
    904         }
     885        buttonForAction = button;
     886        windowForButton = calloutBar.window;
     887        break;
    905888    }
    906889
     
    910893    CGRect rectInRootViewCoordinates = [buttonForAction convertRect:buttonForAction.bounds toView:platformContentView()];
    911894    return m_context->objectFromRect(WebCore::FloatRect(rectInRootViewCoordinates.origin.x, rectInRootViewCoordinates.origin.y, rectInRootViewCoordinates.size.width, rectInRootViewCoordinates.size.height));
     895}
     896
     897JSObjectRef UIScriptController::menuRect() const
     898{
     899    UIView *calloutBar = UICalloutBar.activeCalloutBar;
     900    if (!calloutBar.window)
     901        return nullptr;
     902
     903    CGRect rectInRootViewCoordinates = [calloutBar convertRect:calloutBar.bounds toView:platformContentView()];
     904    return m_context->objectFromRect(WebCore::FloatRect(rectInRootViewCoordinates.origin.x, rectInRootViewCoordinates.origin.y, rectInRootViewCoordinates.size.width, rectInRootViewCoordinates.size.height));
     905}
     906
     907bool UIScriptController::isShowingMenu() const
     908{
     909    return TestController::singleton().mainWebView()->platformView().showingMenu;
    912910}
    913911
Note: See TracChangeset for help on using the changeset viewer.