Title: [195078] trunk
Revision
195078
Author
[email protected]
Date
2016-01-14 14:44:26 -0800 (Thu, 14 Jan 2016)

Log Message

WK2: Request completion candidates when needed
https://bugs.webkit.org/show_bug.cgi?id=153040
-and corresponding-
rdar://problem/24155631

Reviewed by Enrica Casucci and Tim Horton.

Source/WebCore:

Helper functions for stringForCandidateRequest() and 
handleAcceptedCandidate()
* editing/Editor.cpp:
(WebCore::candidateRangeForSelection):
(WebCore::candidateWouldReplaceText):

Request candidates for the word that is currently being typed so long as the 
candidate would replace that word. Otherwise, use String().
(WebCore::Editor::stringForCandidateRequest):

When a candidate has been accepted, insert the text.
(WebCore::Editor::handleAcceptedCandidate):
* editing/Editor.h:

Source/WebKit2:

Mac needs to support postLayoutData in order to have some layout-related 
editing information to request candidates. This patch re-shuffles some items 
in the struct so that they can be shared by Mac and iOS, and it adds 3 new 
items for Mac only.
* Shared/EditorState.cpp:
(WebKit::EditorState::encode):
(WebKit::EditorState::decode):
(WebKit::EditorState::PostLayoutData::encode):
(WebKit::EditorState::PostLayoutData::decode):
* Shared/EditorState.h:

Request and handle candidates here in WebViewImpl, and cache the 
m_lastStringForCandidateRequest so that we can ensure the results we receive 
were received in a timely enough manner that they are still for the same 
String.
* UIProcess/Cocoa/WebViewImpl.h:
* UIProcess/Cocoa/WebViewImpl.mm:
(WebKit::WebViewImpl::selectionDidChange):

When selection changes, request new candidates.
(WebKit::WebViewImpl::requestCandidatesForSelectionIfNeeded):

Once candidates have been received, we ask the sharedSpellChecker to show 
them.
(WebKit::WebViewImpl::handleRequestedCandidates):

If a candidate is accepted, we ask the WebProcess to accept it, so we start 
by converting the NSTextCheckingResult to a WebCore::TextCheckingResult.
(WebKit::textCheckingResultFromNSTextCheckingResult):
(WebKit::WebViewImpl::handleAcceptedCandidate):

Ask the WebProcess to handle accepting the candidate.
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::handleAcceptedCandidate):
* UIProcess/WebPageProxy.h:
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage:: handleAcceptedCandidate):
* WebProcess/WebPage/WebPage.messages.in:

Now that Mac has some postLayoutData in the EditorState, fill that in in 
platformEditorState().
* WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::platformEditorState):

Ask WebCore::Editor to handle the accepted candidate.
(WebKit::WebPage::handleAcceptedCandidate):

LayoutTests:

Getting updated EditorState in platformEditorState causes some extra layout 
to happen, so now the layout test results for WK2 reflect the results that we 
already see on iOS for this test and they reflect the render tree as it is 
when you load the page in browser.
* platform/mac/fast/dom/focus-contenteditable-expected.txt:

WebKit 1 is not affected by these new results, so this adds WK-1 only results 
that match the old Mac results.
* platform/mac-wk1/fast/dom: Added.
* platform/mac-wk1/fast/dom/focus-contenteditable-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (195077 => 195078)


--- trunk/LayoutTests/ChangeLog	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/LayoutTests/ChangeLog	2016-01-14 22:44:26 UTC (rev 195078)
@@ -1,3 +1,23 @@
+2016-01-14  Beth Dakin  <[email protected]>
+
+        WK2: Request completion candidates when needed
+        https://bugs.webkit.org/show_bug.cgi?id=153040
+        -and corresponding-
+        rdar://problem/24155631
+
+        Reviewed by Enrica Casucci and Tim Horton.
+
+        Getting updated EditorState in platformEditorState causes some extra layout 
+        to happen, so now the layout test results for WK2 reflect the results that we 
+        already see on iOS for this test and they reflect the render tree as it is 
+        when you load the page in browser.
+        * platform/mac/fast/dom/focus-contenteditable-expected.txt:
+
+        WebKit 1 is not affected by these new results, so this adds WK-1 only results 
+        that match the old Mac results.
+        * platform/mac-wk1/fast/dom: Added.
+        * platform/mac-wk1/fast/dom/focus-contenteditable-expected.txt: Added.
+
 2016-01-14  Daniel Bates  <[email protected]>
 
         Disallow use of Geolocation service from unique origins

