Title: [243354] trunk
Revision
243354
Author
timothy_hor...@apple.com
Date
2019-03-21 19:26:09 -0700 (Thu, 21 Mar 2019)

Log 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.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (243353 => 243354)


--- trunk/Source/WebCore/ChangeLog	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebCore/ChangeLog	2019-03-22 02:26:09 UTC (rev 243354)
@@ -1,3 +1,22 @@
+2019-03-21  Tim Horton  <timothy_hor...@apple.com>
+
+        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.
+
 2019-03-21  Sihui Liu  <sihui_...@apple.com>
 
         Fix key path extraction code in IndexedDB to check own property

Modified: trunk/Source/WebCore/dom/Range.h (243353 => 243354)


--- trunk/Source/WebCore/dom/Range.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebCore/dom/Range.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -176,7 +176,7 @@
 WEBCORE_EXPORT Ref<Range> rangeOfContents(Node&);
 
 WEBCORE_EXPORT bool areRangesEqual(const Range*, const Range*);
-bool rangesOverlap(const Range*, const Range*);
+WEBCORE_EXPORT bool rangesOverlap(const Range*, const Range*);
 
 inline bool documentOrderComparator(const Node* a, const Node* b)
 {

Modified: trunk/Source/WebCore/editing/TextGranularity.h (243353 => 243354)


--- trunk/Source/WebCore/editing/TextGranularity.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebCore/editing/TextGranularity.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -43,3 +43,23 @@
 };
 
 } // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::TextGranularity> {
+    using values = EnumValues<
+        WebCore::TextGranularity,
+        WebCore::TextGranularity::CharacterGranularity,
+        WebCore::TextGranularity::WordGranularity,
+        WebCore::TextGranularity::SentenceGranularity,
+        WebCore::TextGranularity::LineGranularity,
+        WebCore::TextGranularity::ParagraphGranularity,
+        WebCore::TextGranularity::DocumentGranularity,
+        WebCore::TextGranularity::SentenceBoundary,
+        WebCore::TextGranularity::LineBoundary,
+        WebCore::TextGranularity::ParagraphBoundary,
+        WebCore::TextGranularity::DocumentBoundary
+    >;
+};
+
+}

Modified: trunk/Source/WebCore/editing/TextIterator.cpp (243353 => 243354)


--- trunk/Source/WebCore/editing/TextIterator.cpp	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebCore/editing/TextIterator.cpp	2019-03-22 02:26:09 UTC (rev 243354)
@@ -2684,6 +2684,11 @@
     return result;
 }
 
