⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 243354 in webkit


Ignore:
Timestamp:
Mar 21, 2019, 7:26:09 PM (7 years ago)
Author:
timothy_horton@apple.com
Message:

Adopt UIWKDocumentContext
https://bugs.webkit.org/show_bug.cgi?id=196040
<rdar://problem/48642440>

Reviewed by Ryosuke Niwa.

New API test: WebKit.DocumentEditingContext

  • dom/Range.h:
  • editing/TextGranularity.h:

Make TextGranularity encodable by providing EnumTraits.

  • editing/TextIterator.cpp:

(WebCore::plainTextReplacingNoBreakSpace):

  • editing/TextIterator.h:

Expose an nbsp-replacing variant of plainText that takes Positions instead of Ranges.

  • Platform/spi/ios/UIKitSPI.h:
  • Scripts/webkit/messages.py:
  • Shared/DocumentEditingContext.h: Added.
  • Shared/DocumentEditingContext.mm: Added.

(WebKit::toNSRange):
(WebKit::DocumentEditingContext::toPlatformContext):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext::Range>::encode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext::Range>::decode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext::TextRect>::encode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext::TextRect>::decode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext>::encode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContext>::decode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContextRequest>::encode):
(IPC::ArgumentCoder<WebKit::DocumentEditingContextRequest>::decode):
Add DocumentEditingContext(Request), and coders.
Also expose DocumentEditingContext::toPlatformContext, which populates
a UIWKDocumentContext with the relevant values.

  • SourcesCocoa.txt:
  • UIProcess/WebPageProxy.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(toWebDocumentRequestOptions):
(toWebRequest):
(-[WKContentView adjustSelectionWithDelta:completionHandler:]):
(-[WKContentView requestDocumentContext:completionHandler:]):
(-[WKContentView selectPositionAtPoint:withContextRequest:completionHandler:]):

  • UIProcess/ios/WebPageProxyIOS.mm:

(WebKit::WebPageProxy::adjustSelectionWithDelta):
(WebKit::WebPageProxy::requestDocumentEditingContext):

  • WebKit.xcodeproj/project.pbxproj:
  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/WebPage.messages.in:

Plumb DocumentEditingContext(Request) around.

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::adjustSelectionWithDelta):
Adjust the current selection given deltas to apply to the location and length.

(WebKit::visiblePositionAdjacentToVisiblePosition):
(WebKit::visiblePositionForPointInRootViewCoordinates):
(WebKit::WebPage::requestDocumentEditingContext):
Retrieve the relevant part of the selection, as well as the context,
given either the selection, or a rect.
If we're collecting context for a rect, we split the context between
before and after (except for the part that intersects the selection).
The three strings will always be directly adjacent, and any of the three
can sometimes be null.

  • TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
  • TestWebKitAPI/Tests/WebKitCocoa/DocumentEditingContext.mm: Added.

(makeRequest):
(-[TestWKWebView synchronouslyRequestDocumentContext:]):
(-[TestWKWebView synchronouslyAdjustSelectionWithDelta:]):
(applyStyle):
(TEST):
Add tests for UIWKDocumentContext.

  • TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm:

(TEST):
(recursiveFindWKContentView): Deleted.

  • TestWebKitAPI/cocoa/TestWKWebView.h:
  • TestWebKitAPI/cocoa/TestWKWebView.mm:

(recursiveFindWKContentView):
(-[TestWKWebView wkContentView]):

  • TestWebKitAPI/ios/UIKitSPI.h:

Share the WKContentView finding code between tests.