Modified: trunk/LayoutTests/platform/mac/fast/dom/focus-contenteditable-expected.txt (195077 => 195078)


--- trunk/LayoutTests/platform/mac/fast/dom/focus-contenteditable-expected.txt	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/LayoutTests/platform/mac/fast/dom/focus-contenteditable-expected.txt	2016-01-14 22:44:26 UTC (rev 195078)
@@ -6,7 +6,7 @@
       RenderBlock (anonymous) at (0,0) size 769x36
         RenderText {#text} at (0,0) size 509x18
           text run at (0,0) width 509: "This test will try to call focus() on a contenteditable div, and then a normal div. "
-        RenderBR {BR} at (508,14) size 1x0
+        RenderBR {BR} at (0,0) size 0x0
         RenderText {#text} at (0,18) size 379x18
           text run at (0,18) width 379: "The window should scroll to reveal the contenteditable div."
       RenderBlock {DIV} at (0,36) size 500x800

Added: trunk/LayoutTests/platform/mac-wk1/fast/dom/focus-contenteditable-expected.txt (0 => 195078)


--- trunk/LayoutTests/platform/mac-wk1/fast/dom/focus-contenteditable-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/mac-wk1/fast/dom/focus-contenteditable-expected.txt	2016-01-14 22:44:26 UTC (rev 195078)
@@ -0,0 +1,21 @@
+layer at (0,0) size 785x894
+  RenderView at (0,0) size 785x600
+layer at (0,0) size 785x894
+  RenderBlock {HTML} at (0,0) size 785x894
+    RenderBody {BODY} at (8,8) size 769x878
+      RenderBlock (anonymous) at (0,0) size 769x36
+        RenderText {#text} at (0,0) size 509x18
+          text run at (0,0) width 509: "This test will try to call focus() on a contenteditable div, and then a normal div. "
+        RenderBR {BR} at (508,14) size 1x0
+        RenderText {#text} at (0,18) size 379x18
+          text run at (0,18) width 379: "The window should scroll to reveal the contenteditable div."
+      RenderBlock {DIV} at (0,36) size 500x800
+      RenderBlock {DIV} at (0,836) size 769x24 [border: (3px solid #000000)]
+        RenderText {#text} at (3,3) size 125x18
+          text run at (3,3) width 125: "contentEditable div"
+      RenderBlock {DIV} at (0,860) size 769x18
+        RenderText {#text} at (0,0) size 78x18
+          text run at (0,0) width 78: "Test Passed."
+      RenderBlock {DIV} at (0,878) size 769x0
+caret: position 0 of child 0 {#text} of child 5 {DIV} of body
+scrolled to 0,276

Modified: trunk/Source/WebCore/ChangeLog (195077 => 195078)


--- trunk/Source/WebCore/ChangeLog	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebCore/ChangeLog	2016-01-14 22:44:26 UTC (rev 195078)
@@ -1,3 +1,26 @@
+2016-01-14  Beth Dakin  <[email protected]>
+
+        WK2: Request completion candidates when needed
+        https://bugs.webkit.org/show_bug.cgi?id=153040
+        -and corresponding-
+        rdar://problem/24155631
+
+        Reviewed by Enrica Casucci and Tim Horton.
+
+        Helper functions for stringForCandidateRequest() and 
+        handleAcceptedCandidate()
+        * editing/Editor.cpp:
+        (WebCore::candidateRangeForSelection):
+        (WebCore::candidateWouldReplaceText):
+
+        Request candidates for the word that is currently being typed so long as the 
+        candidate would replace that word. Otherwise, use String().
+        (WebCore::Editor::stringForCandidateRequest):
+
+        When a candidate has been accepted, insert the text.
+        (WebCore::Editor::handleAcceptedCandidate):
+        * editing/Editor.h:
+
 2016-01-14  Daniel Bates  <[email protected]>
 
         Disallow use of Geolocation service from unique origins

Modified: trunk/Source/WebCore/editing/Editor.cpp (195077 => 195078)


--- trunk/Source/WebCore/editing/Editor.cpp	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebCore/editing/Editor.cpp	2016-01-14 22:44:26 UTC (rev 195078)
@@ -3533,6 +3533,43 @@
     return checkingTypes;
 }
 
+static RefPtr<Range> candidateRangeForSelection(Frame& frame)
+{
+    const VisibleSelection& selection = frame.selection().selection();
+    return selection.isCaret() ? wordRangeFromPosition(selection.start()) : frame.selection().toNormalizedRange();
+}
+
+static bool candidateWouldReplaceText(const VisibleSelection& selection)
+{
+    // If the character behind the caret in the current selection is anything but a space or a newline then we should
+    // replace the whole current word with the candidate.
+    UChar32 characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection = 0;
+    charactersAroundPosition(selection.visibleStart(), characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection);
+    return !(characterBeforeSelection == '\0' || characterBeforeSelection == '\n' || characterBeforeSelection == ' ');
+}
+
+String Editor::stringForCandidateRequest() const
+{
+    const VisibleSelection& selection = m_frame.selection().selection();
+    RefPtr<Range> rangeForCurrentlyTypedString = candidateRangeForSelection(m_frame);
+    if (rangeForCurrentlyTypedString && candidateWouldReplaceText(selection))
+        return plainText(rangeForCurrentlyTypedString.get());
+
+    return String();
+}
+
+void Editor::handleAcceptedCandidate(TextCheckingResult acceptedCandidate)
+{
+    const VisibleSelection& selection = m_frame.selection().selection();
+    RefPtr<Range> candidateRange = candidateRangeForSelection(m_frame);
+
+    if (candidateWouldReplaceText(selection))
+        m_frame.selection().setSelectedRange(candidateRange.get(), UPSTREAM, true);
+
+    insertText(acceptedCandidate.replacement, 0);
+    insertText(String(" "), 0);
+}
+
 bool Editor::unifiedTextCheckerEnabled() const
 {
     return WebCore::unifiedTextCheckerEnabled(&m_frame);

Modified: trunk/Source/WebCore/editing/Editor.h (195077 => 195078)


--- trunk/Source/WebCore/editing/Editor.h	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebCore/editing/Editor.h	2016-01-14 22:44:26 UTC (rev 195078)
@@ -455,6 +455,9 @@
     const Vector<RefPtr<Range>>& detectedTelephoneNumberRanges() const { return m_detectedTelephoneNumberRanges; }
 #endif
 
+    WEBCORE_EXPORT String stringForCandidateRequest() const;
+    WEBCORE_EXPORT void handleAcceptedCandidate(TextCheckingResult);
+
 private:
     class WebContentReader;
 

Modified: trunk/Source/WebKit2/ChangeLog (195077 => 195078)


--- trunk/Source/WebKit2/ChangeLog	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/ChangeLog	2016-01-14 22:44:26 UTC (rev 195078)
@@ -1,3 +1,59 @@
+2016-01-14  Beth Dakin  <[email protected]>
+
+        WK2: Request completion candidates when needed
+        https://bugs.webkit.org/show_bug.cgi?id=153040
+        -and corresponding-
+        rdar://problem/24155631
+
+        Reviewed by Enrica Casucci and Tim Horton.
+
+        Mac needs to support postLayoutData in order to have some layout-related 
+        editing information to request candidates. This patch re-shuffles some items 
+        in the struct so that they can be shared by Mac and iOS, and it adds 3 new 
+        items for Mac only.
+        * Shared/EditorState.cpp:
+        (WebKit::EditorState::encode):
+        (WebKit::EditorState::decode):
+        (WebKit::EditorState::PostLayoutData::encode):
+        (WebKit::EditorState::PostLayoutData::decode):
+        * Shared/EditorState.h:
+
+        Request and handle candidates here in WebViewImpl, and cache the 
+        m_lastStringForCandidateRequest so that we can ensure the results we receive 
+        were received in a timely enough manner that they are still for the same 
+        String.
+        * UIProcess/Cocoa/WebViewImpl.h:
+        * UIProcess/Cocoa/WebViewImpl.mm:
+        (WebKit::WebViewImpl::selectionDidChange):
+
+        When selection changes, request new candidates.
+        (WebKit::WebViewImpl::requestCandidatesForSelectionIfNeeded):
+
+        Once candidates have been received, we ask the sharedSpellChecker to show 
+        them.
+        (WebKit::WebViewImpl::handleRequestedCandidates):
+
+        If a candidate is accepted, we ask the WebProcess to accept it, so we start 
+        by converting the NSTextCheckingResult to a WebCore::TextCheckingResult.
+        (WebKit::textCheckingResultFromNSTextCheckingResult):
+        (WebKit::WebViewImpl::handleAcceptedCandidate):
+
+        Ask the WebProcess to handle accepting the candidate.
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::handleAcceptedCandidate):
+        * UIProcess/WebPageProxy.h:
+        * WebProcess/WebPage/WebPage.h:
+        (WebKit::WebPage:: handleAcceptedCandidate):
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        Now that Mac has some postLayoutData in the EditorState, fill that in in 
+        platformEditorState().
+        * WebProcess/WebPage/mac/WebPageMac.mm:
+        (WebKit::WebPage::platformEditorState):
+
+        Ask WebCore::Editor to handle the accepted candidate.
+        (WebKit::WebPage::handleAcceptedCandidate):
+
 2016-01-14  Commit Queue  <[email protected]>
 
         Unreviewed, rolling out r195002.

Modified: trunk/Source/WebKit2/Shared/EditorState.cpp (195077 => 195078)


--- trunk/Source/WebKit2/Shared/EditorState.cpp	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/Shared/EditorState.cpp	2016-01-14 22:44:26 UTC (rev 195078)
@@ -43,7 +43,7 @@
     encoder << hasComposition;
     encoder << isMissingPostLayoutData;
 
-#if PLATFORM(IOS) || PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
     if (!isMissingPostLayoutData)
         m_postLayoutData.encode(encoder);
 #endif
@@ -84,7 +84,7 @@
     if (!decoder.decode(result.isMissingPostLayoutData))
         return false;
 
-#if PLATFORM(IOS) || PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
     if (!result.isMissingPostLayoutData) {
         if (!PostLayoutData::decode(decoder, result.postLayoutData()))
             return false;
@@ -103,42 +103,55 @@
     return true;
 }
 
-#if PLATFORM(IOS) || PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
 void EditorState::PostLayoutData::encode(IPC::ArgumentEncoder& encoder) const
 {
+#if PLATFORM(IOS) || PLATFORM(GTK)
     encoder << typingAttributes;
     encoder << caretRectAtStart;
+#endif
+#if PLATFORM(IOS) || PLATFORM(MAC)
+    encoder << selectionClipRect;
+    encoder << selectedTextLength;
+#endif
 #if PLATFORM(IOS)
     encoder << caretRectAtEnd;
-    encoder << selectionClipRect;
     encoder << selectionRects;
     encoder << wordAtSelection;
-    encoder << selectedTextLength;
     encoder << characterAfterSelection;
     encoder << characterBeforeSelection;
     encoder << twoCharacterBeforeSelection;
     encoder << isReplaceAllowed;
     encoder << hasContent;
 #endif
+#if PLATFORM(MAC)
+    encoder << candidateRequestStartPosition;
+    encoder << paragraphContextForCandidateRequest;
+    encoder << stringForCandidateRequest;
+#endif
 }
 
 bool EditorState::PostLayoutData::decode(IPC::ArgumentDecoder& decoder, PostLayoutData& result)
 {
+#if PLATFORM(IOS) || PLATFORM(GTK)
     if (!decoder.decode(result.typingAttributes))
         return false;
     if (!decoder.decode(result.caretRectAtStart))
         return false;
+#endif
+#if PLATFORM(IOS) || PLATFORM(MAC)
+    if (!decoder.decode(result.selectionClipRect))
+        return false;
+    if (!decoder.decode(result.selectedTextLength))
+        return false;
+#endif
 #if PLATFORM(IOS)
     if (!decoder.decode(result.caretRectAtEnd))
         return false;
-    if (!decoder.decode(result.selectionClipRect))
-        return false;
     if (!decoder.decode(result.selectionRects))
         return false;
     if (!decoder.decode(result.wordAtSelection))
         return false;
-    if (!decoder.decode(result.selectedTextLength))
-        return false;
     if (!decoder.decode(result.characterAfterSelection))
         return false;
     if (!decoder.decode(result.characterBeforeSelection))
@@ -150,9 +163,19 @@
     if (!decoder.decode(result.hasContent))
         return false;
 #endif
+#if PLATFORM(MAC)
+    if (!decoder.decode(result.candidateRequestStartPosition))
+        return false;
 
+    if (!decoder.decode(result.paragraphContextForCandidateRequest))
+        return false;
+
+    if (!decoder.decode(result.stringForCandidateRequest))
+        return false;
+#endif
+
     return true;
 }
-#endif
+#endif // PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
 
 }

Modified: trunk/Source/WebKit2/Shared/EditorState.h (195077 => 195078)


--- trunk/Source/WebKit2/Shared/EditorState.h	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/Shared/EditorState.h	2016-01-14 22:44:26 UTC (rev 195078)
@@ -62,22 +62,31 @@
     String markedText;
 #endif
 
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
+    struct PostLayoutData {
 #if PLATFORM(IOS) || PLATFORM(GTK)
-    struct PostLayoutData {
         uint32_t typingAttributes { AttributeNone };
         WebCore::IntRect caretRectAtStart;
+#endif
+#if PLATFORM(IOS) || PLATFORM(MAC)
+        WebCore::IntRect selectionClipRect;
+        uint64_t selectedTextLength { 0 };
+#endif
 #if PLATFORM(IOS)
         WebCore::IntRect caretRectAtEnd;
-        WebCore::IntRect selectionClipRect;
         Vector<WebCore::SelectionRect> selectionRects;
         String wordAtSelection;
-        uint64_t selectedTextLength { 0 };
         UChar32 characterAfterSelection { 0 };
         UChar32 characterBeforeSelection { 0 };
         UChar32 twoCharacterBeforeSelection { 0 };
         bool isReplaceAllowed { false };
         bool hasContent { false };
 #endif
+#if PLATFORM(MAC)
+        uint64_t candidateRequestStartPosition { 0 };
+        String paragraphContextForCandidateRequest;
+        String stringForCandidateRequest;
+#endif
 
         void encode(IPC::ArgumentEncoder&) const;
         static bool decode(IPC::ArgumentDecoder&, PostLayoutData&);
@@ -85,18 +94,18 @@
 
     const PostLayoutData& postLayoutData() const;
     PostLayoutData& postLayoutData();
-#endif // PLATFORM(IOS) || PLATFORM(GTK)
+#endif // PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
 
     void encode(IPC::ArgumentEncoder&) const;
     static bool decode(IPC::ArgumentDecoder&, EditorState&);
 
-#if PLATFORM(IOS) || PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
 private:
     PostLayoutData m_postLayoutData;
 #endif
 };
 
-#if PLATFORM(IOS) || PLATFORM(GTK)
+#if PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(MAC)
 inline auto EditorState::postLayoutData() -> PostLayoutData&
 {
     ASSERT_WITH_MESSAGE(!isMissingPostLayoutData, "Attempt to access post layout data before receiving it");

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h (195077 => 195078)


--- trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.h	2016-01-14 22:44:26 UTC (rev 195078)
@@ -294,6 +294,10 @@
     void lowercaseWord();
     void capitalizeWord();
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+    void requestCandidatesForSelectionIfNeeded();
+#endif
+
     void preferencesDidChange();
 
     void setTextIndicator(WebCore::TextIndicator&, WebCore::TextIndicatorWindowLifetime = WebCore::TextIndicatorWindowLifetime::Permanent);
@@ -498,6 +502,11 @@
 
     Vector<NSTouch *> touchesOrderedByAge();
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+    void handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates);
+    void handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate);
+#endif
+
     NSView <WebViewImplDelegate> *m_view;
     std::unique_ptr<PageClient> m_pageClient;
     Ref<WebPageProxy> m_page;
@@ -610,6 +619,10 @@
 
     Vector<RetainPtr<id <NSObject, NSCopying>>> m_activeTouchIdentities;
     RetainPtr<NSArray> m_lastTouches;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+    String m_lastStringForCandidateRequest;
+#endif
 };
     
 } // namespace WebKit

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm (195077 => 195078)


--- trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebViewImpl.mm	2016-01-14 22:44:26 UTC (rev 195078)
@@ -75,6 +75,7 @@
 #import <WebCore/LookupSPI.h>
 #import <WebCore/NSApplicationSPI.h>
 #import <WebCore/NSImmediateActionGestureRecognizerSPI.h>
+#import <WebCore/NSSpellCheckerSPI.h>
 #import <WebCore/NSTextFinderSPI.h>
 #import <WebCore/NSWindowSPI.h>
 #import <WebCore/PlatformEventFactoryMac.h>
@@ -1723,6 +1724,10 @@
 void WebViewImpl::selectionDidChange()
 {
     updateFontPanelIfNeeded();
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+    if (!m_page->editorState().isMissingPostLayoutData)
+        requestCandidatesForSelectionIfNeeded();
+#endif
 }
 
 void WebViewImpl::startObservingFontPanel()
@@ -2088,6 +2093,103 @@
     m_page->capitalizeWord();
 }
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+void WebViewImpl::requestCandidatesForSelectionIfNeeded()
+{
+    const EditorState& editorState = m_page->editorState();
+    if (!editorState.isContentEditable)
+        return;
+
+    if (editorState.isMissingPostLayoutData)
+        return;
+
+    auto& postLayoutData = editorState.postLayoutData();
+    m_lastStringForCandidateRequest = postLayoutData.stringForCandidateRequest;
+
+    NSRange rangeForCandidates = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
+    NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
+    auto weakThis = createWeakPtr();
+    [[NSSpellChecker sharedSpellChecker] requestCandidatesForSelectedRange:rangeForCandidates inString:postLayoutData.paragraphContextForCandidateRequest types:checkingTypes options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() completionHandler:[weakThis](NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates) {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (!weakThis)
+                return;
+            weakThis->handleRequestedCandidates(sequenceNumber, candidates);
+        });
+    }];
+}
+
+void WebViewImpl::handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates)
+{
+    const EditorState& editorState = m_page->editorState();
+    if (!editorState.isContentEditable)
+        return;
+
+    // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
+    // and that we just bail if it is missing.
+    if (editorState.isMissingPostLayoutData)
+        return;
+
+    auto& postLayoutData = editorState.postLayoutData();
+    if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
+        return;
+
+    auto weakThis = createWeakPtr();
+    [[NSSpellChecker sharedSpellChecker] showCandidates:candidates forString:postLayoutData.stringForCandidateRequest inRect:postLayoutData.selectionClipRect view:m_view completionHandler:[weakThis](NSTextCheckingResult *acceptedCandidate) {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (!weakThis)
+                return;
+            weakThis->handleAcceptedCandidate(acceptedCandidate);
+        });
+    }];
+}
+
+static WebCore::TextCheckingResult textCheckingResultFromNSTextCheckingResult(NSTextCheckingResult *nsResult)
+{
+    WebCore::TextCheckingResult result;
+
+    // FIXME: Right now we only request candidates for spelling, replacement, and correction, but we plan to
+    // support more types, and we will have to update this at that time.
+    switch ([nsResult resultType]) {
+    case NSTextCheckingTypeSpelling:
+        result.type = WebCore::TextCheckingTypeSpelling;
+        break;
+    case NSTextCheckingTypeReplacement:
+        result.type = WebCore::TextCheckingTypeReplacement;
+        break;
+    case NSTextCheckingTypeCorrection:
+        result.type = WebCore::TextCheckingTypeCorrection;
+        break;
+    default:
+        result.type = WebCore::TextCheckingTypeNone;
+    }
+
+    NSRange resultRange = [nsResult range];
+    result.location = resultRange.location;
+    result.length = resultRange.length;
+    result.replacement = [nsResult replacementString];
+
+    return result;
+}
+
+void WebViewImpl::handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate)
+{
+    const EditorState& editorState = m_page->editorState();
+    if (!editorState.isContentEditable)
+        return;
+
+    // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
+    // and that we just bail if it is missing.
+    if (editorState.isMissingPostLayoutData)
+        return;
+
+    auto& postLayoutData = editorState.postLayoutData();
+    if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
+        return;
+
+    m_page->handleAcceptedCandidate(textCheckingResultFromNSTextCheckingResult(acceptedCandidate));
+}
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+
 void WebViewImpl::preferencesDidChange()
 {
     BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();

Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (195077 => 195078)


--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2016-01-14 22:44:26 UTC (rev 195078)
@@ -6010,6 +6010,11 @@
     uint64_t callbackID = m_callbacks.put(voidCallback.release());
     m_nextViewStateChangeCallbacks.append(callbackID);
 }