+String plainTextReplacingNoBreakSpace(Position start, Position end, TextIteratorBehavior defaultBehavior, bool isDisplayString)
+{
+    return plainText(start, end, defaultBehavior, isDisplayString).replace(noBreakSpace, ' ');
+}
+
 String plainText(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
 {
     if (!range)

Modified: trunk/Source/WebCore/editing/TextIterator.h (243353 => 243354)


--- trunk/Source/WebCore/editing/TextIterator.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebCore/editing/TextIterator.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -44,6 +44,7 @@
 }
 
 WEBCORE_EXPORT String plainText(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
+WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
 
 WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
 WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
@@ -102,7 +103,7 @@
 class TextIterator {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    explicit TextIterator(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior);
+    WEBCORE_EXPORT explicit TextIterator(Position start, Position end, TextIteratorBehavior = TextIteratorDefaultBehavior);
     WEBCORE_EXPORT explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
     WEBCORE_EXPORT ~TextIterator();
 

Modified: trunk/Source/WebKit/ChangeLog (243353 => 243354)


--- trunk/Source/WebKit/ChangeLog	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/ChangeLog	2019-03-22 02:26:09 UTC (rev 243354)
@@ -1,3 +1,59 @@
+2019-03-21  Tim Horton  <timothy_hor...@apple.com>
+
+        Adopt UIWKDocumentContext
+        https://bugs.webkit.org/show_bug.cgi?id=196040
+        <rdar://problem/48642440>
+
+        Reviewed by Ryosuke Niwa.
+
+        * 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.
+
 2019-03-21  James Magahern  <jmagah...@apple.com>
 
         Long press gesture recognizers in WKWebView are conflicting with internal scroll view long press gesture recognizers

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (243353 => 243354)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -1140,6 +1140,36 @@
 #endif
 }
 
+@interface UIWKDocumentContext : NSObject
+
+@property (nonatomic, copy) NSObject *contextBefore;
+@property (nonatomic, copy) NSObject *selectedText;
+@property (nonatomic, copy) NSObject *contextAfter;
+@property (nonatomic, copy) NSObject *markedText;
+@property (nonatomic, assign) NSRange selectedRangeInMarkedText;
+@property (nonatomic, copy) NSAttributedString *annotatedText;
+
+- (void)addTextRect:(CGRect)rect forCharacterRange:(NSRange)range;
+
+@end
+
+typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
+    UIWKDocumentRequestNone = 0,
+    UIWKDocumentRequestText = 1 << 0,
+    UIWKDocumentRequestAttributed = 1 << 1,
+    UIWKDocumentRequestRects = 1 << 2,
+    UIWKDocumentRequestSpatial = 1 << 3,
+    UIWKDocumentRequestAnnotation = 1 << 4,
+};
+
+@interface UIWKDocumentRequest : NSObject
+@property (nonatomic, assign) UIWKDocumentRequestFlags flags;
+@property (nonatomic, assign) UITextGranularity surroundingGranularity;
+@property (nonatomic, assign) NSInteger granularityCount;
+@property (nonatomic, assign) CGRect documentRect;
+@property (nonatomic, retain) id <NSCopying> inputElementIdentifier;
+@end
+
 WTF_EXTERN_C_BEGIN
 
 BOOL UIKeyboardEnabledInputModesAllowOneToManyShortcuts(void);

Modified: trunk/Source/WebKit/Scripts/webkit/messages.py (243353 => 243354)


--- trunk/Source/WebKit/Scripts/webkit/messages.py	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/Scripts/webkit/messages.py	2019-03-22 02:26:09 UTC (rev 243354)
@@ -397,6 +397,7 @@
         'PAL::SessionID': ['<pal/SessionID.h>'],
         'WebCore::AutoplayEventFlags': ['<WebCore/AutoplayEvent.h>'],
         'WebCore::DOMPasteAccessResponse': ['<WebCore/DOMPasteAccess.h>'],
+        'WebKit::DocumentEditingContextRequest': ['"DocumentEditingContext.h"'],
         'WebCore::DragHandlingMethod': ['<WebCore/DragActions.h>'],
         'WebCore::ExceptionDetails': ['<WebCore/JSDOMExceptionHandling.h>'],
         'WebCore::FileChooserSettings': ['<WebCore/FileChooser.h>'],

Added: trunk/Source/WebKit/Shared/DocumentEditingContext.h (0 => 243354)


--- trunk/Source/WebKit/Shared/DocumentEditingContext.h	                        (rev 0)
+++ trunk/Source/WebKit/Shared/DocumentEditingContext.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(IOS_FAMILY)
+
+#include "ArgumentCoders.h"
+#include "AttributedString.h"
+#include "TextInputContext.h"
+#include <WebCore/FloatRect.h>
+#include <WebCore/TextGranularity.h>
+#include <wtf/OptionSet.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+OBJC_CLASS UIWKDocumentContext;
+
+namespace WebKit {
+
+struct DocumentEditingContextRequest {
+    enum class Options : uint8_t {
+        Text = 1 << 0,
+        AttributedText = 1 << 1,
+        Rects = 1 << 2,
+        Spatial = 1 << 3,
+        Annotation = 1 << 4,
+    };
+
+    OptionSet<Options> options;
+
+    WebCore::TextGranularity surroundingGranularity { WebCore::CharacterGranularity };
+    int64_t granularityCount { 0 };
+
+    WebCore::FloatRect rect;
+
+    Optional<WebKit::TextInputContext> textInputContext;
+};
+
+struct DocumentEditingContext {
+    UIWKDocumentContext *toPlatformContext(OptionSet<WebKit::DocumentEditingContextRequest::Options>);
+
+    AttributedString contextBefore;
+    AttributedString selectedText;
+    AttributedString contextAfter;
+    AttributedString markedText;
+    AttributedString annotatedText;
+
+    struct Range {
+        uint64_t location { 0 };
+        uint64_t length { 0 };
+    };
+
+    Range selectedRangeInMarkedText;
+
+    struct TextRectAndRange {
+        WebCore::FloatRect rect;
+        Range range;
+    };
+
+    Vector<TextRectAndRange> textRects;
+};
+
+}
+
+namespace IPC {
+template<> struct ArgumentCoder<WebKit::DocumentEditingContext::Range> {
+    static void encode(Encoder&, const WebKit::DocumentEditingContext::Range&);
+    static Optional<WebKit::DocumentEditingContext::Range> decode(Decoder&);
+};
+
+template<> struct ArgumentCoder<WebKit::DocumentEditingContext::TextRectAndRange> {
+    static void encode(Encoder&, const WebKit::DocumentEditingContext::TextRectAndRange&);
+    static Optional<WebKit::DocumentEditingContext::TextRectAndRange> decode(Decoder&);
+};
+
+template<> struct ArgumentCoder<WebKit::DocumentEditingContext> {
+    static void encode(Encoder&, const WebKit::DocumentEditingContext&);
+    static Optional<WebKit::DocumentEditingContext> decode(Decoder&);
+};
+
+template<> struct ArgumentCoder<WebKit::DocumentEditingContextRequest> {
+    static void encode(Encoder&, const WebKit::DocumentEditingContextRequest&);
+    static Optional<WebKit::DocumentEditingContextRequest> decode(Decoder&);
+};
+}
+
+#endif

Added: trunk/Source/WebKit/Shared/DocumentEditingContext.mm (0 => 243354)


--- trunk/Source/WebKit/Shared/DocumentEditingContext.mm	                        (rev 0)
+++ trunk/Source/WebKit/Shared/DocumentEditingContext.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DocumentEditingContext.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#include "TextInputContext.h"
+#include "UIKitSPI.h"
+#include "WebCoreArgumentCoders.h"
+
+namespace WebKit {
+
+static inline NSRange toNSRange(DocumentEditingContext::Range range)
+{
+    return NSMakeRange(range.location, range.length);
+}
+
+UIWKDocumentContext *DocumentEditingContext::toPlatformContext(OptionSet<DocumentEditingContextRequest::Options> options)
+{
+    RetainPtr<UIWKDocumentContext> platformContext = adoptNS([[NSClassFromString(@"UIWKDocumentContext") alloc] init]);
+
+    if (options.contains(DocumentEditingContextRequest::Options::AttributedText)) {
+        [platformContext setContextBefore:contextBefore.string.get()];
+        [platformContext setSelectedText:selectedText.string.get()];
+        [platformContext setContextAfter:contextAfter.string.get()];
+        [platformContext setMarkedText:markedText.string.get()];
+    } else if (options.contains(DocumentEditingContextRequest::Options::Text)) {
+        [platformContext setContextBefore:[contextBefore.string string]];
+        [platformContext setSelectedText:[selectedText.string string]];
+        [platformContext setContextAfter:[contextAfter.string string]];
+        [platformContext setMarkedText:[markedText.string string]];
+    }
+
+    [platformContext setSelectedRangeInMarkedText:toNSRange(selectedRangeInMarkedText)];
+
+    for (const auto& rect : textRects)
+        [platformContext addTextRect:rect.rect forCharacterRange:toNSRange(rect.range)];
+
+    [platformContext setAnnotatedText:annotatedText.string.get()];
+
+    return platformContext.autorelease();
+}
+
+}
+
+namespace IPC {
+
+void ArgumentCoder<WebKit::DocumentEditingContext::Range>::encode(Encoder& encoder, const WebKit::DocumentEditingContext::Range& range)
+{
+    encoder << range.location;
+    encoder << range.length;
+}
+
+Optional<WebKit::DocumentEditingContext::Range> ArgumentCoder<WebKit::DocumentEditingContext::Range>::decode(Decoder& decoder)
+{
+    WebKit::DocumentEditingContext::Range range;
+
+    if (!decoder.decode(range.location))
+        return WTF::nullopt;
+    if (!decoder.decode(range.length))
+        return WTF::nullopt;
+
+    return range;
+}
+
+void ArgumentCoder<WebKit::DocumentEditingContext::TextRectAndRange>::encode(Encoder& encoder, const WebKit::DocumentEditingContext::TextRectAndRange& rect)
+{
+    encoder << rect.rect;
+    encoder << rect.range;
+}
+
+Optional<WebKit::DocumentEditingContext::TextRectAndRange> ArgumentCoder<WebKit::DocumentEditingContext::TextRectAndRange>::decode(Decoder& decoder)
+{
+    WebKit::DocumentEditingContext::TextRectAndRange rect;
+
+    if (!decoder.decode(rect.rect))
+        return WTF::nullopt;
+
+    Optional<WebKit::DocumentEditingContext::Range> range;
+    decoder >> range;
+    if (!range)
+        return WTF::nullopt;
+    rect.range = *range;
+
+    return rect;
+}
+
+void ArgumentCoder<WebKit::DocumentEditingContext>::encode(Encoder& encoder, const WebKit::DocumentEditingContext& context)
+{
+    encoder << context.contextBefore;
+    encoder << context.selectedText;
+    encoder << context.contextAfter;
+    encoder << context.markedText;
+    encoder << context.annotatedText;
+
+    encoder << context.selectedRangeInMarkedText;
+
+    encoder << context.textRects;
+}
+
+Optional<WebKit::DocumentEditingContext> ArgumentCoder<WebKit::DocumentEditingContext>::decode(Decoder& decoder)
+{
+    WebKit::DocumentEditingContext context;
+
+    Optional<WebKit::AttributedString> contextBefore;
+    decoder >> contextBefore;
+    if (!contextBefore)
+        return WTF::nullopt;
+    context.contextBefore = *contextBefore;
+
+    Optional<WebKit::AttributedString> selectedText;
+    decoder >> selectedText;
+    if (!selectedText)
+        return WTF::nullopt;
+    context.selectedText = *selectedText;
+
+    Optional<WebKit::AttributedString> contextAfter;
+    decoder >> contextAfter;
+    if (!contextAfter)
+        return WTF::nullopt;
+    context.contextAfter = *contextAfter;
+
+    Optional<WebKit::AttributedString> markedText;
+    decoder >> markedText;
+    if (!markedText)
+        return WTF::nullopt;
+    context.markedText = *markedText;
+
+    Optional<WebKit::AttributedString> annotatedText;
+    decoder >> annotatedText;
+    if (!annotatedText)
+        return WTF::nullopt;
+    context.annotatedText = *annotatedText;
+
+    Optional<WebKit::DocumentEditingContext::Range> selectedRangeInMarkedText;
+    decoder >> selectedRangeInMarkedText;
+    if (!selectedRangeInMarkedText)
+        return WTF::nullopt;
+    context.selectedRangeInMarkedText = *selectedRangeInMarkedText;
+
+    if (!decoder.decode(context.textRects))
+        return WTF::nullopt;
+
+    return context;
+}
+
+void ArgumentCoder<WebKit::DocumentEditingContextRequest>::encode(Encoder& encoder, const WebKit::DocumentEditingContextRequest& request)
+{
+    encoder << request.options;
+    encoder << request.surroundingGranularity;
+    encoder << request.granularityCount;
+    encoder << request.rect;
+    encoder << request.textInputContext;
+}
+
+Optional<WebKit::DocumentEditingContextRequest> ArgumentCoder<WebKit::DocumentEditingContextRequest>::decode(Decoder& decoder)
+{
+    WebKit::DocumentEditingContextRequest request;
+
+    if (!decoder.decode(request.options))
+        return WTF::nullopt;
+
+    if (!decoder.decode(request.surroundingGranularity))
+        return WTF::nullopt;
+
+    if (!decoder.decode(request.granularityCount))
+        return WTF::nullopt;
+
+    if (!decoder.decode(request.rect))
+        return WTF::nullopt;
+
+    Optional<Optional<WebKit::TextInputContext>> optionalTextInputContext;
+    decoder >> optionalTextInputContext;
+    if (!optionalTextInputContext)
+        return WTF::nullopt;
+
+    request.textInputContext = optionalTextInputContext.value();
+
+    return request;
+}
+
+}
+
+#endif

Modified: trunk/Source/WebKit/SourcesCocoa.txt (243353 => 243354)


--- trunk/Source/WebKit/SourcesCocoa.txt	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2019-03-22 02:26:09 UTC (rev 243354)
@@ -125,6 +125,7 @@
 
 Shared/APIWebArchive.mm
 Shared/APIWebArchiveResource.mm
+Shared/DocumentEditingContext.mm
 Shared/FocusedElementInformation.cpp
 Shared/VisibleContentRectUpdateInfo.cpp
 Shared/WebSQLiteDatabaseTracker.cpp

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (243353 => 243354)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -276,6 +276,8 @@
 
 struct AttributedString;
 struct ColorSpaceData;
+struct DocumentEditingContext;
+struct DocumentEditingContextRequest;
 struct EditingRange;
 struct EditorState;
 struct FrameInfoData;
@@ -708,6 +710,8 @@
     void hardwareKeyboardAvailabilityChanged(bool keyboardIsAttached);
     bool isScrollingOrZooming() const { return m_isScrollingOrZooming; }
     void requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&&);