Location:
trunk
Files:
3 added
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r243348 r243354  
     12019-03-21  Tim Horton  <timothy_horton@apple.com>
     2
     3        Adopt UIWKDocumentContext
     4        https://bugs.webkit.org/show_bug.cgi?id=196040
     5        <rdar://problem/48642440>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        New API test: WebKit.DocumentEditingContext
     10
     11        * dom/Range.h:
     12        * editing/TextGranularity.h:
     13        Make TextGranularity encodable by providing EnumTraits.
     14
     15        * editing/TextIterator.cpp:
     16        (WebCore::plainTextReplacingNoBreakSpace):
     17        * editing/TextIterator.h:
     18        Expose an nbsp-replacing variant of plainText that takes Positions instead of Ranges.
     19
    1202019-03-21  Sihui Liu  <sihui_liu@apple.com>
    221
  • trunk/Source/WebCore/dom/Range.h

    r240237 r243354  
    177177
    178178WEBCORE_EXPORT bool areRangesEqual(const Range*, const Range*);
    179 bool rangesOverlap(const Range*, const Range*);
     179WEBCORE_EXPORT bool rangesOverlap(const Range*, const Range*);
    180180
    181181inline bool documentOrderComparator(const Node* a, const Node* b)
  • trunk/Source/WebCore/editing/TextGranularity.h

    r208646 r243354  
    4444
    4545} // namespace WebCore
     46
     47namespace WTF {
     48
     49template<> struct EnumTraits<WebCore::TextGranularity> {
     50    using values = EnumValues<
     51        WebCore::TextGranularity,
     52        WebCore::TextGranularity::CharacterGranularity,
     53        WebCore::TextGranularity::WordGranularity,
     54        WebCore::TextGranularity::SentenceGranularity,
     55        WebCore::TextGranularity::LineGranularity,
     56        WebCore::TextGranularity::ParagraphGranularity,
     57        WebCore::TextGranularity::DocumentGranularity,
     58        WebCore::TextGranularity::SentenceBoundary,
     59        WebCore::TextGranularity::LineBoundary,
     60        WebCore::TextGranularity::ParagraphBoundary,
     61        WebCore::TextGranularity::DocumentBoundary
     62    >;
     63};
     64
     65}
  • trunk/Source/WebCore/editing/TextIterator.cpp

    r243163 r243354  
    26852685}
    26862686
     2687String plainTextReplacingNoBreakSpace(Position start, Position end, TextIteratorBehavior defaultBehavior, bool isDisplayString)
     2688{
     2689    return plainText(start, end, defaultBehavior, isDisplayString).replace(noBreakSpace, ' ');
     2690}
     2691
    26872692String plainText(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
    26882693{
  • trunk/Source/WebCore/editing/TextIterator.h

    r241183 r243354  
    4545
    4646WEBCORE_EXPORT String plainText(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
     47WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
    4748
    4849WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
     
    103104    WTF_MAKE_FAST_ALLOCATED;
    104105public:
    105     explicit TextIterator(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior);
     106    WEBCORE_EXPORT explicit TextIterator(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior);
    106107    WEBCORE_EXPORT explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
    107108    WEBCORE_EXPORT ~TextIterator();
  • trunk/Source/WebKit/ChangeLog

    r243351 r243354  
     12019-03-21  Tim Horton  <timothy_horton@apple.com>
     2
     3        Adopt UIWKDocumentContext
     4        https://bugs.webkit.org/show_bug.cgi?id=196040
     5        <rdar://problem/48642440>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        * Platform/spi/ios/UIKitSPI.h:
     10        * Scripts/webkit/messages.py:
     11        * Shared/DocumentEditingContext.h: Added.
     12        * Shared/DocumentEditingContext.mm: Added.
     13        (WebKit::toNSRange):
     14        (WebKit::DocumentEditingContext::toPlatformContext):
     15        (IPC::ArgumentCoder<WebKit::DocumentEditingContext::Range>::encode):
     16        (IPC::ArgumentCoder<WebKit::DocumentEditingContext::Range>::decode):
     17        (IPC::ArgumentCoder<WebKit::DocumentEditingContext::TextRect>::encode):
     18        (IPC::ArgumentCoder<WebKit::DocumentEditingContext::TextRect>::decode):
     19        (IPC::ArgumentCoder<WebKit::DocumentEditingContext>::encode):
     20        (IPC::ArgumentCoder<WebKit::DocumentEditingContext>::decode):
     21        (IPC::ArgumentCoder<WebKit::DocumentEditingContextRequest>::encode):
     22        (IPC::ArgumentCoder<WebKit::DocumentEditingContextRequest>::decode):
     23        Add DocumentEditingContext(Request), and coders.
     24        Also expose DocumentEditingContext::toPlatformContext, which populates
     25        a UIWKDocumentContext with the relevant values.
     26
     27        * SourcesCocoa.txt:
     28        * UIProcess/WebPageProxy.h:
     29        * UIProcess/ios/WKContentViewInteraction.mm:
     30        (toWebDocumentRequestOptions):
     31        (toWebRequest):
     32        (-[WKContentView adjustSelectionWithDelta:completionHandler:]):
     33        (-[WKContentView requestDocumentContext:completionHandler:]):
     34        (-[WKContentView selectPositionAtPoint:withContextRequest:completionHandler:]):
     35        * UIProcess/ios/WebPageProxyIOS.mm:
     36        (WebKit::WebPageProxy::adjustSelectionWithDelta):
     37        (WebKit::WebPageProxy::requestDocumentEditingContext):
     38        * WebKit.xcodeproj/project.pbxproj:
     39        * WebProcess/WebPage/WebPage.h:
     40        * WebProcess/WebPage/WebPage.messages.in:
     41        Plumb DocumentEditingContext(Request) around.
     42
     43        * WebProcess/WebPage/ios/WebPageIOS.mm:
     44        (WebKit::WebPage::adjustSelectionWithDelta):
     45        Adjust the current selection given deltas to apply to the location and length.
     46
     47        (WebKit::visiblePositionAdjacentToVisiblePosition):
     48        (WebKit::visiblePositionForPointInRootViewCoordinates):
     49        (WebKit::WebPage::requestDocumentEditingContext):
     50        Retrieve the relevant part of the selection, as well as the context,
     51        given either the selection, or a rect.
     52        If we're collecting context for a rect, we split the context between
     53        before and after (except for the part that intersects the selection).
     54        The three strings will always be directly adjacent, and any of the three
     55        can sometimes be null.
     56
    1572019-03-21  James Magahern  <jmagahern@apple.com>
    258
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r243113 r243354  
    11411141}
    11421142
     1143@interface UIWKDocumentContext : NSObject
     1144
     1145@property (nonatomic, copy) NSObject *contextBefore;
     1146@property (nonatomic, copy) NSObject *selectedText;
     1147@property (nonatomic, copy) NSObject *contextAfter;
     1148@property (nonatomic, copy) NSObject *markedText;
     1149@property (nonatomic, assign) NSRange selectedRangeInMarkedText;
     1150@property (nonatomic, copy) NSAttributedString *annotatedText;
     1151
     1152- (void)addTextRect:(CGRect)rect forCharacterRange:(NSRange)range;
     1153
     1154@end
     1155
     1156typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
     1157    UIWKDocumentRequestNone = 0,
     1158    UIWKDocumentRequestText = 1 << 0,
     1159    UIWKDocumentRequestAttributed = 1 << 1,
     1160    UIWKDocumentRequestRects = 1 << 2,
     1161    UIWKDocumentRequestSpatial = 1 << 3,
     1162    UIWKDocumentRequestAnnotation = 1 << 4,
     1163};
     1164
     1165@interface UIWKDocumentRequest : NSObject
     1166@property (nonatomic, assign) UIWKDocumentRequestFlags flags;
     1167@property (nonatomic, assign) UITextGranularity surroundingGranularity;
     1168@property (nonatomic, assign) NSInteger granularityCount;
     1169@property (nonatomic, assign) CGRect documentRect;
     1170@property (nonatomic, retain) id <NSCopying> inputElementIdentifier;
     1171@end
     1172
    11431173WTF_EXTERN_C_BEGIN
    11441174
  • trunk/Source/WebKit/Scripts/webkit/messages.py

    r243345 r243354  
    398398        'WebCore::AutoplayEventFlags': ['<WebCore/AutoplayEvent.h>'],
    399399        'WebCore::DOMPasteAccessResponse': ['<WebCore/DOMPasteAccess.h>'],
     400        'WebKit::DocumentEditingContextRequest': ['"DocumentEditingContext.h"'],
    400401        'WebCore::DragHandlingMethod': ['<WebCore/DragActions.h>'],
    401402        'WebCore::ExceptionDetails': ['<WebCore/JSDOMExceptionHandling.h>'],
  • trunk/Source/WebKit/SourcesCocoa.txt

    r243319 r243354  
    126126Shared/APIWebArchive.mm
    127127Shared/APIWebArchiveResource.mm
     128Shared/DocumentEditingContext.mm
    128129Shared/FocusedElementInformation.cpp
    129130Shared/VisibleContentRectUpdateInfo.cpp
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r243328 r243354  
    277277struct AttributedString;
    278278struct ColorSpaceData;
     279struct DocumentEditingContext;
     280struct DocumentEditingContextRequest;
    279281struct EditingRange;
    280282struct EditorState;
     
    709711    bool isScrollingOrZooming() const { return m_isScrollingOrZooming; }
    710712    void requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&&);
     713    void updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&&);
     714    void requestDocumentEditingContext(WebKit::DocumentEditingContextRequest, CompletionHandler<void(WebKit::DocumentEditingContext)>&&);
    711715#if ENABLE(DATA_INTERACTION)
    712716    void didHandleDragStartRequest(bool started);
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r243351 r243354  
    3030
    3131#import "APIUIClient.h"
     32#import "DocumentEditingContext.h"
    3233#import "EditableImageController.h"
    3334#import "EditingRange.h"
     
    7475#import "_WKFocusedElementInfo.h"
    7576#import "_WKInputDelegate.h"
     77#import "_WKTextInputContextInternal.h"
    7678#import <CoreText/CTFont.h>
    7779#import <CoreText/CTFontDescriptor.h>
     
    62836285}
    62846286
     6287
     6288static inline OptionSet<WebKit::DocumentEditingContextRequest::Options> toWebDocumentRequestOptions(UIWKDocumentRequestFlags flags)
     6289{
     6290    OptionSet<WebKit::DocumentEditingContextRequest::Options> options;
     6291
     6292    if (flags & UIWKDocumentRequestText)
     6293        options.add(WebKit::DocumentEditingContextRequest::Options::Text);
     6294    if (flags & UIWKDocumentRequestAttributed)
     6295        options.add(WebKit::DocumentEditingContextRequest::Options::AttributedText);
     6296    if (flags & UIWKDocumentRequestRects)
     6297        options.add(WebKit::DocumentEditingContextRequest::Options::Rects);
     6298    if (flags & UIWKDocumentRequestSpatial)
     6299        options.add(WebKit::DocumentEditingContextRequest::Options::Spatial);
     6300    if (flags & UIWKDocumentRequestAnnotation)
     6301        options.add(WebKit::DocumentEditingContextRequest::Options::Annotation);
     6302
     6303    return options;
     6304}
     6305
     6306static WebKit::DocumentEditingContextRequest toWebRequest(UIWKDocumentRequest *request)
     6307{
     6308    WebKit::DocumentEditingContextRequest webRequest = {
     6309        .options = toWebDocumentRequestOptions(request.flags),
     6310        .surroundingGranularity = toWKTextGranularity(request.surroundingGranularity),
     6311        .granularityCount = request.granularityCount,
     6312        .rect = request.documentRect
     6313    };
     6314
     6315    if (auto textInputContext = dynamic_objc_cast<_WKTextInputContext>(request.inputElementIdentifier))
     6316        webRequest.textInputContext = [textInputContext _textInputContext];
     6317
     6318    return webRequest;
     6319}
     6320
     6321- (void)adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler
     6322{
     6323    // UIKit is putting casted signed integers into NSRange. Cast them back to reveal any negative values.
     6324    _page->updateSelectionWithDelta(static_cast<int64_t>(deltaRange.location), static_cast<int64_t>(deltaRange.length), [capturedCompletionHandler = makeBlockPtr(completionHandler)] {
     6325        capturedCompletionHandler();
     6326    });
     6327}
     6328
     6329- (void)requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
     6330{
     6331    auto webRequest = toWebRequest(request);
     6332    OptionSet<WebKit::DocumentEditingContextRequest::Options> options = webRequest.options;
     6333    _page->requestDocumentEditingContext(webRequest, [capturedCompletionHandler = makeBlockPtr(completionHandler), options] (WebKit::DocumentEditingContext editingContext) {
     6334        capturedCompletionHandler(editingContext.toPlatformContext(options));
     6335    });
     6336}
     6337
     6338- (void)selectPositionAtPoint:(CGPoint)point withContextRequest:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
     6339{
     6340    // FIXME: Reduce to 1 message.
     6341    [self selectPositionAtPoint:point completionHandler:^{
     6342        [self requestDocumentContext:request completionHandler:^(UIWKDocumentContext *context) {
     6343            completionHandler(context);
     6344        }];
     6345    }];
     6346}
     6347
     6348
    62856349#pragma mark - UIDragInteractionDelegate
    62866350
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r243327 r243354  
    3232#import "Connection.h"
    3333#import "DataReference.h"
     34#import "DocumentEditingContext.h"
    3435#import "EditingRange.h"
    3536#import "GlobalFindInPageState.h"
     
    11381139}
    11391140
     1141void WebPageProxy::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
     1142{
     1143    if (!hasRunningProcess()) {
     1144        completionHandler();
     1145        return;
     1146    }
     1147
     1148    m_process->connection()->sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithDelta(locationDelta, lengthDelta), WTFMove(completionHandler), m_pageID);
     1149}
     1150
     1151void WebPageProxy::requestDocumentEditingContext(WebKit::DocumentEditingContextRequest request, CompletionHandler<void(WebKit::DocumentEditingContext)>&& completionHandler)
     1152{
     1153    if (!hasRunningProcess()) {
     1154        completionHandler({ });
     1155        return;
     1156    }
     1157
     1158    m_process->connection()->sendWithAsyncReply(Messages::WebPage::RequestDocumentEditingContext(request), WTFMove(completionHandler), m_pageID);
     1159}
     1160
    11401161#if ENABLE(DATA_INTERACTION)
    11411162
  • trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj

    r243319 r243354  
    27102710                2D7DEE2E21269E4E00B9F73C /* UnifiedSource57-mm.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "UnifiedSource57-mm.mm"; path = "DerivedSources/WebKit2/unified-sources/UnifiedSource57-mm.mm"; sourceTree = BUILT_PRODUCTS_DIR; };
    27112711                2D7DEE3121269E4E00B9F73C /* UnifiedSource12-mm.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "UnifiedSource12-mm.mm"; path = "DerivedSources/WebKit2/unified-sources/UnifiedSource12-mm.mm"; sourceTree = BUILT_PRODUCTS_DIR; };
     2712                2D7FD190223C730F007887F1 /* DocumentEditingContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DocumentEditingContext.h; sourceTree = "<group>"; };
     2713                2D7FD191223C7310007887F1 /* DocumentEditingContext.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DocumentEditingContext.mm; sourceTree = "<group>"; };
    27122714                2D819B99186275B3001F03D1 /* ViewGestureGeometryCollector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ViewGestureGeometryCollector.cpp; sourceTree = "<group>"; };
    27132715                2D819B9A186275B3001F03D1 /* ViewGestureGeometryCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewGestureGeometryCollector.h; sourceTree = "<group>"; };
     
    50835085                                5106D7C018BDBE73000AB166 /* ContextMenuContextData.h */,
    50845086                                99F642D21FABE378009621E9 /* CoordinateSystem.h */,
     5087                                2D7FD190223C730F007887F1 /* DocumentEditingContext.h */,
     5088                                2D7FD191223C7310007887F1 /* DocumentEditingContext.mm */,
    50855089                                C517388012DF8F4F00EE3F47 /* DragControllerAction.h */,
    50865090                                0FB659221208B4DB0044816C /* DrawingAreaInfo.h */,
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r243328 r243354  
    65886588void WebPage::focusTextInputContext(const TextInputContext& textInputContext, CompletionHandler<void(bool)>&& completionHandler)
    65896589{
    6590     completionHandler([&] {
    6591         if (textInputContext.webPageIdentifier != m_pageID)
    6592             return false;
    6593 
    6594         auto* document = Document::allDocumentsMap().get(textInputContext.documentIdentifier);
    6595         if (!document)
    6596             return false;
    6597 
    6598         if (document->page() != m_page.get())
    6599             return false;
    6600 
    6601         auto* element = document->searchForElementByIdentifier(textInputContext.elementIdentifier);
    6602         if (!element)
    6603             return false;
    6604 
     6590    RefPtr<Element> element = elementForTextInputContext(textInputContext);
     6591
     6592    if (element)
    66056593        element->focus();
    66066594
    6607         return true;
    6608     }());
     6595    completionHandler(element);
     6596}
     6597
     6598Element* WebPage::elementForTextInputContext(const TextInputContext& textInputContext)
     6599{
     6600    if (textInputContext.webPageIdentifier != m_pageID)
     6601        return nullptr;
     6602
     6603    auto* document = Document::allDocumentsMap().get(textInputContext.documentIdentifier);
     6604    if (!document)
     6605        return nullptr;
     6606
     6607    if (document->page() != m_page.get())
     6608        return nullptr;
     6609
     6610    return document->searchForElementByIdentifier(textInputContext.elementIdentifier);
    66096611}
    66106612
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r243328 r243354  
    687687    bool forceAlwaysUserScalable() const { return m_forceAlwaysUserScalable; }
    688688    void setForceAlwaysUserScalable(bool);
     689
     690    void updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&&);
     691    void requestDocumentEditingContext(WebKit::DocumentEditingContextRequest, CompletionHandler<void(WebKit::DocumentEditingContext)>&&);
    689692#endif
    690693
     
    11661169
    11671170    void configureLoggingChannel(const String&, WTFLogChannelState, WTFLogLevel);
     1171
     1172    WebCore::Element* elementForTextInputContext(const TextInputContext&);
    11681173
    11691174#if ENABLE(APPLE_PAY)
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r243345 r243354  
    111111    HardwareKeyboardAvailabilityChanged(bool keyboardIsAttached)
    112112    SetIsShowingInputViewForFocusedElement(bool showingInputView)
     113    UpdateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta) -> () Async
     114    RequestDocumentEditingContext(struct WebKit::DocumentEditingContextRequest request) -> (struct WebKit::DocumentEditingContext response) Async
    113115#endif
    114116
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r243326 r243354  
    3131#import "AccessibilityIOS.h"
    3232#import "DataReference.h"
     33#import "DocumentEditingContext.h"
    3334#import "DrawingArea.h"
    3435#import "EditingRange.h"
     
    33133314}
    33143315
     3316void WebPage::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
     3317{
     3318    Ref<Frame> frame = corePage()->focusController().focusedOrMainFrame();
     3319    VisibleSelection selection = frame->selection().selection();
     3320    if (selection.isNone()) {
     3321        completionHandler();
     3322        return;
     3323    }
     3324
     3325    auto root = frame->selection().rootEditableElementOrDocumentElement();
     3326    auto range = selection.toNormalizedRange();
     3327    if (!root || !range) {
     3328        completionHandler();
     3329        return;
     3330    }
     3331
     3332    size_t selectionLocation;
     3333    size_t selectionLength;
     3334    TextIterator::getLocationAndLengthFromRange(root, range.get(), selectionLocation, selectionLength);
     3335
     3336    CheckedInt64 newSelectionLocation { selectionLocation };
     3337    CheckedInt64 newSelectionLength { selectionLength };
     3338    newSelectionLocation += locationDelta;
     3339    newSelectionLength += lengthDelta;
     3340
     3341    if (newSelectionLocation.hasOverflowed() || newSelectionLength.hasOverflowed()) {
     3342        completionHandler();
     3343        return;
     3344    }
     3345
     3346    if (auto range = TextIterator::rangeFromLocationAndLength(root, newSelectionLocation.unsafeGet(), newSelectionLength.unsafeGet()))
     3347        frame->selection().setSelectedRange(range.get(), DOWNSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
     3348
     3349    completionHandler();
     3350}
     3351
     3352static VisiblePosition moveByGranularityRespectingWordBoundary(Frame& frame, VisiblePosition& position, TextGranularity granularity, uint64_t granularityCount, SelectionDirection direction)
     3353{
     3354    bool backwards = direction == DirectionBackward;
     3355    auto farthestPositionInDirection = backwards ? startOfEditableContent(position) : endOfEditableContent(position);
     3356    if (position == farthestPositionInDirection)
     3357        return { };
     3358
     3359    VisiblePosition currentPosition = position;
     3360    VisiblePosition nextPosition;
     3361    for (unsigned i = 0; i < granularityCount + 1; ++i) {
     3362        nextPosition = positionOfNextBoundaryOfGranularity(currentPosition, granularity, direction);
     3363        // FIXME (196127): We shouldn't need to do this, but have seen previousParagraphPosition go forwards.
     3364        if ((backwards && nextPosition > currentPosition) || (!backwards && nextPosition < currentPosition))
     3365            break;
     3366        if (nextPosition.isNull())
     3367            break;
     3368        currentPosition = nextPosition;
     3369    }
     3370
     3371    return backwards ? startOfWord(currentPosition) : endOfWord(currentPosition);
     3372}
     3373
     3374static VisiblePosition visiblePositionForPointInRootViewCoordinates(Frame& frame, FloatPoint pointInRootViewCoordinates)
     3375{
     3376    auto pointInDocument = frame.view()->rootViewToContents(roundedIntPoint(pointInRootViewCoordinates));
     3377    return frame.visiblePositionForPoint(pointInDocument);
     3378}
     3379
     3380void WebPage::requestDocumentEditingContext(DocumentEditingContextRequest request, CompletionHandler<void(DocumentEditingContext)>&& completionHandler)
     3381{
     3382    if (!request.options.contains(DocumentEditingContextRequest::Options::Text) && !request.options.contains(DocumentEditingContextRequest::Options::AttributedText)) {
     3383        completionHandler({ });
     3384        return;
     3385    }
     3386
     3387    m_page->focusController().focusedOrMainFrame().document()->updateLayoutIgnorePendingStylesheets();
     3388
     3389    Ref<Frame> frame = m_page->focusController().focusedOrMainFrame();
     3390    VisibleSelection selection = frame->selection().selection();
     3391
     3392    VisiblePosition rangeOfInterestStart;
     3393    VisiblePosition rangeOfInterestEnd;
     3394    VisiblePosition selectionStart = selection.visibleStart();
     3395    VisiblePosition selectionEnd = selection.visibleEnd();
     3396
     3397    bool isSpatialRequest = request.options.contains(DocumentEditingContextRequest::Options::Spatial);
     3398    bool wantsRects = request.options.contains(DocumentEditingContextRequest::Options::Rects);
     3399
     3400    if (auto textInputContext = request.textInputContext) {
     3401        RefPtr<Element> element = elementForTextInputContext(*textInputContext);
     3402        if (!element) {
     3403            completionHandler({ });
     3404            return;
     3405        }
     3406        if (is<HTMLTextFormControlElement>(element)) {
     3407            auto& textFormControlElement = downcast<HTMLTextFormControlElement>(*element);
     3408            rangeOfInterestStart = textFormControlElement.visiblePositionForIndex(0);
     3409            rangeOfInterestEnd = textFormControlElement.visiblePositionForIndex(textFormControlElement.value().length());
     3410        } else {
     3411            rangeOfInterestStart = firstPositionInOrBeforeNode(element.get());
     3412            rangeOfInterestEnd = lastPositionInOrAfterNode(element.get());
     3413        }
     3414    } else if (isSpatialRequest) {
     3415        // FIXME: We might need to be a bit more careful that we get something useful (test the other corners?).
     3416        rangeOfInterestStart = visiblePositionForPointInRootViewCoordinates(frame.get(), request.rect.minXMinYCorner());
     3417        rangeOfInterestEnd = visiblePositionForPointInRootViewCoordinates(frame.get(), request.rect.maxXMaxYCorner());
     3418        if (rangeOfInterestEnd < rangeOfInterestStart)
     3419            std::exchange(rangeOfInterestStart, rangeOfInterestEnd);
     3420    } else if (!selection.isNone()) {
     3421        rangeOfInterestStart = selectionStart;
     3422        rangeOfInterestEnd = selectionEnd;
     3423    }
     3424
     3425    if (rangeOfInterestStart.isNull() || rangeOfInterestStart.isOrphan() || rangeOfInterestEnd.isNull() || rangeOfInterestEnd.isOrphan()) {
     3426        completionHandler({ });
     3427        return;
     3428    }
     3429
     3430    DocumentEditingContext context;
     3431
     3432    // The subset of the selection that is inside the range of interest.
     3433    VisiblePosition startOfRangeOfInterestInSelection;
     3434    VisiblePosition endOfRangeOfInterestInSelection;
     3435
     3436    auto selectionRange = selection.toNormalizedRange();
     3437    auto rangeOfInterest = makeRange(rangeOfInterestStart, rangeOfInterestEnd);
     3438    if (selectionRange && rangesOverlap(rangeOfInterest.get(), selectionRange.get())) {
     3439        startOfRangeOfInterestInSelection = rangeOfInterestStart > selectionStart ? rangeOfInterestStart : selectionStart;
     3440        endOfRangeOfInterestInSelection = rangeOfInterestEnd < selectionEnd ? rangeOfInterestEnd : selectionEnd;
     3441    } else {
     3442        size_t rangeOfInterestLocation;
     3443        size_t rangeOfInterestLength;
     3444        RefPtr<Node> rootNode = rangeOfInterest->commonAncestorContainer();
     3445        if (!rootNode) {
     3446            completionHandler({ });
     3447            return;
     3448        }
     3449
     3450        RefPtr<ContainerNode> rootContainerNode = rootNode->isContainerNode() ? downcast<ContainerNode>(rootNode.get()) : rootNode->parentNode();
     3451        TextIterator::getLocationAndLengthFromRange(rootContainerNode.get(), rangeOfInterest.get(), rangeOfInterestLocation, rangeOfInterestLength);
     3452
     3453        CheckedSize midpointLocation { rangeOfInterestLocation };
     3454        midpointLocation += rangeOfInterestLength / 2;
     3455        if (midpointLocation.hasOverflowed()) {
     3456            completionHandler({ });
     3457            return;
     3458        }
     3459
     3460        auto midpointRange = TextIterator::rangeFromLocationAndLength(rootContainerNode.get(), midpointLocation.unsafeGet(), 0);
     3461
     3462        auto midpoint = midpointRange->startPosition();
     3463        startOfRangeOfInterestInSelection = startOfWord(midpoint);
     3464        if (startOfRangeOfInterestInSelection < rangeOfInterestStart) {
     3465            startOfRangeOfInterestInSelection = endOfWord(midpoint);
     3466            if (startOfRangeOfInterestInSelection > rangeOfInterestEnd)
     3467                startOfRangeOfInterestInSelection = midpoint;
     3468        }
     3469
     3470        endOfRangeOfInterestInSelection = startOfRangeOfInterestInSelection;
     3471    }
     3472
     3473    VisiblePosition contextBeforeStart;
     3474    VisiblePosition contextAfterEnd;
     3475    if (request.granularityCount) {
     3476        contextBeforeStart = moveByGranularityRespectingWordBoundary(frame.get(), rangeOfInterestStart, request.surroundingGranularity, request.granularityCount, DirectionBackward);
     3477        contextAfterEnd = moveByGranularityRespectingWordBoundary(frame.get(), rangeOfInterestEnd, request.surroundingGranularity, request.granularityCount, DirectionForward);
     3478    } else {
     3479        contextBeforeStart = rangeOfInterestStart;
     3480        contextAfterEnd = rangeOfInterestEnd;
     3481    }
     3482
     3483    auto makeString = [&](VisiblePosition& start, VisiblePosition& end) -> NSAttributedString * {
     3484        if (start.isNull() || end.isNull() || start == end)
     3485            return nil;
     3486        // FIXME: This should return editing-offset-compatible attributed strings if that option is requested.
     3487        return adoptNS([[NSAttributedString alloc] initWithString:plainTextReplacingNoBreakSpace(start.deepEquivalent(), end.deepEquivalent())]).autorelease();
     3488    };
     3489
     3490    context.contextBefore = makeString(contextBeforeStart, startOfRangeOfInterestInSelection);
     3491    context.selectedText = makeString(startOfRangeOfInterestInSelection, endOfRangeOfInterestInSelection);
     3492    context.contextAfter = makeString(endOfRangeOfInterestInSelection, contextAfterEnd);
     3493
     3494    auto compositionRange = frame->editor().compositionRange();
     3495    if (compositionRange && rangesOverlap(rangeOfInterest.get(), compositionRange.get())) {
     3496        VisiblePosition compositionStart(compositionRange->startPosition());
     3497        VisiblePosition compositionEnd(compositionRange->endPosition());
     3498
     3499        VisiblePosition relevantCompositionStart = rangeOfInterestStart > compositionStart ? rangeOfInterestStart : compositionStart;
     3500        VisiblePosition relevantCompositionEnd = rangeOfInterestEnd < compositionEnd ? rangeOfInterestEnd : compositionEnd;
     3501
     3502        context.markedText = makeString(relevantCompositionStart, relevantCompositionEnd);
     3503        context.selectedRangeInMarkedText.location = distanceBetweenPositions(relevantCompositionStart, startOfRangeOfInterestInSelection);
     3504        context.selectedRangeInMarkedText.length = [context.selectedText.string length];
     3505    }
     3506
     3507    if (wantsRects) {
     3508        TextIterator contextIterator(contextBeforeStart.deepEquivalent(), contextAfterEnd.deepEquivalent());
     3509        unsigned currentLocation = 0;
     3510        while (!contextIterator.atEnd()) {
     3511            unsigned length = contextIterator.text().length();
     3512            if (!length) {
     3513                contextIterator.advance();
     3514                continue;
     3515            }
     3516
     3517            DocumentEditingContext::TextRectAndRange rect;
     3518            rect.rect = contextIterator.range()->absoluteBoundingBox();
     3519            rect.range = { currentLocation, length };
     3520            context.textRects.append(rect);
     3521
     3522            currentLocation += length;
     3523            contextIterator.advance();
     3524        }
     3525    }
     3526
     3527    // FIXME: Support Annotation option.
     3528
     3529    completionHandler(context);
     3530}
     3531
    33153532} // namespace WebKit
    33163533
  • trunk/Tools/ChangeLog

    r243347 r243354  
     12019-03-21  Tim Horton  <timothy_horton@apple.com>
     2
     3        Adopt UIWKDocumentContext
     4        https://bugs.webkit.org/show_bug.cgi?id=196040
     5        <rdar://problem/48642440>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     10        * TestWebKitAPI/Tests/WebKitCocoa/DocumentEditingContext.mm: Added.
     11        (makeRequest):
     12        (-[TestWKWebView synchronouslyRequestDocumentContext:]):
     13        (-[TestWKWebView synchronouslyAdjustSelectionWithDelta:]):
     14        (applyStyle):
     15        (TEST):
     16        Add tests for UIWKDocumentContext.
     17
     18        * TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm:
     19        (TEST):
     20        (recursiveFindWKContentView): Deleted.
     21        * TestWebKitAPI/cocoa/TestWKWebView.h:
     22        * TestWebKitAPI/cocoa/TestWKWebView.mm:
     23        (recursiveFindWKContentView):
     24        (-[TestWKWebView wkContentView]):
     25        * TestWebKitAPI/ios/UIKitSPI.h:
     26        Share the WKContentView finding code between tests.
     27
    1282019-03-21  Simon Fraser  <simon.fraser@apple.com>
    229
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r243324 r243354  
    9999                2D70059621EDA0C6003463CB /* TabOutOfWebView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */; };
    100100                2D70059921EDA4D0003463CB /* OffscreenWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059721EDA4D0003463CB /* OffscreenWindow.mm */; };
     101                2D7FD19322419087007887F1 /* DocumentEditingContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D7FD19222419087007887F1 /* DocumentEditingContext.mm */; };
    101102                2D838B1F1EEF3A5C009B980E /* WKContentViewEditingActions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */; };
    102103                2DADF26321CB8F32003D3E3A /* GetResourceData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DADF26221CB8F32003D3E3A /* GetResourceData.mm */; };
     
    14541455                2D70059721EDA4D0003463CB /* OffscreenWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OffscreenWindow.mm; sourceTree = "<group>"; };
    14551456                2D70059821EDA4D0003463CB /* OffscreenWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OffscreenWindow.h; sourceTree = "<group>"; };
     1457                2D7FD19222419087007887F1 /* DocumentEditingContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DocumentEditingContext.mm; sourceTree = "<group>"; };
    14561458                2D8104CB1BEC13E70020DA46 /* FindInPage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FindInPage.mm; sourceTree = "<group>"; };
    14571459                2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKContentViewEditingActions.mm; sourceTree = "<group>"; };
     
    25492551                                CEA7F57B20895F5B0078EF6E /* DidResignInputElementStrongPasswordAppearance.mm */,
    25502552                                518EE51A20A78CFB00E024F3 /* DoAfterNextPresentationUpdateAfterCrash.mm */,
     2553                                2D7FD19222419087007887F1 /* DocumentEditingContext.mm */,
    25512554                                518EE51620A78CDF00E024F3 /* DoubleDefersLoading.mm */,
    25522555                                518EE51720A78CDF00E024F3 /* DoubleDefersLoadingPlugin.mm */,
     
    40724075                                AD57AC211DA7465B00FF1BDE /* DidRemoveFrameFromHiearchyInPageCache.cpp in Sources */,
    40734076                                518EE51B20A78D0000E024F3 /* DoAfterNextPresentationUpdateAfterCrash.mm in Sources */,
     4077                                2D7FD19322419087007887F1 /* DocumentEditingContext.mm in Sources */,
    40744078                                7CCE7EEB1A411AE600447C4C /* DocumentStartUserScriptAlertCrash.cpp in Sources */,
    40754079                                7CCE7EBB1A411A7E00447C4C /* DOMHTMLTableCellCellAbove.mm in Sources */,
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm

    r237266 r243354  
    3535#import <wtf/RetainPtr.h>
    3636
    37 static UIView *recursiveFindWKContentView(UIView *view)
    38 {
    39     if ([view isKindOfClass:NSClassFromString(@"WKContentView")])
    40         return view;
    41 
    42     for (UIView *subview in view.subviews) {
    43         UIView *contentView = recursiveFindWKContentView(subview);
    44         if (contentView)
    45             return contentView;
    46     }
    47 
    48     return nil;
    49 }
    50 
    5137TEST(WebKit, WKContentViewEditingActions)
    5238{
     
    5945    [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
    6046
    61     UIView *contentView = recursiveFindWKContentView(webView.get());
     47    UIView *contentView = [webView wkContentView];
    6248
    6349    __block bool done = false;
  • trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h

    r242339 r243354  
    8080
    8181#if PLATFORM(IOS_FAMILY)
     82@interface WKContentView : UIView
     83@end
     84
    8285@interface TestWKWebView (IOSOnly)
    8386@property (nonatomic, readonly) UIView <UITextInputPrivate, UITextInputMultiDocument> *textInputContentView;
     
    8790- (_WKActivatedElementInfo *)activatedElementAtPosition:(CGPoint)position;
    8891- (void)evaluateJavaScriptAndWaitForInputSessionToChange:(NSString *)script;
     92- (WKContentView *)wkContentView;
    8993@end
    9094#endif
  • trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm

    r242339 r243354  
    510510}
    511511
     512static WKContentView *recursiveFindWKContentView(UIView *view)
     513{
     514    if ([view isKindOfClass:NSClassFromString(@"WKContentView")])
     515        return (WKContentView *)view;
     516
     517    for (UIView *subview in view.subviews) {
     518        WKContentView *contentView = recursiveFindWKContentView(subview);
     519        if (contentView)
     520            return contentView;
     521    }
     522
     523    return nil;
     524}
     525
     526- (WKContentView *)wkContentView
     527{
     528    return recursiveFindWKContentView(self);
     529}
     530
    512531@end
    513532
  • trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h

    r239026 r243354  
    142142@end
    143143
     144@interface UIWKDocumentContext : NSObject
     145
     146@property (nonatomic, copy) NSObject *contextBefore;
     147@property (nonatomic, copy) NSObject *selectedText;
     148@property (nonatomic, copy) NSObject *contextAfter;
     149@property (nonatomic, copy) NSObject *markedText;
     150@property (nonatomic, assign) NSRange selectedRangeInMarkedText;
     151@property (nonatomic, copy) NSAttributedString *annotatedText;
     152
     153- (NSArray<NSValue *> *)characterRectsForCharacterRange:(NSRange)range;
     154
     155@end
     156
     157typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
     158    UIWKDocumentRequestNone = 0,
     159    UIWKDocumentRequestText = 1 << 0,
     160    UIWKDocumentRequestAttributed = 1 << 1,
     161    UIWKDocumentRequestRects = 1 << 2,
     162    UIWKDocumentRequestSpatial = 1 << 3,
     163    UIWKDocumentRequestAnnotation = 1 << 4,
     164};
     165
     166@interface UIWKDocumentRequest : NSObject
     167
     168@property (nonatomic, assign) UIWKDocumentRequestFlags flags;
     169@property (nonatomic, assign) UITextGranularity surroundingGranularity;
     170@property (nonatomic, assign) NSInteger granularityCount;
     171@property (nonatomic, assign) CGRect documentRect;
     172@property (nonatomic, retain) id <NSCopying> inputElementIdentifier;
     173
     174@end
     175
    144176#endif // PLATFORM(IOS_FAMILY)
Note: See TracChangeset for help on using the changeset viewer.