+
+void WebPageProxy::handleAcceptedCandidate(WebCore::TextCheckingResult acceptedCandidate)
+{
+    m_process->send(Messages::WebPage::HandleAcceptedCandidate(acceptedCandidate), m_pageID);
+}
 #endif
 
 void WebPageProxy::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize)

Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.h (195077 => 195078)


--- trunk/Source/WebKit2/UIProcess/WebPageProxy.h	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.h	2016-01-14 22:44:26 UTC (rev 195078)
@@ -1027,6 +1027,8 @@
     void* immediateActionAnimationControllerForHitTestResult(RefPtr<API::HitTestResult>, uint64_t, RefPtr<API::Object>);
 
     void installViewStateChangeCompletionHandler(void(^completionHandler)());
+
+    void handleAcceptedCandidate(WebCore::TextCheckingResult);
 #endif
 
 #if PLATFORM(EFL) && HAVE(ACCESSIBILITY) && defined(HAVE_ECORE_X)

Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h (195077 => 195078)


--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h	2016-01-14 22:44:26 UTC (rev 195078)
@@ -1142,6 +1142,8 @@
     void dataDetectorsDidPresentUI(WebCore::PageOverlay::PageOverlayID);
     void dataDetectorsDidChangeUI(WebCore::PageOverlay::PageOverlayID);
     void dataDetectorsDidHideUI(WebCore::PageOverlay::PageOverlayID);