+    void updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&&);
+    void requestDocumentEditingContext(WebKit::DocumentEditingContextRequest, CompletionHandler<void(WebKit::DocumentEditingContext)>&&);
 #if ENABLE(DATA_INTERACTION)
     void didHandleDragStartRequest(bool started);
     void didHandleAdditionalDragItemsRequest(bool added);

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (243353 => 243354)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -29,6 +29,7 @@
 #if PLATFORM(IOS_FAMILY)
 
 #import "APIUIClient.h"
+#import "DocumentEditingContext.h"
 #import "EditableImageController.h"
 #import "EditingRange.h"
 #import "InputViewUpdateDeferrer.h"
@@ -73,6 +74,7 @@
 #import "_WKElementAction.h"
 #import "_WKFocusedElementInfo.h"
 #import "_WKInputDelegate.h"
+#import "_WKTextInputContextInternal.h"
 #import <CoreText/CTFont.h>
 #import <CoreText/CTFontDescriptor.h>
 #import <MobileCoreServices/UTCoreTypes.h>
@@ -6282,6 +6284,68 @@
     return nil;
 }
 
+
+static inline OptionSet<WebKit::DocumentEditingContextRequest::Options> toWebDocumentRequestOptions(UIWKDocumentRequestFlags flags)
+{
+    OptionSet<WebKit::DocumentEditingContextRequest::Options> options;
+
+    if (flags & UIWKDocumentRequestText)
+        options.add(WebKit::DocumentEditingContextRequest::Options::Text);
+    if (flags & UIWKDocumentRequestAttributed)
+        options.add(WebKit::DocumentEditingContextRequest::Options::AttributedText);
+    if (flags & UIWKDocumentRequestRects)
+        options.add(WebKit::DocumentEditingContextRequest::Options::Rects);
+    if (flags & UIWKDocumentRequestSpatial)
+        options.add(WebKit::DocumentEditingContextRequest::Options::Spatial);
+    if (flags & UIWKDocumentRequestAnnotation)
+        options.add(WebKit::DocumentEditingContextRequest::Options::Annotation);
+
+    return options;
+}
+
+static WebKit::DocumentEditingContextRequest toWebRequest(UIWKDocumentRequest *request)
+{
+    WebKit::DocumentEditingContextRequest webRequest = {
+        .options = toWebDocumentRequestOptions(request.flags),
+        .surroundingGranularity = toWKTextGranularity(request.surroundingGranularity),
+        .granularityCount = request.granularityCount,
+        .rect = request.documentRect
+    };
+
+    if (auto textInputContext = dynamic_objc_cast<_WKTextInputContext>(request.inputElementIdentifier))
+        webRequest.textInputContext = [textInputContext _textInputContext];
+
+    return webRequest;
+}
+
+- (void)adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler
+{
+    // UIKit is putting casted signed integers into NSRange. Cast them back to reveal any negative values.
+    _page->updateSelectionWithDelta(static_cast<int64_t>(deltaRange.location), static_cast<int64_t>(deltaRange.length), [capturedCompletionHandler = makeBlockPtr(completionHandler)] {
+        capturedCompletionHandler();
+    });
+}
+
+- (void)requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
+{
+    auto webRequest = toWebRequest(request);
+    OptionSet<WebKit::DocumentEditingContextRequest::Options> options = webRequest.options;
+    _page->requestDocumentEditingContext(webRequest, [capturedCompletionHandler = makeBlockPtr(completionHandler), options] (WebKit::DocumentEditingContext editingContext) {
+        capturedCompletionHandler(editingContext.toPlatformContext(options));
+    });
+}
+
+- (void)selectPositionAtPoint:(CGPoint)point withContextRequest:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
+{
+    // FIXME: Reduce to 1 message.
+    [self selectPositionAtPoint:point completionHandler:^{
+        [self requestDocumentContext:request completionHandler:^(UIWKDocumentContext *context) {
+            completionHandler(context);
+        }];
+    }];
+}
+
+
 #pragma mark - UIDragInteractionDelegate
 
 - (BOOL)_dragInteraction:(UIDragInteraction *)interaction shouldDelayCompetingGestureRecognizer:(UIGestureRecognizer *)competingGestureRecognizer

Modified: trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm (243353 => 243354)


--- trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -31,6 +31,7 @@
 #import "APIUIClient.h"
 #import "Connection.h"
 #import "DataReference.h"
+#import "DocumentEditingContext.h"
 #import "EditingRange.h"
 #import "GlobalFindInPageState.h"
 #import "InteractionInformationAtPosition.h"
@@ -1137,6 +1138,26 @@
     m_process->connection()->sendWithAsyncReply(Messages::WebPage::RequestEvasionRectsAboveSelection(), WTFMove(callback), m_pageID);
 }
 
+void WebPageProxy::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
+{
+    if (!hasRunningProcess()) {
+        completionHandler();
+        return;
+    }
+
+    m_process->connection()->sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithDelta(locationDelta, lengthDelta), WTFMove(completionHandler), m_pageID);
+}
+
+void WebPageProxy::requestDocumentEditingContext(WebKit::DocumentEditingContextRequest request, CompletionHandler<void(WebKit::DocumentEditingContext)>&& completionHandler)
+{
+    if (!hasRunningProcess()) {
+        completionHandler({ });
+        return;
+    }
+
+    m_process->connection()->sendWithAsyncReply(Messages::WebPage::RequestDocumentEditingContext(request), WTFMove(completionHandler), m_pageID);
+}
+
 #if ENABLE(DATA_INTERACTION)
 
 void WebPageProxy::didHandleDragStartRequest(bool started)

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (243353 => 243354)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2019-03-22 02:26:09 UTC (rev 243354)
@@ -2709,6 +2709,8 @@
 		2D7DEE2721269E4E00B9F73C /* UnifiedSource13.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = UnifiedSource13.cpp; path = "DerivedSources/WebKit2/unified-sources/UnifiedSource13.cpp"; sourceTree = BUILT_PRODUCTS_DIR; };
 		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; };
 		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; };
+		2D7FD190223C730F007887F1 /* DocumentEditingContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DocumentEditingContext.h; sourceTree = "<group>"; };
+		2D7FD191223C7310007887F1 /* DocumentEditingContext.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DocumentEditingContext.mm; sourceTree = "<group>"; };
 		2D819B99186275B3001F03D1 /* ViewGestureGeometryCollector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ViewGestureGeometryCollector.cpp; sourceTree = "<group>"; };
 		2D819B9A186275B3001F03D1 /* ViewGestureGeometryCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewGestureGeometryCollector.h; sourceTree = "<group>"; };
 		2D819B9B186275B3001F03D1 /* ViewGestureGeometryCollector.messages.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ViewGestureGeometryCollector.messages.in; sourceTree = "<group>"; };
@@ -5082,6 +5084,8 @@
 				5106D7BF18BDBE73000AB166 /* ContextMenuContextData.cpp */,
 				5106D7C018BDBE73000AB166 /* ContextMenuContextData.h */,
 				99F642D21FABE378009621E9 /* CoordinateSystem.h */,
+				2D7FD190223C730F007887F1 /* DocumentEditingContext.h */,
+				2D7FD191223C7310007887F1 /* DocumentEditingContext.mm */,
 				C517388012DF8F4F00EE3F47 /* DragControllerAction.h */,
 				0FB659221208B4DB0044816C /* DrawingAreaInfo.h */,
 				2D9CD5EE21FA75EE0029ACFA /* EditingRange.cpp */,

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (243353 => 243354)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-03-22 02:26:09 UTC (rev 243354)
@@ -6587,25 +6587,27 @@
 
 void WebPage::focusTextInputContext(const TextInputContext& textInputContext, CompletionHandler<void(bool)>&& completionHandler)
 {
-    completionHandler([&] {
-        if (textInputContext.webPageIdentifier != m_pageID)
-            return false;
+    RefPtr<Element> element = elementForTextInputContext(textInputContext);
 
-        auto* document = Document::allDocumentsMap().get(textInputContext.documentIdentifier);
-        if (!document)
-            return false;
+    if (element)
+        element->focus();
 
-        if (document->page() != m_page.get())
-            return false;
+    completionHandler(element);
+}
 
-        auto* element = document->searchForElementByIdentifier(textInputContext.elementIdentifier);
-        if (!element)
-            return false;
+Element* WebPage::elementForTextInputContext(const TextInputContext& textInputContext)
+{
+    if (textInputContext.webPageIdentifier != m_pageID)
+        return nullptr;
 
-        element->focus();
+    auto* document = Document::allDocumentsMap().get(textInputContext.documentIdentifier);
+    if (!document)
+        return nullptr;
 
-        return true;
-    }());
+    if (document->page() != m_page.get())
+        return nullptr;
+
+    return document->searchForElementByIdentifier(textInputContext.elementIdentifier);
 }
 
 void WebPage::configureLoggingChannel(const String& channelName, WTFLogChannelState state, WTFLogLevel level)

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (243353 => 243354)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -686,6 +686,9 @@
 
     bool forceAlwaysUserScalable() const { return m_forceAlwaysUserScalable; }
     void setForceAlwaysUserScalable(bool);
+
+    void updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&&);
+    void requestDocumentEditingContext(WebKit::DocumentEditingContextRequest, CompletionHandler<void(WebKit::DocumentEditingContext)>&&);
 #endif
 
 #if PLATFORM(IOS_FAMILY) && ENABLE(IOS_TOUCH_EVENTS)
@@ -1166,6 +1169,8 @@
 
     void configureLoggingChannel(const String&, WTFLogChannelState, WTFLogLevel);
 
+    WebCore::Element* elementForTextInputContext(const TextInputContext&);
+
 #if ENABLE(APPLE_PAY)
     WebPaymentCoordinator* paymentCoordinator();
 #endif

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (243353 => 243354)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2019-03-22 02:26:09 UTC (rev 243354)
@@ -110,6 +110,8 @@
     RequestFocusedElementInformation(WebKit::CallbackID callbackID)
     HardwareKeyboardAvailabilityChanged(bool keyboardIsAttached)
     SetIsShowingInputViewForFocusedElement(bool showingInputView)
+    UpdateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta) -> () Async
+    RequestDocumentEditingContext(struct WebKit::DocumentEditingContextRequest request) -> (struct WebKit::DocumentEditingContext response) Async
 #endif
 
     SetControlledByAutomation(bool controlled)

Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (243353 => 243354)


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -30,6 +30,7 @@
 
 #import "AccessibilityIOS.h"
 #import "DataReference.h"
+#import "DocumentEditingContext.h"
 #import "DrawingArea.h"
 #import "EditingRange.h"
 #import "EditorState.h"
@@ -3312,6 +3313,222 @@
 #endif
 }
 