+
+    void handleAcceptedCandidate(WebCore::TextCheckingResult);
 #endif
 
     void setShouldDispatchFakeMouseMoveEvents(bool dispatch) { m_shouldDispatchFakeMouseMoveEvents = dispatch; }

Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in (195077 => 195078)


--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in	2016-01-14 22:44:26 UTC (rev 195078)
@@ -404,6 +404,7 @@
     DataDetectorsDidPresentUI(WebCore::PageOverlay::PageOverlayID pageOverlay)
     DataDetectorsDidChangeUI(WebCore::PageOverlay::PageOverlayID pageOverlay)
     DataDetectorsDidHideUI(WebCore::PageOverlay::PageOverlayID pageOverlay)
+    HandleAcceptedCandidate(struct WebCore::TextCheckingResult acceptedCandidate)
 #endif
 
     SetShouldDispatchFakeMouseMoveEvents(bool shouldDispatchFakeMouseMoveEvents)

Modified: trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm (195077 => 195078)


--- trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm	2016-01-14 22:44:19 UTC (rev 195077)
+++ trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm	2016-01-14 22:44:26 UTC (rev 195078)
@@ -118,10 +118,45 @@
     [m_mockAccessibilityElement setWebPage:nullptr];
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint) const
+void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
 {
+    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || !result.isContentEditable) {
+        result.isMissingPostLayoutData = true;
+        return;
+    }
+
+    const VisibleSelection& selection = frame.selection().selection();
+    RefPtr<Range> selectedRange = selection.toNormalizedRange();
+    if (!selectedRange)
+        return;
+
+    auto& postLayoutData = result.postLayoutData();
+    VisiblePosition selectionStart = selection.visibleStart();
+    VisiblePosition selectionEnd = selection.visibleEnd();
+    VisiblePosition paragraphStart = startOfParagraph(selectionStart);
+    VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
+
+    postLayoutData.candidateRequestStartPosition = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
+    postLayoutData.selectedTextLength = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get()) - postLayoutData.candidateRequestStartPosition;
+    postLayoutData.paragraphContextForCandidateRequest = plainText(makeRange(paragraphStart, paragraphEnd).get());
+    postLayoutData.stringForCandidateRequest = frame.editor().stringForCandidateRequest();
+
+    IntRect rectForSelectionCandidates;
+    Vector<FloatQuad> quads;
+    selectedRange->absoluteTextQuads(quads);
+    if (!quads.isEmpty())
+        postLayoutData.selectionClipRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
 }
 
+void WebPage::handleAcceptedCandidate(WebCore::TextCheckingResult acceptedCandidate)
+{
+    Frame* frame = m_page->focusController().focusedFrame();
+    if (!frame)
+        return;
+
+    frame->editor().handleAcceptedCandidate(acceptedCandidate);
+}
+
 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
 {
     if (!m_page)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to