+void WebPage::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
+{
+    Ref<Frame> frame = corePage()->focusController().focusedOrMainFrame();
+    VisibleSelection selection = frame->selection().selection();
+    if (selection.isNone()) {
+        completionHandler();
+        return;
+    }
+
+    auto root = frame->selection().rootEditableElementOrDocumentElement();
+    auto range = selection.toNormalizedRange();
+    if (!root || !range) {
+        completionHandler();
+        return;
+    }
+
+    size_t selectionLocation;
+    size_t selectionLength;
+    TextIterator::getLocationAndLengthFromRange(root, range.get(), selectionLocation, selectionLength);
+
+    CheckedInt64 newSelectionLocation { selectionLocation };
+    CheckedInt64 newSelectionLength { selectionLength };
+    newSelectionLocation += locationDelta;
+    newSelectionLength += lengthDelta;
+
+    if (newSelectionLocation.hasOverflowed() || newSelectionLength.hasOverflowed()) {
+        completionHandler();
+        return;
+    }
+
+    if (auto range = TextIterator::rangeFromLocationAndLength(root, newSelectionLocation.unsafeGet(), newSelectionLength.unsafeGet()))
+        frame->selection().setSelectedRange(range.get(), DOWNSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
+
+    completionHandler();
+}
+
+static VisiblePosition moveByGranularityRespectingWordBoundary(Frame& frame, VisiblePosition& position, TextGranularity granularity, uint64_t granularityCount, SelectionDirection direction)
+{
+    bool backwards = direction == DirectionBackward;
+    auto farthestPositionInDirection = backwards ? startOfEditableContent(position) : endOfEditableContent(position);
+    if (position == farthestPositionInDirection)
+        return { };
+
+    VisiblePosition currentPosition = position;
+    VisiblePosition nextPosition;
+    for (unsigned i = 0; i < granularityCount + 1; ++i) {
+        nextPosition = positionOfNextBoundaryOfGranularity(currentPosition, granularity, direction);
+        // FIXME (196127): We shouldn't need to do this, but have seen previousParagraphPosition go forwards.
+        if ((backwards && nextPosition > currentPosition) || (!backwards && nextPosition < currentPosition))
+            break;
+        if (nextPosition.isNull())
+            break;
+        currentPosition = nextPosition;
+    }
+
+    return backwards ? startOfWord(currentPosition) : endOfWord(currentPosition);
+}
+
+static VisiblePosition visiblePositionForPointInRootViewCoordinates(Frame& frame, FloatPoint pointInRootViewCoordinates)
+{
+    auto pointInDocument = frame.view()->rootViewToContents(roundedIntPoint(pointInRootViewCoordinates));
+    return frame.visiblePositionForPoint(pointInDocument);
+}
+
+void WebPage::requestDocumentEditingContext(DocumentEditingContextRequest request, CompletionHandler<void(DocumentEditingContext)>&& completionHandler)
+{
+    if (!request.options.contains(DocumentEditingContextRequest::Options::Text) && !request.options.contains(DocumentEditingContextRequest::Options::AttributedText)) {
+        completionHandler({ });
+        return;
+    }
+
+    m_page->focusController().focusedOrMainFrame().document()->updateLayoutIgnorePendingStylesheets();
+
+    Ref<Frame> frame = m_page->focusController().focusedOrMainFrame();
+    VisibleSelection selection = frame->selection().selection();
+
+    VisiblePosition rangeOfInterestStart;
+    VisiblePosition rangeOfInterestEnd;
+    VisiblePosition selectionStart = selection.visibleStart();
+    VisiblePosition selectionEnd = selection.visibleEnd();
+
+    bool isSpatialRequest = request.options.contains(DocumentEditingContextRequest::Options::Spatial);
+    bool wantsRects = request.options.contains(DocumentEditingContextRequest::Options::Rects);
+
+    if (auto textInputContext = request.textInputContext) {
+        RefPtr<Element> element = elementForTextInputContext(*textInputContext);
+        if (!element) {
+            completionHandler({ });
+            return;
+        }
+        if (is<HTMLTextFormControlElement>(element)) {
+            auto& textFormControlElement = downcast<HTMLTextFormControlElement>(*element);
+            rangeOfInterestStart = textFormControlElement.visiblePositionForIndex(0);
+            rangeOfInterestEnd = textFormControlElement.visiblePositionForIndex(textFormControlElement.value().length());
+        } else {
+            rangeOfInterestStart = firstPositionInOrBeforeNode(element.get());
+            rangeOfInterestEnd = lastPositionInOrAfterNode(element.get());
+        }
+    } else if (isSpatialRequest) {
+        // FIXME: We might need to be a bit more careful that we get something useful (test the other corners?).
+        rangeOfInterestStart = visiblePositionForPointInRootViewCoordinates(frame.get(), request.rect.minXMinYCorner());
+        rangeOfInterestEnd = visiblePositionForPointInRootViewCoordinates(frame.get(), request.rect.maxXMaxYCorner());
+        if (rangeOfInterestEnd < rangeOfInterestStart)
+            std::exchange(rangeOfInterestStart, rangeOfInterestEnd);
+    } else if (!selection.isNone()) {
+        rangeOfInterestStart = selectionStart;
+        rangeOfInterestEnd = selectionEnd;
+    }
+
+    if (rangeOfInterestStart.isNull() || rangeOfInterestStart.isOrphan() || rangeOfInterestEnd.isNull() || rangeOfInterestEnd.isOrphan()) {
+        completionHandler({ });
+        return;
+    }
+
+    DocumentEditingContext context;
+
+    // The subset of the selection that is inside the range of interest.
+    VisiblePosition startOfRangeOfInterestInSelection;
+    VisiblePosition endOfRangeOfInterestInSelection;
+
+    auto selectionRange = selection.toNormalizedRange();
+    auto rangeOfInterest = makeRange(rangeOfInterestStart, rangeOfInterestEnd);
+    if (selectionRange && rangesOverlap(rangeOfInterest.get(), selectionRange.get())) {
+        startOfRangeOfInterestInSelection = rangeOfInterestStart > selectionStart ? rangeOfInterestStart : selectionStart;
+        endOfRangeOfInterestInSelection = rangeOfInterestEnd < selectionEnd ? rangeOfInterestEnd : selectionEnd;
+    } else {
+        size_t rangeOfInterestLocation;
+        size_t rangeOfInterestLength;
+        RefPtr<Node> rootNode = rangeOfInterest->commonAncestorContainer();
+        if (!rootNode) {
+            completionHandler({ });
+            return;
+        }
+
+        RefPtr<ContainerNode> rootContainerNode = rootNode->isContainerNode() ? downcast<ContainerNode>(rootNode.get()) : rootNode->parentNode();
+        TextIterator::getLocationAndLengthFromRange(rootContainerNode.get(), rangeOfInterest.get(), rangeOfInterestLocation, rangeOfInterestLength);
+
+        CheckedSize midpointLocation { rangeOfInterestLocation };
+        midpointLocation += rangeOfInterestLength / 2;
+        if (midpointLocation.hasOverflowed()) {
+            completionHandler({ });
+            return;
+        }
+
+        auto midpointRange = TextIterator::rangeFromLocationAndLength(rootContainerNode.get(), midpointLocation.unsafeGet(), 0);
+
+        auto midpoint = midpointRange->startPosition();
+        startOfRangeOfInterestInSelection = startOfWord(midpoint);
+        if (startOfRangeOfInterestInSelection < rangeOfInterestStart) {
+            startOfRangeOfInterestInSelection = endOfWord(midpoint);
+            if (startOfRangeOfInterestInSelection > rangeOfInterestEnd)
+                startOfRangeOfInterestInSelection = midpoint;
+        }
+
+        endOfRangeOfInterestInSelection = startOfRangeOfInterestInSelection;
+    }
+
+    VisiblePosition contextBeforeStart;
+    VisiblePosition contextAfterEnd;
+    if (request.granularityCount) {
+        contextBeforeStart = moveByGranularityRespectingWordBoundary(frame.get(), rangeOfInterestStart, request.surroundingGranularity, request.granularityCount, DirectionBackward);
+        contextAfterEnd = moveByGranularityRespectingWordBoundary(frame.get(), rangeOfInterestEnd, request.surroundingGranularity, request.granularityCount, DirectionForward);
+    } else {
+        contextBeforeStart = rangeOfInterestStart;
+        contextAfterEnd = rangeOfInterestEnd;
+    }
+
+    auto makeString = [&](VisiblePosition& start, VisiblePosition& end) -> NSAttributedString * {
+        if (start.isNull() || end.isNull() || start == end)
+            return nil;
+        // FIXME: This should return editing-offset-compatible attributed strings if that option is requested.
+        return adoptNS([[NSAttributedString alloc] initWithString:plainTextReplacingNoBreakSpace(start.deepEquivalent(), end.deepEquivalent())]).autorelease();
+    };
+
+    context.contextBefore = makeString(contextBeforeStart, startOfRangeOfInterestInSelection);
+    context.selectedText = makeString(startOfRangeOfInterestInSelection, endOfRangeOfInterestInSelection);
+    context.contextAfter = makeString(endOfRangeOfInterestInSelection, contextAfterEnd);
+
+    auto compositionRange = frame->editor().compositionRange();
+    if (compositionRange && rangesOverlap(rangeOfInterest.get(), compositionRange.get())) {
+        VisiblePosition compositionStart(compositionRange->startPosition());
+        VisiblePosition compositionEnd(compositionRange->endPosition());
+
+        VisiblePosition relevantCompositionStart = rangeOfInterestStart > compositionStart ? rangeOfInterestStart : compositionStart;
+        VisiblePosition relevantCompositionEnd = rangeOfInterestEnd < compositionEnd ? rangeOfInterestEnd : compositionEnd;
+
+        context.markedText = makeString(relevantCompositionStart, relevantCompositionEnd);
+        context.selectedRangeInMarkedText.location = distanceBetweenPositions(relevantCompositionStart, startOfRangeOfInterestInSelection);
+        context.selectedRangeInMarkedText.length = [context.selectedText.string length];
+    }
+
+    if (wantsRects) {
+        TextIterator contextIterator(contextBeforeStart.deepEquivalent(), contextAfterEnd.deepEquivalent());
+        unsigned currentLocation = 0;
+        while (!contextIterator.atEnd()) {
+            unsigned length = contextIterator.text().length();
+            if (!length) {
+                contextIterator.advance();
+                continue;
+            }
+
+            DocumentEditingContext::TextRectAndRange rect;
+            rect.rect = contextIterator.range()->absoluteBoundingBox();
+            rect.range = { currentLocation, length };
+            context.textRects.append(rect);
+
+            currentLocation += length;
+            contextIterator.advance();
+        }
+    }
+
+    // FIXME: Support Annotation option.
+
+    completionHandler(context);
+}
+
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/ChangeLog (243353 => 243354)


--- trunk/Tools/ChangeLog	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/ChangeLog	2019-03-22 02:26:09 UTC (rev 243354)
@@ -1,3 +1,30 @@
+2019-03-21  Tim Horton  <timothy_hor...@apple.com>
+
+        Adopt UIWKDocumentContext
+        https://bugs.webkit.org/show_bug.cgi?id=196040
+        <rdar://problem/48642440>
+
+        Reviewed by Ryosuke Niwa.
+
+        * 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.
+
 2019-03-21  Simon Fraser  <simon.fra...@apple.com>
 
         [iOS WK2] Turn on async overflow scrolling by default

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (243353 => 243354)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2019-03-22 02:26:09 UTC (rev 243354)
@@ -98,6 +98,7 @@
 		2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D51A0C51C8BF00400765C45 /* DOMHTMLVideoElementWrapper.mm */; };
 		2D70059621EDA0C6003463CB /* TabOutOfWebView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */; };
 		2D70059921EDA4D0003463CB /* OffscreenWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D70059721EDA4D0003463CB /* OffscreenWindow.mm */; };
+		2D7FD19322419087007887F1 /* DocumentEditingContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D7FD19222419087007887F1 /* DocumentEditingContext.mm */; };
 		2D838B1F1EEF3A5C009B980E /* WKContentViewEditingActions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */; };
 		2DADF26321CB8F32003D3E3A /* GetResourceData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DADF26221CB8F32003D3E3A /* GetResourceData.mm */; };
 		2DB0232F1E4E871800707123 /* InteractionDeadlockAfterCrash.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0232E1E4E871800707123 /* InteractionDeadlockAfterCrash.mm */; };
@@ -1453,6 +1454,7 @@
 		2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TabOutOfWebView.mm; sourceTree = "<group>"; };
 		2D70059721EDA4D0003463CB /* OffscreenWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OffscreenWindow.mm; sourceTree = "<group>"; };
 		2D70059821EDA4D0003463CB /* OffscreenWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OffscreenWindow.h; sourceTree = "<group>"; };
+		2D7FD19222419087007887F1 /* DocumentEditingContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DocumentEditingContext.mm; sourceTree = "<group>"; };
 		2D8104CB1BEC13E70020DA46 /* FindInPage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FindInPage.mm; sourceTree = "<group>"; };
 		2D838B1E1EEF3A5B009B980E /* WKContentViewEditingActions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKContentViewEditingActions.mm; sourceTree = "<group>"; };
 		2D9A53AE1B31FA8D0074D5AA /* ShrinkToFit.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ShrinkToFit.mm; sourceTree = "<group>"; };
@@ -2548,6 +2550,7 @@
 				46918EFB2237283500468DFE /* DeviceOrientation.mm */,
 				CEA7F57B20895F5B0078EF6E /* DidResignInputElementStrongPasswordAppearance.mm */,
 				518EE51A20A78CFB00E024F3 /* DoAfterNextPresentationUpdateAfterCrash.mm */,
+				2D7FD19222419087007887F1 /* DocumentEditingContext.mm */,
 				518EE51620A78CDF00E024F3 /* DoubleDefersLoading.mm */,
 				518EE51720A78CDF00E024F3 /* DoubleDefersLoadingPlugin.mm */,
 				A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
@@ -4071,6 +4074,7 @@
 				7CCE7EEA1A411AE600447C4C /* DidNotHandleKeyDown.cpp in Sources */,
 				AD57AC211DA7465B00FF1BDE /* DidRemoveFrameFromHiearchyInPageCache.cpp in Sources */,
 				518EE51B20A78D0000E024F3 /* DoAfterNextPresentationUpdateAfterCrash.mm in Sources */,
+				2D7FD19322419087007887F1 /* DocumentEditingContext.mm in Sources */,
 				7CCE7EEB1A411AE600447C4C /* DocumentStartUserScriptAlertCrash.cpp in Sources */,
 				7CCE7EBB1A411A7E00447C4C /* DOMHTMLTableCellCellAbove.mm in Sources */,
 				2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DocumentEditingContext.mm (0 => 243354)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DocumentEditingContext.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DocumentEditingContext.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestNavigationDelegate.h"
+#import "TestWKWebView.h"
+#import "UIKitSPI.h"
+#import <WebKit/WKPreferencesRefPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/WebKit.h>
+#import <WebKit/_WKTextInputContext.h>
+#import <wtf/RetainPtr.h>
+
+#define EXPECT_NSSTRING_EQ(expected, actual) \
+    EXPECT_TRUE([actual isKindOfClass:[NSString class]]); \
+    EXPECT_WK_STREQ(expected, (NSString *)actual);
+
+#define EXPECT_ATTRIBUTED_STRING_EQ(expected, actual) \
+    EXPECT_TRUE([actual isKindOfClass:[NSAttributedString class]]); \
+    EXPECT_WK_STREQ(expected, [(NSAttributedString *)actual string]);
+
+#define EXPECT_RECT_EQ(xExpected, yExpected, widthExpected, heightExpected, rect) \
+    EXPECT_DOUBLE_EQ(xExpected, rect.origin.x); \
+    EXPECT_DOUBLE_EQ(yExpected, rect.origin.y); \
+    EXPECT_DOUBLE_EQ(widthExpected, rect.size.width); \
+    EXPECT_DOUBLE_EQ(heightExpected, rect.size.height);
+
+@interface WKContentView ()
+- (void)requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler;
+- (void)adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler;
+@end
+
+static UIWKDocumentRequest *makeRequest(UIWKDocumentRequestFlags flags, UITextGranularity granularity, NSInteger granularityCount, CGRect documentRect = CGRectZero, id <NSCopying> inputElementIdentifier = nil)
+{
+    RetainPtr<UIWKDocumentRequest> request = adoptNS([[NSClassFromString(@"UIWKDocumentRequest") alloc] init]);
+    [request setFlags:flags];
+    [request setSurroundingGranularity:granularity];
+    [request setGranularityCount:granularityCount];
+    [request setDocumentRect:documentRect];
+    [request setInputElementIdentifier:inputElementIdentifier];
+    return request.autorelease();
+}
+
+@implementation TestWKWebView (SynchronousDocumentContext)
+
+- (UIWKDocumentContext *)synchronouslyRequestDocumentContext:(UIWKDocumentRequest *)request
+{
+    __block bool finished = false;
+    __block RetainPtr<UIWKDocumentContext> result;
+    [[self wkContentView] requestDocumentContext:request completionHandler:^(UIWKDocumentContext *context) {
+        result = context;
+        finished = true;
+    }];
+    TestWebKitAPI::Util::run(&finished);
+    return result.autorelease();
+}
+
+- (void)synchronouslyAdjustSelectionWithDelta:(NSRange)range
+{
+    __block bool finished = false;
+    [[self wkContentView] adjustSelectionWithDelta:range completionHandler:^() {
+        finished = true;
+    }];
+    TestWebKitAPI::Util::run(&finished);
+}
+
+- (NSArray<_WKTextInputContext *> *)synchronouslyRequestTextInputContextsInRect:(CGRect)rect
+{
+    __block bool finished = false;
+    __block RetainPtr<NSArray<_WKTextInputContext *>> result;
+    [self _requestTextInputContextsInRect:rect completionHandler:^(NSArray<_WKTextInputContext *> *contexts) {
+        result = contexts;
+        finished = true;
+    }];
+    TestWebKitAPI::Util::run(&finished);
+    return result.autorelease();
+}
+
+@end
+
+static NSString *applyStyle(NSString *HTMLString)
+{
+    return [@"<style>body { margin: 0; } iframe { border: none; }</style><meta name='viewport' content='initial-scale=1'>" stringByAppendingString:HTMLString];
+}
+
+static NSString *applyAhemStyle(NSString *HTMLString)
+{
+    return [@"<style>@font-face { font-family: customFont; src: url(Ahem.ttf); } body { margin: 0; font-family: customFont; } iframe { border: none; }</style><meta name='viewport' content='initial-scale=1'>" stringByAppendingString:HTMLString];
+}
+
+TEST(WebKit, DocumentEditingContext)
+{
+    if (!NSClassFromString(@"UIWKDocumentRequest"))
+        return;
+
+    RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+
+    UIWKDocumentContext *context;
+
+    [webView synchronouslyLoadHTMLString:applyStyle(@"<span id='the'>The</span> quick brown fox <span id='jumps'>jumps</span> over the lazy <span id='dog'>dog.</span>")];
+    [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(jumps, 0, jumps, 1)"];
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("fox ", context.contextBefore);
+    EXPECT_NSSTRING_EQ("jumps", context.selectedText);
+    EXPECT_NSSTRING_EQ(" over", context.contextAfter);
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 2)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("brown fox ", context.contextBefore);
+    EXPECT_NSSTRING_EQ("jumps", context.selectedText);
+    EXPECT_NSSTRING_EQ(" over the", context.contextAfter);
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityParagraph, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("The quick brown fox ", context.contextBefore);
+    EXPECT_NSSTRING_EQ("jumps", context.selectedText);
+    EXPECT_NSSTRING_EQ(" over the lazy dog.", context.contextAfter);
+
+    // Attributed strings.
+    // FIXME: Currently we don't test the attributes... because we don't set any.
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestAttributed, UITextGranularityWord, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_ATTRIBUTED_STRING_EQ("fox ", context.contextBefore);
+    EXPECT_ATTRIBUTED_STRING_EQ("jumps", context.selectedText);
+    EXPECT_ATTRIBUTED_STRING_EQ(" over", context.contextAfter);
+
+    // Extremities of the document.
+    [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(the, 0, the, 1)"];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NULL(context.contextBefore);
+    EXPECT_NSSTRING_EQ("The", context.selectedText);
+    EXPECT_NSSTRING_EQ(" quick", context.contextAfter);
+
+    [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(dog, 0, dog, 1)"];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularitySentence, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("The quick brown fox jumps over the lazy ", context.contextBefore);
+    EXPECT_NSSTRING_EQ("dog.", context.selectedText);
+    EXPECT_NULL(context.contextAfter);
+
+    // Spatial requests.
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText | UIWKDocumentRequestSpatial, UITextGranularityWord, 1, CGRectMake(0, 0, 10, 10))];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NULL(context.contextBefore);
+    EXPECT_NULL(context.selectedText);
+    EXPECT_NSSTRING_EQ("The quick", context.contextAfter);
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText | UIWKDocumentRequestSpatial, UITextGranularityWord, 1, CGRectMake(0, 0, 800, 600))];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("The quick brown fox jumps over the lazy ", context.contextBefore);
+    EXPECT_NSSTRING_EQ("dog.", context.selectedText);
+    EXPECT_NULL(context.contextAfter);
+
+    [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText | UIWKDocumentRequestSpatial, UITextGranularityWord, 1, CGRectMake(0, 0, 800, 600))];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("The quick brown fox ", context.contextBefore);
+    EXPECT_NULL(context.selectedText);
+    EXPECT_NSSTRING_EQ("jumps over the lazy dog.", context.contextAfter);
+
+    // Selection adjustment.
+    [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(jumps, 0, jumps, 1)"];
+    [webView synchronouslyAdjustSelectionWithDelta:NSMakeRange(6, -1)];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("over", context.selectedText);
+
+    [webView synchronouslyAdjustSelectionWithDelta:NSMakeRange(-6, 1)];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 1)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NSSTRING_EQ("jumps", context.selectedText);
+
+    // Text rects.
+    [webView synchronouslyLoadHTMLString:applyAhemStyle(@"<span id='four'>MMMM</span> MMM MM M")];
+    [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(four, 0, four, 1)"];
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText | UIWKDocumentRequestRects, UITextGranularityWord, 0)];
+    EXPECT_NOT_NULL(context);
+    EXPECT_NULL(context.contextBefore);
+    EXPECT_NSSTRING_EQ("MMMM", context.selectedText);
+    EXPECT_NULL(context.contextAfter);
+
+    NSArray<NSValue *> *rects = [context characterRectsForCharacterRange:NSMakeRange(0, 1)];
+    EXPECT_EQ(1UL, rects.count);
+    EXPECT_RECT_EQ(0, 0, 92, 24, rects.firstObject.CGRectValue);
+    rects = [context characterRectsForCharacterRange:NSMakeRange(5, 1)];
+    EXPECT_EQ(0UL, rects.count);
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText | UIWKDocumentRequestRects, UITextGranularityWord, 1)];
+    EXPECT_NSSTRING_EQ(" MMM", context.contextAfter);
+    rects = [context characterRectsForCharacterRange:NSMakeRange(0, 1)];
+    EXPECT_EQ(1UL, rects.count);
+    EXPECT_RECT_EQ(0, 0, 92, 24, rects.firstObject.CGRectValue);
+    rects = [context characterRectsForCharacterRange:NSMakeRange(6, 1)];
+    EXPECT_EQ(1UL, rects.count);
+    EXPECT_RECT_EQ(92, 0, 92, 24, rects.firstObject.CGRectValue);
+
+    // Text Input Context
+    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' style='width: 50px; height: 50px;' value='hello, world'>")];
+    NSArray<_WKTextInputContext *> *textInputContexts = [webView synchronouslyRequestTextInputContextsInRect:[webView frame]];
+    EXPECT_EQ(1UL, textInputContexts.count);
+    EXPECT_RECT_EQ(0, 0, 50, 50, textInputContexts[0].boundingRect);
+
+    context = [webView synchronouslyRequestDocumentContext:makeRequest(UIWKDocumentRequestText, UITextGranularityWord, 0, CGRectZero, textInputContexts[0])];
+    EXPECT_NSSTRING_EQ("hello,", context.contextBefore);
+    EXPECT_NULL(context.selectedText);
+    EXPECT_NSSTRING_EQ(" world", context.contextAfter);
+}
+
+#endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm (243353 => 243354)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -34,20 +34,6 @@
 #import <WebKit/WebKit.h>
 #import <wtf/RetainPtr.h>
 
-static UIView *recursiveFindWKContentView(UIView *view)
-{
-    if ([view isKindOfClass:NSClassFromString(@"WKContentView")])
-        return view;
-
-    for (UIView *subview in view.subviews) {
-        UIView *contentView = recursiveFindWKContentView(subview);
-        if (contentView)
-            return contentView;
-    }
-
-    return nil;
-}
-
 TEST(WebKit, WKContentViewEditingActions)
 {
     [UIPasteboard generalPasteboard].items = @[];
@@ -58,7 +44,7 @@
 
     [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
 
-    UIView *contentView = recursiveFindWKContentView(webView.get());
+    UIView *contentView = [webView wkContentView];
 
     __block bool done = false;
 

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (243353 => 243354)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -79,6 +79,9 @@
 @end
 
 #if PLATFORM(IOS_FAMILY)
+@interface WKContentView : UIView
+@end
+
 @interface TestWKWebView (IOSOnly)
 @property (nonatomic, readonly) UIView <UITextInputPrivate, UITextInputMultiDocument> *textInputContentView;
 @property (nonatomic, readonly) RetainPtr<NSArray> selectionRectsAfterPresentationUpdate;
@@ -86,6 +89,7 @@
 @property (nonatomic, readonly) NSArray<NSValue *> *selectionViewRectsInContentCoordinates;
 - (_WKActivatedElementInfo *)activatedElementAtPosition:(CGPoint)position;
 - (void)evaluateJavaScriptAndWaitForInputSessionToChange:(NSString *)script;
+- (WKContentView *)wkContentView;
 @end
 #endif
 

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (243353 => 243354)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2019-03-22 02:26:09 UTC (rev 243354)
@@ -509,6 +509,25 @@
     return info.autorelease();
 }
 
+static WKContentView *recursiveFindWKContentView(UIView *view)
+{
+    if ([view isKindOfClass:NSClassFromString(@"WKContentView")])
+        return (WKContentView *)view;
+
+    for (UIView *subview in view.subviews) {
+        WKContentView *contentView = recursiveFindWKContentView(subview);
+        if (contentView)
+            return contentView;
+    }
+
+    return nil;
+}
+
+- (WKContentView *)wkContentView
+{
+    return recursiveFindWKContentView(self);
+}
+
 @end
 
 #endif

Modified: trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h (243353 => 243354)


--- trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h	2019-03-22 01:58:13 UTC (rev 243353)
+++ trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h	2019-03-22 02:26:09 UTC (rev 243354)
@@ -141,4 +141,36 @@
 + (BOOL)isInHardwareKeyboardMode;
 @end
 
+@interface UIWKDocumentContext : NSObject
+
+@property (nonatomic, copy) NSObject *contextBefore;
+@property (nonatomic, copy) NSObject *selectedText;
+@property (nonatomic, copy) NSObject *contextAfter;
+@property (nonatomic, copy) NSObject *markedText;
+@property (nonatomic, assign) NSRange selectedRangeInMarkedText;
+@property (nonatomic, copy) NSAttributedString *annotatedText;
+
+- (NSArray<NSValue *> *)characterRectsForCharacterRange:(NSRange)range;
+
+@end
+
+typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
+    UIWKDocumentRequestNone = 0,
+    UIWKDocumentRequestText = 1 << 0,
+    UIWKDocumentRequestAttributed = 1 << 1,
+    UIWKDocumentRequestRects = 1 << 2,
+    UIWKDocumentRequestSpatial = 1 << 3,
+    UIWKDocumentRequestAnnotation = 1 << 4,
+};
+
+@interface UIWKDocumentRequest : NSObject
+
+@property (nonatomic, assign) UIWKDocumentRequestFlags flags;
+@property (nonatomic, assign) UITextGranularity surroundingGranularity;
+@property (nonatomic, assign) NSInteger granularityCount;
+@property (nonatomic, assign) CGRect documentRect;
+@property (nonatomic, retain) id <NSCopying> inputElementIdentifier;
+
+@end
+
 #endif // PLATFORM(IOS_FAMILY)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to