Title: [243195] trunk/Source
Revision
243195
Author
[email protected]
Date
2019-03-20 00:32:40 -0700 (Wed, 20 Mar 2019)

Log Message

Add an platform-driven spell-checking mechanism
https://bugs.webkit.org/show_bug.cgi?id=195795

Reviewed by Ryosuke Niwa.

Source/WebCore:

* dom/Document.cpp:
(WebCore::Document::textInserted):
PlatformTextChecking markers are not expected
to propagate to newly inserted text, so remove them.

* dom/DocumentMarker.h:
(WebCore::DocumentMarker::allMarkers):
Add a new type of DocumentMarker, PlatformTextChecking,
and a new data variant that stores a key value pair of strings.

* dom/DocumentMarkerController.cpp:
(WebCore::DocumentMarkerController::addPlatformTextCheckingMarker):
(WebCore::DocumentMarkerController::removeMarkers):
(WebCore::DocumentMarkerController::filterMarkers):
(WebCore::shouldInsertAsSeparateMarker):
* dom/DocumentMarkerController.h:
Export some things.
Add addPlatformTextCheckingMarker, like the others.
Make it possible to filter out markers of a particular type
in a range with a predicate function.

* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::replaceTextInNodePreservingMarkers):
Propagate PlatformTextChecking data.
A future change should probably make it possible for
any DocumentMarker to copy its data here, instead of
special-casing each type that is important.

* editing/Editor.cpp:
(WebCore::Editor::markMisspellingsAfterTypingToWord):
(WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
(WebCore::Editor::markMisspellingsAndBadGrammar):
* editing/TextCheckingHelper.cpp:
(WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar):
(WebCore::TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange const):
(WebCore::platformDrivenTextCheckerEnabled):
* editing/TextCheckingHelper.h:
Bail from traditional spell checking if this mechanism is
enabled. (I wrote it this way to make it runtime switchable
in the near future, and to be similar to unifiedTextCheckerEnabled).

Source/WebKit:

* DerivedSources-input.xcfilelist:
* DerivedSources-output.xcfilelist:
* DerivedSources.make:
* SourcesCocoa.txt:
* UIProcess/Cocoa/TextCheckingController.h: Added.
* UIProcess/Cocoa/TextCheckingController.mm: Added.
(WebKit::TextCheckingController::TextCheckingController):
(WebKit::TextCheckingController::replaceRelativeToSelection):
(WebKit::TextCheckingController::removeAnnotationRelativeToSelection):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
(-[WKContentView replaceSelectionOffset:length:withAnnotatedString:relativeReplacementRange:]):
(-[WKContentView removeAnnotation:forSelectionOffset:length:]):
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebPage/WebPage.cpp:
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage::textCheckingController):
Plumb two UITextInput methods through to the Web Content process.
I added a new object instead of just sticking things on WebPage
because there are quite a few more related ones coming down the pipeline,
and will also end up being messages going in the opposite direction.

* WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h: Added.
* WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in: Added.
* WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm: Added.
(WebKit::TextCheckingControllerProxy::TextCheckingControllerProxy):
(WebKit::TextCheckingControllerProxy::~TextCheckingControllerProxy):
(WebKit::relevantMarkerTypes):
(WebKit::TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection):
(WebKit::TextCheckingControllerProxy::replaceRelativeToSelection):
(WebKit::TextCheckingControllerProxy::removeAnnotationRelativeToSelection):
Make it possible for the platform to maintain arbitrary key-value pairs
attached to document ranges, as a way for it to keep track of various
text checking context (e.g. if something has been checked, replaced,
what language it might be, ...).

Allow it to replace the text of a range and the annotations in that range,
or remove annotations in a range. Ranges are specified relative to
the selection.

One large missing piece is giving the platform the ability to retrieve
annotations in a range; that is coming in a future patch.

We translate certain annotations into traditional WebCore spelling
and grammar document markers, for normal display-time treatment.

* WebProcess/WebPage/mac/WKAccessibilityWebPageObjectMac.mm:
(-[WKAccessibilityWebPageObject convertScreenPointToRootView:]):
(-[WKAccessibilityWebPageObject accessibilityHitTest:]):
Unified sources fixes.

Source/WTF:

* wtf/Platform.h:
Add an ENABLE flag.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (243194 => 243195)


--- trunk/Source/WTF/ChangeLog	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WTF/ChangeLog	2019-03-20 07:32:40 UTC (rev 243195)
@@ -1,3 +1,13 @@
+2019-03-20  Tim Horton  <[email protected]>
+
+        Add an platform-driven spell-checking mechanism
+        https://bugs.webkit.org/show_bug.cgi?id=195795
+
+        Reviewed by Ryosuke Niwa.
+
+        * wtf/Platform.h:
+        Add an ENABLE flag.
+
 2019-03-19  Michael Catanzaro  <[email protected]>
 
         Build cleanly with GCC 9

Modified: trunk/Source/WTF/wtf/Platform.h (243194 => 243195)


--- trunk/Source/WTF/wtf/Platform.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WTF/wtf/Platform.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -1518,3 +1518,7 @@
 #if PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 130000
 #define HAVE_UI_WEB_TOUCH_EVENTS_GESTURE_RECOGNIZER_WITH_ACTIVE_TOUCHES_BY_ID 1
 #endif
+
+#if PLATFORM(IOSMAC)
+#define ENABLE_PLATFORM_DRIVEN_TEXT_CHECKING 1
+#endif

Modified: trunk/Source/WebCore/ChangeLog (243194 => 243195)


--- trunk/Source/WebCore/ChangeLog	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/ChangeLog	2019-03-20 07:32:40 UTC (rev 243195)
@@ -1,3 +1,51 @@
+2019-03-20  Tim Horton  <[email protected]>
+
+        Add an platform-driven spell-checking mechanism
+        https://bugs.webkit.org/show_bug.cgi?id=195795
+
+        Reviewed by Ryosuke Niwa.
+
+        * dom/Document.cpp:
+        (WebCore::Document::textInserted):
+        PlatformTextChecking markers are not expected
+        to propagate to newly inserted text, so remove them.
+
+        * dom/DocumentMarker.h:
+        (WebCore::DocumentMarker::allMarkers):
+        Add a new type of DocumentMarker, PlatformTextChecking,
+        and a new data variant that stores a key value pair of strings.
+
+        * dom/DocumentMarkerController.cpp:
+        (WebCore::DocumentMarkerController::addPlatformTextCheckingMarker):
+        (WebCore::DocumentMarkerController::removeMarkers):
+        (WebCore::DocumentMarkerController::filterMarkers):
+        (WebCore::shouldInsertAsSeparateMarker):
+        * dom/DocumentMarkerController.h:
+        Export some things.
+        Add addPlatformTextCheckingMarker, like the others.
+        Make it possible to filter out markers of a particular type
+        in a range with a predicate function.
+
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::CompositeEditCommand::replaceTextInNodePreservingMarkers):
+        Propagate PlatformTextChecking data.
+        A future change should probably make it possible for
+        any DocumentMarker to copy its data here, instead of
+        special-casing each type that is important.
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::markMisspellingsAfterTypingToWord):
+        (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
+        (WebCore::Editor::markMisspellingsAndBadGrammar):
+        * editing/TextCheckingHelper.cpp:
+        (WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar):
+        (WebCore::TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange const):
+        (WebCore::platformDrivenTextCheckerEnabled):
+        * editing/TextCheckingHelper.h:
+        Bail from traditional spell checking if this mechanism is
+        enabled. (I wrote it this way to make it runtime switchable
+        in the near future, and to be similar to unifiedTextCheckerEnabled).
+
 2019-03-19  Jiewen Tan  <[email protected]>
 
         [WebAuthN] Implement FIDO AppID extension

Modified: trunk/Source/WebCore/dom/Document.cpp (243194 => 243195)


--- trunk/Source/WebCore/dom/Document.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/dom/Document.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -4533,6 +4533,11 @@
 
     // Update the markers for spelling and grammar checking.
     m_markers->shiftMarkers(text, offset, length);
+
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    // Freshly inserted text is expected to not inherit PlatformTextChecking markers.
+    m_markers->removeMarkers(text, offset, length, DocumentMarker::PlatformTextChecking);
+#endif
 }
 
 void Document::textRemoved(Node& text, unsigned offset, unsigned length)

Modified: trunk/Source/WebCore/dom/DocumentMarker.h (243194 => 243195)


--- trunk/Source/WebCore/dom/DocumentMarker.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/dom/DocumentMarker.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -79,7 +79,11 @@
         // This marker indicates that the user has selected a text candidate.
         AcceptedCandidate = 1 << 13,
         // This marker indicates that the user has initiated a drag with this content.
-        DraggedContent = 1 << 14
+        DraggedContent = 1 << 14,
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+        // This marker maintains state for the platform text checker.
+        PlatformTextChecking = 1 << 15,
+#endif
     };
 
     static constexpr OptionSet<MarkerType> allMarkers();
@@ -99,7 +103,17 @@
     struct DraggedContentData {
         RefPtr<Node> targetNode;
     };
-    using Data = "" DescriptionData, DictationData, DictationAlternativesData, DraggedContentData>;
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    struct PlatformTextCheckingData {
+        String key;
+        String value;
+    };
+#endif
+    using Data = "" DescriptionData, DictationData, DictationAlternativesData, DraggedContentData
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    , PlatformTextCheckingData
+#endif
+    >;
 
     DocumentMarker(unsigned startOffset, unsigned endOffset, bool isActiveMatch);
     DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, const String& description = String());
@@ -163,6 +177,9 @@
         DictationPhraseWithAlternatives,
         DictationResult,
 #endif
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+        PlatformTextChecking
+#endif
     };
 }
 

Modified: trunk/Source/WebCore/dom/DocumentMarkerController.cpp (243194 => 243195)


--- trunk/Source/WebCore/dom/DocumentMarkerController.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/dom/DocumentMarkerController.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -142,6 +142,17 @@
     }
 }
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+void DocumentMarkerController::addPlatformTextCheckingMarker(Range& range, const String& key, const String& value)
+{
+    for (TextIterator markedText(&range); !markedText.atEnd(); markedText.advance()) {
+        auto textPiece = markedText.range();
+        DocumentMarker::PlatformTextCheckingData textCheckingData { key, value };
+        addMarker(textPiece->startContainer(), { DocumentMarker::PlatformTextChecking, textPiece->startOffset(), textPiece->endOffset(), WTFMove(textCheckingData) });
+    }
+}
+#endif
+
 void DocumentMarkerController::removeMarkers(Range& range, OptionSet<DocumentMarker::MarkerType> markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
 {
     for (TextIterator markedText(&range); !markedText.atEnd(); markedText.advance()) {
@@ -152,10 +163,24 @@
         auto textPiece = markedText.range();
         unsigned startOffset = textPiece->startOffset();
         unsigned endOffset = textPiece->endOffset();
-        removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
+        removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, nullptr, shouldRemovePartiallyOverlappingMarker);
     }
 }
 
+void DocumentMarkerController::filterMarkers(Range& range, std::function<bool(DocumentMarker*)> filterFunction, OptionSet<DocumentMarker::MarkerType> markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
+{
+    for (TextIterator markedText(&range); !markedText.atEnd(); markedText.advance()) {
+        if (!possiblyHasMarkers(markerTypes))
+            return;
+        ASSERT(!m_markers.isEmpty());
+
+        auto textPiece = markedText.range();
+        unsigned startOffset = textPiece->startOffset();
+        unsigned endOffset = textPiece->endOffset();
+        removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, filterFunction, shouldRemovePartiallyOverlappingMarker);
+    }
+}
+
 static void updateRenderedRectsForMarker(RenderedDocumentMarker& marker, Node& node)
 {
     ASSERT(!node.document().view() || !node.document().view()->needsLayout());
@@ -300,6 +325,11 @@
 
 static bool shouldInsertAsSeparateMarker(const DocumentMarker& newMarker)
 {
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    if (newMarker.type() == DocumentMarker::PlatformTextChecking)
+        return true;
+#endif
+
 #if PLATFORM(IOS_FAMILY)
     if (newMarker.type() == DocumentMarker::DictationPhraseWithAlternatives || newMarker.type() == DocumentMarker::DictationResult)
         return true;
@@ -433,7 +463,7 @@
         dstNode.renderer()->repaint();
 }
 
-void DocumentMarkerController::removeMarkers(Node& node, unsigned startOffset, int length, OptionSet<DocumentMarker::MarkerType> markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
+void DocumentMarkerController::removeMarkers(Node& node, unsigned startOffset, int length, OptionSet<DocumentMarker::MarkerType> markerTypes, std::function<bool(DocumentMarker*)> filterFunction, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
 {
     if (length <= 0)
         return;
@@ -461,6 +491,11 @@
             continue;
         }
 
+        if (filterFunction && !filterFunction(&marker)) {
+            i++;
+            continue;
+        }
+
         // at this point we know that marker and target intersect in some way
         docDirty = true;
 

Modified: trunk/Source/WebCore/dom/DocumentMarkerController.h (243194 => 243195)


--- trunk/Source/WebCore/dom/DocumentMarkerController.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/dom/DocumentMarkerController.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -48,8 +48,8 @@
     ~DocumentMarkerController();
 
     void detach();
-    void addMarker(Range&, DocumentMarker::MarkerType);
-    void addMarker(Range&, DocumentMarker::MarkerType, const String& description);
+    WEBCORE_EXPORT void addMarker(Range&, DocumentMarker::MarkerType);
+    WEBCORE_EXPORT void addMarker(Range&, DocumentMarker::MarkerType, const String& description);
     void addMarkerToNode(Node&, unsigned startOffset, unsigned length, DocumentMarker::MarkerType);
     void addMarkerToNode(Node&, unsigned startOffset, unsigned length, DocumentMarker::MarkerType, DocumentMarker::Data&&);
     WEBCORE_EXPORT void addTextMatchMarker(const Range&, bool activeMatch);
@@ -60,6 +60,10 @@
 #endif
     void addDraggedContentMarker(Range&);
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    WEBCORE_EXPORT void addPlatformTextCheckingMarker(Range&, const String& key, const String& value);
+#endif
+
     void copyMarkers(Node& srcNode, unsigned startOffset, int length, Node& dstNode, int delta);
     bool hasMarkers() const
     {
@@ -72,9 +76,12 @@
     // remove the marker. If the argument is false, we will adjust the span of the marker so that it retains
     // the portion that is outside of the range.
     enum RemovePartiallyOverlappingMarkerOrNot { DoNotRemovePartiallyOverlappingMarker, RemovePartiallyOverlappingMarker };
-    void removeMarkers(Range&, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers(), RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker);
-    void removeMarkers(Node&, unsigned startOffset, int length, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers(),  RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker);
+    WEBCORE_EXPORT void removeMarkers(Range&, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers(), RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker);
+    void removeMarkers(Node&, unsigned startOffset, int length, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers(), std::function<bool(DocumentMarker*)> filterFunction = nullptr, RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker);
 
+    // Return false from filterFunction to remove the marker.
+    WEBCORE_EXPORT void filterMarkers(Range&, std::function<bool(DocumentMarker*)> filterFunction, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers(), RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker);
+
     WEBCORE_EXPORT void removeMarkers(OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers());
     void removeMarkers(Node&, OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers());
     void repaintMarkers(OptionSet<DocumentMarker::MarkerType> = DocumentMarker::allMarkers());

Modified: trunk/Source/WebCore/editing/CompositeEditCommand.cpp (243194 => 243195)


--- trunk/Source/WebCore/editing/CompositeEditCommand.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/editing/CompositeEditCommand.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -781,6 +781,18 @@
             continue;
         }
 #endif
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+        if (marker.type() == DocumentMarker::PlatformTextChecking) {
+            if (!WTF::holds_alternative<DocumentMarker::PlatformTextCheckingData>(marker.data())) {
+                ASSERT_NOT_REACHED();
+                continue;
+            }
+
+            auto& textCheckingData = WTF::get<DocumentMarker::PlatformTextCheckingData>(marker.data());
+            markerController.addPlatformTextCheckingMarker(newRange, textCheckingData.key, textCheckingData.value);
+            continue;
+        }
+#endif
         markerController.addMarker(newRange, marker.type(), marker.description());
     }
 }

Modified: trunk/Source/WebCore/editing/Editor.cpp (243194 => 243195)


--- trunk/Source/WebCore/editing/Editor.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/editing/Editor.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -2408,6 +2408,9 @@
 {
     Ref<Frame> protection(m_frame);
 
+    if (platformDrivenTextCheckerEnabled())
+        return;
+
 #if PLATFORM(IOS_FAMILY)
     UNUSED_PARAM(selectionAfterTyping);
     UNUSED_PARAM(doReplacement);
@@ -2630,6 +2633,9 @@
 
 void Editor::markAllMisspellingsAndBadGrammarInRanges(OptionSet<TextCheckingType> textCheckingOptions, RefPtr<Range>&& spellingRange, RefPtr<Range>&& automaticReplacementRange, RefPtr<Range>&& grammarRange)
 {
+    if (platformDrivenTextCheckerEnabled())
+        return;
+
     ASSERT(unifiedTextCheckerEnabled());
 
     // There shouldn't be pending autocorrection at this moment.
@@ -2693,6 +2699,11 @@
     return false;
 }
 
+void Editor::replaceRangeForSpellChecking(Range& rangeToReplace, const String& replacement)
+{
+    SpellingCorrectionCommand::create(rangeToReplace, replacement)->apply();
+}
+
 static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, Range& rangeToReplace, const String& replacement, int resultLocation, int resultLength)
 {
     auto& scope = downcast<ContainerNode>(paragraph.paragraphRange().startContainer().rootNode());
@@ -2905,6 +2916,9 @@
 
 void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
 {
+    if (platformDrivenTextCheckerEnabled())
+        return;
+
     if (unifiedTextCheckerEnabled()) {
         if (!isContinuousSpellCheckingEnabled())
             return;

Modified: trunk/Source/WebCore/editing/Editor.h (243194 => 243195)


--- trunk/Source/WebCore/editing/Editor.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/editing/Editor.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -301,6 +301,7 @@
     void markBadGrammar(const VisibleSelection&);
     void markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection);
     void markAndReplaceFor(const SpellCheckRequest&, const Vector<TextCheckingResult>&);
+    WEBCORE_EXPORT void replaceRangeForSpellChecking(Range&, const String&);
 
     bool isOverwriteModeEnabled() const { return m_overwriteModeEnabled; }
     WEBCORE_EXPORT void toggleOverwriteModeEnabled();

Modified: trunk/Source/WebCore/editing/TextCheckingHelper.cpp (243194 => 243195)


--- trunk/Source/WebCore/editing/TextCheckingHelper.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/editing/TextCheckingHelper.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -303,6 +303,9 @@
     if (!unifiedTextCheckerEnabled())
         return emptyString();
 
+    if (platformDrivenTextCheckerEnabled())
+        return emptyString();
+
     String firstFoundItem;
     String misspelledWord;
     String badGrammarPhrase;
@@ -572,6 +575,9 @@
     if (!unifiedTextCheckerEnabled())
         return Vector<String>();
 
+    if (platformDrivenTextCheckerEnabled())
+        return Vector<String>();
+
     Vector<String> guesses;
     misspelled = false;
     ungrammatical = false;
@@ -689,4 +695,13 @@
     return frame->settings().unifiedTextCheckerEnabled();
 }
 
+bool platformDrivenTextCheckerEnabled()
+{
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    return true;
+#else
+    return false;
+#endif
 }
+
+}

Modified: trunk/Source/WebCore/editing/TextCheckingHelper.h (243194 => 243195)


--- trunk/Source/WebCore/editing/TextCheckingHelper.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebCore/editing/TextCheckingHelper.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -111,5 +111,6 @@
 void checkTextOfParagraph(TextCheckerClient&, StringView, OptionSet<TextCheckingType>, Vector<TextCheckingResult>&, const VisibleSelection& currentSelection);
 
 bool unifiedTextCheckerEnabled(const Frame*);
+bool platformDrivenTextCheckerEnabled();
 
 } // namespace WebCore

Modified: trunk/Source/WebKit/ChangeLog (243194 => 243195)


--- trunk/Source/WebKit/ChangeLog	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/ChangeLog	2019-03-20 07:32:40 UTC (rev 243195)
@@ -1,3 +1,62 @@
+2019-03-20  Tim Horton  <[email protected]>
+
+        Add an platform-driven spell-checking mechanism
+        https://bugs.webkit.org/show_bug.cgi?id=195795
+
+        Reviewed by Ryosuke Niwa.
+
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources-output.xcfilelist:
+        * DerivedSources.make:
+        * SourcesCocoa.txt:
+        * UIProcess/Cocoa/TextCheckingController.h: Added.
+        * UIProcess/Cocoa/TextCheckingController.mm: Added.
+        (WebKit::TextCheckingController::TextCheckingController):
+        (WebKit::TextCheckingController::replaceRelativeToSelection):
+        (WebKit::TextCheckingController::removeAnnotationRelativeToSelection):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView replaceSelectionOffset:length:withAnnotatedString:relativeReplacementRange:]):
+        (-[WKContentView removeAnnotation:forSelectionOffset:length:]):
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebPage/WebPage.cpp:
+        * WebProcess/WebPage/WebPage.h:
+        (WebKit::WebPage::textCheckingController):
+        Plumb two UITextInput methods through to the Web Content process.
+        I added a new object instead of just sticking things on WebPage
+        because there are quite a few more related ones coming down the pipeline,
+        and will also end up being messages going in the opposite direction.
+
+        * WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h: Added.
+        * WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in: Added.
+        * WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm: Added.
+        (WebKit::TextCheckingControllerProxy::TextCheckingControllerProxy):
+        (WebKit::TextCheckingControllerProxy::~TextCheckingControllerProxy):
+        (WebKit::relevantMarkerTypes):
+        (WebKit::TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection):
+        (WebKit::TextCheckingControllerProxy::replaceRelativeToSelection):
+        (WebKit::TextCheckingControllerProxy::removeAnnotationRelativeToSelection):
+        Make it possible for the platform to maintain arbitrary key-value pairs
+        attached to document ranges, as a way for it to keep track of various
+        text checking context (e.g. if something has been checked, replaced,
+        what language it might be, ...).
+
+        Allow it to replace the text of a range and the annotations in that range,
+        or remove annotations in a range. Ranges are specified relative to
+        the selection.
+
+        One large missing piece is giving the platform the ability to retrieve
+        annotations in a range; that is coming in a future patch.
+
+        We translate certain annotations into traditional WebCore spelling
+        and grammar document markers, for normal display-time treatment.
+
+        * WebProcess/WebPage/mac/WKAccessibilityWebPageObjectMac.mm:
+        (-[WKAccessibilityWebPageObject convertScreenPointToRootView:]):
+        (-[WKAccessibilityWebPageObject accessibilityHitTest:]):
+        Unified sources fixes.
+
 2019-03-19  Jiewen Tan  <[email protected]>
 
         [WebAuthN] Implement FIDO AppID extension

Modified: trunk/Source/WebKit/DerivedSources-input.xcfilelist (243194 => 243195)


--- trunk/Source/WebKit/DerivedSources-input.xcfilelist	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/DerivedSources-input.xcfilelist	2019-03-20 07:32:40 UTC (rev 243195)
@@ -112,6 +112,7 @@
 $(PROJECT_DIR)/WebProcess/Storage/WebSWContextManagerConnection.messages.in
 $(PROJECT_DIR)/WebProcess/UserContent/WebUserContentController.messages.in
 $(PROJECT_DIR)/WebProcess/WebAuthentication/WebAuthenticatorCoordinator.messages.in
+$(PROJECT_DIR)/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in
 $(PROJECT_DIR)/WebProcess/WebPage/DrawingArea.messages.in
 $(PROJECT_DIR)/WebProcess/WebPage/EventDispatcher.messages.in
 $(PROJECT_DIR)/WebProcess/WebPage/RemoteLayerTree/RemoteScrollingCoordinator.messages.in

Modified: trunk/Source/WebKit/DerivedSources-output.xcfilelist (243194 => 243195)


--- trunk/Source/WebKit/DerivedSources-output.xcfilelist	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/DerivedSources-output.xcfilelist	2019-03-20 07:32:40 UTC (rev 243195)
@@ -88,6 +88,8 @@
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/StorageAreaMapMessages.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/StorageManagerMessageReceiver.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/StorageManagerMessages.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/TextCheckingControllerProxyMessageReceiver.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/TextCheckingControllerProxyMessages.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/UserMediaCaptureManagerMessageReceiver.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/UserMediaCaptureManagerMessages.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebKit2/UserMediaCaptureManagerProxyMessageReceiver.cpp

Modified: trunk/Source/WebKit/DerivedSources.make (243194 => 243195)


--- trunk/Source/WebKit/DerivedSources.make	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/DerivedSources.make	2019-03-20 07:32:40 UTC (rev 243195)
@@ -59,6 +59,7 @@
     $(WebKit2)/WebProcess/WebAuthentication \
     $(WebKit2)/WebProcess/WebCoreSupport \
     $(WebKit2)/WebProcess/WebPage \
+    $(WebKit2)/WebProcess/WebPage/Cocoa \
     $(WebKit2)/WebProcess/WebPage/RemoteLayerTree \
     $(WebKit2)/WebProcess/WebStorage \
     $(WebKit2)/WebProcess/cocoa \
@@ -135,6 +136,7 @@
     SmartMagnificationController \
     StorageAreaMap \
     StorageManager \
+    TextCheckingControllerProxy \
     UserMediaCaptureManager \
     UserMediaCaptureManagerProxy \
     VideoFullscreenManager \

Modified: trunk/Source/WebKit/SourcesCocoa.txt (243194 => 243195)


--- trunk/Source/WebKit/SourcesCocoa.txt	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2019-03-20 07:32:40 UTC (rev 243195)
@@ -339,6 +339,7 @@
 UIProcess/Cocoa/SafeBrowsingWarningCocoa.mm
 UIProcess/Cocoa/SessionStateCoding.mm
 UIProcess/Cocoa/SystemPreviewControllerCocoa.mm
+UIProcess/Cocoa/TextCheckingController.mm
 UIProcess/Cocoa/UIDelegate.mm
 UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp
 UIProcess/Cocoa/VersionChecks.mm
@@ -558,6 +559,7 @@
 WebProcess/WebPage/ViewUpdateDispatcher.cpp
 WebProcess/WebPage/WKAccessibilityWebPageObjectIOS.mm
 
+WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm
 WebProcess/WebPage/Cocoa/WebPageCocoa.mm
 
 WebProcess/WebPage/ios/FindControllerIOS.mm
@@ -584,3 +586,4 @@
 // Derived Sources
 EditableImageControllerMessageReceiver.cpp
 ServiceWorkerFetchTaskMessageReceiver.cpp
+TextCheckingControllerProxyMessageReceiver.cpp

Added: trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.h (0 => 243195)


--- trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -0,0 +1,54 @@
+/*
+ * 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 ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+
+#import "EditingRange.h"
+#import "WebPageProxy.h"
+#import <WebCore/FloatRect.h>
+#import <wtf/CompletionHandler.h>
+
+OBJC_CLASS NSAttributedString;
+
+namespace WebKit {
+
+class TextCheckingController final {
+    WTF_MAKE_NONCOPYABLE(TextCheckingController);
+public:
+    explicit TextCheckingController(WebPageProxy&);
+    ~TextCheckingController() = default;
+
+    void replaceRelativeToSelection(NSAttributedString *annotatedString, int64_t selectionOffset, uint64_t length, bool textChanged);
+    void removeAnnotationRelativeToSelection(NSString *annotationName, int64_t selectionOffset, uint64_t length);
+
+private:
+    WebPageProxy& m_page;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)

Added: trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.mm (0 => 243195)


--- trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.mm	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/Cocoa/TextCheckingController.mm	2019-03-20 07:32:40 UTC (rev 243195)
@@ -0,0 +1,60 @@
+/*
+ * 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"
+#import "TextCheckingController.h"
+
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+
+#import "AttributedString.h"
+#import "TextCheckingControllerProxyMessages.h"
+#import "WebProcessProxy.h"
+
+namespace WebKit {
+
+TextCheckingController::TextCheckingController(WebPageProxy& webPageProxy)
+    : m_page(webPageProxy)
+{
+}
+
+void TextCheckingController::replaceRelativeToSelection(NSAttributedString *annotatedString, int64_t selectionOffset, uint64_t length, bool textChanged)
+{
+    if (!m_page.hasRunningProcess())
+        return;
+
+    m_page.process().send(Messages::TextCheckingControllerProxy::ReplaceRelativeToSelection(annotatedString, selectionOffset, length, textChanged), m_page.pageID());
+}
+
+void TextCheckingController::removeAnnotationRelativeToSelection(NSString *annotationName, int64_t selectionOffset, uint64_t length)
+{
+    if (!m_page.hasRunningProcess())
+        return;
+
+    m_page.process().send(Messages::TextCheckingControllerProxy::RemoveAnnotationRelativeToSelection(annotationName, selectionOffset, length), m_page.pageID());
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)

Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm (243194 => 243195)


--- trunk/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm	2019-03-20 07:32:40 UTC (rev 243195)
@@ -64,11 +64,6 @@
 #import "UIKitSPI.h"
 #endif
 
-#if PLATFORM(IOS)
-#import "UIKitSPI.h"
-#import <wtf/SoftLinking.h>
-#endif
-
 NSString *WebServiceWorkerRegistrationDirectoryDefaultsKey = @"WebServiceWorkerRegistrationDirectory";
 NSString *WebKitLocalCacheDefaultsKey = @"WebKitLocalCache";
 NSString *WebKitJSCJITEnabledDefaultsKey = @"WebKitJSCJITEnabledDefaultsKey";

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (243194 => 243195)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -36,6 +36,7 @@
 #import "FocusedElementInformation.h"
 #import "GestureTypes.h"
 #import "InteractionInformationAtPosition.h"
+#import "TextCheckingController.h"
 #import "UIKitSPI.h"
 #import "WKActionSheetAssistant.h"
 #import "WKAirPlayRoutePicker.h"
@@ -340,6 +341,10 @@
     BOOL _shouldRestoreFirstResponderStatusAfterLosingFocus;
     BlockPtr<void()> _activeFocusedStateRetainBlock;
 #endif
+
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    std::unique_ptr<WebKit::TextCheckingController> _textCheckingController;
+#endif
 }
 
 @end

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (243194 => 243195)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-03-20 07:32:40 UTC (rev 243195)
@@ -782,6 +782,10 @@
     _dataListTextSuggestions = nil;
 #endif
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    _textCheckingController = std::make_unique<WebKit::TextCheckingController>(*_page);
+#endif
+
     _hasSetUpInteractions = YES;
 }
 
@@ -5511,6 +5515,18 @@
     _page->extendSelection(WebCore::WordGranularity);
 }
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+- (void)replaceSelectionOffset:(NSInteger)selectionOffset length:(NSUInteger)length withAnnotatedString:(NSAttributedString *)annotatedString relativeReplacementRange:(NSRange)relativeReplacementRange
+{
+    _textCheckingController->replaceRelativeToSelection(annotatedString, selectionOffset, length, relativeReplacementRange.location != NSNotFound);
+}
+
+- (void)removeAnnotation:(NSAttributedStringKey)annotationName forSelectionOffset:(NSInteger)selectionOffset length:(NSUInteger)length
+{
+    _textCheckingController->removeAnnotationRelativeToSelection(annotationName, selectionOffset, length);
+}
+#endif
+
 - (void)_updateChangedSelection
 {
     [self _updateChangedSelection:NO];

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (243194 => 243195)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2019-03-20 07:32:40 UTC (rev 243195)
@@ -2476,6 +2476,8 @@
 		29D04E2821F7C73D0076741D /* AccessibilityPrivSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AccessibilityPrivSPI.h; sourceTree = "<group>"; };
 		2D0035221BC7414800DA8716 /* PDFPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PDFPlugin.h; path = PDF/PDFPlugin.h; sourceTree = "<group>"; };
 		2D0035231BC7414800DA8716 /* PDFPlugin.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PDFPlugin.mm; path = PDF/PDFPlugin.mm; sourceTree = "<group>"; };
+		2D0CF64B21F2A80300787566 /* TextCheckingController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TextCheckingController.mm; sourceTree = "<group>"; };
+		2D0CF64C21F2A80300787566 /* TextCheckingController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextCheckingController.h; sourceTree = "<group>"; };
 		2D10875E1D2C573E00B85F82 /* LoadParameters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LoadParameters.cpp; sourceTree = "<group>"; };
 		2D10875F1D2C573E00B85F82 /* LoadParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadParameters.h; sourceTree = "<group>"; };
 		2D1087621D2C641B00B85F82 /* LoadParametersCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadParametersCocoa.mm; sourceTree = "<group>"; };
@@ -2739,6 +2741,9 @@
 		2D8786221BDB58FF00D02ABB /* APIUserStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIUserStyleSheet.h; sourceTree = "<group>"; };
 		2D8949EE182044F600E898AA /* PlatformCALayerRemoteTiledBacking.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlatformCALayerRemoteTiledBacking.cpp; sourceTree = "<group>"; };
 		2D8949EF182044F600E898AA /* PlatformCALayerRemoteTiledBacking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformCALayerRemoteTiledBacking.h; sourceTree = "<group>"; };
+		2D9CD5EB21FA503F0029ACFA /* TextCheckingControllerProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = TextCheckingControllerProxy.messages.in; sourceTree = "<group>"; };
+		2D9CD5EC21FA503F0029ACFA /* TextCheckingControllerProxy.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TextCheckingControllerProxy.mm; sourceTree = "<group>"; };
+		2D9CD5ED21FA503F0029ACFA /* TextCheckingControllerProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextCheckingControllerProxy.h; sourceTree = "<group>"; };
 		2D9CD5EE21FA75EE0029ACFA /* EditingRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditingRange.cpp; sourceTree = "<group>"; };
 		2D9EA30C1A96CB59002D2807 /* WKPageInjectedBundleClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKPageInjectedBundleClient.h; sourceTree = "<group>"; };
 		2D9EA30E1A96CBFF002D2807 /* WebPageInjectedBundleClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageInjectedBundleClient.h; sourceTree = "<group>"; };
@@ -5375,6 +5380,8 @@
 				1A002D47196B345D00B9AD44 /* SessionStateCoding.h */,
 				1A002D46196B345D00B9AD44 /* SessionStateCoding.mm */,
 				3157135C2040A9B20084F9CF /* SystemPreviewControllerCocoa.mm */,
+				2D0CF64C21F2A80300787566 /* TextCheckingController.h */,
+				2D0CF64B21F2A80300787566 /* TextCheckingController.mm */,
 				1AFE436418B6C081009C7A48 /* UIDelegate.h */,
 				1AFE436318B6C081009C7A48 /* UIDelegate.mm */,
 				E4E8648E1B1673FB00C82F40 /* VersionChecks.h */,
@@ -5613,6 +5620,9 @@
 		2D29ECCD192F2C2E00984B78 /* Cocoa */ = {
 			isa = PBXGroup;
 			children = (
+				2D9CD5ED21FA503F0029ACFA /* TextCheckingControllerProxy.h */,
+				2D9CD5EB21FA503F0029ACFA /* TextCheckingControllerProxy.messages.in */,
+				2D9CD5EC21FA503F0029ACFA /* TextCheckingControllerProxy.mm */,
 				2DC4CF7A1D2DE24B00ECCC94 /* WebPageCocoa.mm */,
 			);
 			path = Cocoa;

Added: trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h (0 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -0,0 +1,69 @@
+/*
+ * 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 ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+
+#include "AttributedString.h"
+#include "Connection.h"
+#include "EditingRange.h"
+#include "MessageReceiver.h"
+#include <wtf/Vector.h>
+
+namespace IPC {
+class Decoder;
+class Encoder;
+}
+
+namespace WebKit {
+
+class WebPage;
+
+class TextCheckingControllerProxy : public IPC::MessageReceiver {
+public:
+    TextCheckingControllerProxy(WebPage&);
+    ~TextCheckingControllerProxy();
+
+private:
+    // IPC::MessageReceiver
+    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
+
+    struct RangeAndOffset {
+        RefPtr<WebCore::Range> range;
+        size_t locationInRoot;    
+    };
+    Optional<RangeAndOffset> rangeAndOffsetRelativeToSelection(int64_t offset, uint64_t length);
+
+    // Message handlers.
+    void replaceRelativeToSelection(AttributedString, int64_t selectionOffset, uint64_t length, bool textChanged);
+    void removeAnnotationRelativeToSelection(String annotationName, int64_t selectionOffset, uint64_t length);
+
+    WebPage& m_page;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)

Added: trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in (0 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.messages.in	2019-03-20 07:32:40 UTC (rev 243195)
@@ -0,0 +1,31 @@
+# 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.
+
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+
+messages -> TextCheckingControllerProxy {
+    ReplaceRelativeToSelection(struct WebKit::AttributedString annotatedString, int64_t selectionOffset, uint64_t length, bool textChanged)
+
+    RemoveAnnotationRelativeToSelection(String annotationName, int64_t selectionOffset, uint64_t length)
+}
+
+#endif
\ No newline at end of file

Added: trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm (0 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm	2019-03-20 07:32:40 UTC (rev 243195)
@@ -0,0 +1,168 @@
+/*
+ * 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"
+#import "TextCheckingControllerProxy.h"
+
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+
+#import "ArgumentCoders.h"
+#import "TextCheckingControllerProxyMessages.h"
+#import "WebCoreArgumentCoders.h"
+#import "WebPage.h"
+#import "WebProcess.h"
+#import <WebCore/DocumentMarker.h>
+#import <WebCore/DocumentMarkerController.h>
+#import <WebCore/Editing.h>
+#import <WebCore/Editor.h>
+#import <WebCore/FocusController.h>
+#import <WebCore/RenderObject.h>
+#import <WebCore/RenderedDocumentMarker.h>
+#import <WebCore/TextIterator.h>
+#import <WebCore/VisibleUnits.h>
+
+// FIXME: Remove this after rdar://problem/48914153 is resolved.
+#if PLATFORM(IOSMAC)
+typedef NS_ENUM(NSInteger, NSSpellingState) {
+    NSSpellingStateSpellingFlag = (1 << 0),
+    NSSpellingStateGrammarFlag = (1 << 1)
+};
+#endif
+
+namespace WebKit {
+using namespace WebCore;
+
+TextCheckingControllerProxy::TextCheckingControllerProxy(WebPage& page)
+    : m_page(page)
+{
+    WebProcess::singleton().addMessageReceiver(Messages::TextCheckingControllerProxy::messageReceiverName(), m_page.pageID(), *this);
+}
+
+TextCheckingControllerProxy::~TextCheckingControllerProxy()
+{
+    WebProcess::singleton().removeMessageReceiver(Messages::TextCheckingControllerProxy::messageReceiverName(), m_page.pageID());
+}
+
+static OptionSet<DocumentMarker::MarkerType> relevantMarkerTypes()
+{
+    return { DocumentMarker::PlatformTextChecking, DocumentMarker::Spelling, DocumentMarker::Grammar };
+}
+
+Optional<TextCheckingControllerProxy::RangeAndOffset> TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection(int64_t offset, uint64_t length)
+{
+    Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
+    const FrameSelection& frameSelection = frame.selection();
+    const VisibleSelection& selection = frameSelection.selection();
+    if (selection.isNone())
+        return WTF::nullopt;
+
+    auto root = frameSelection.rootEditableElementOrDocumentElement();
+    auto range = selection.toNormalizedRange();
+    range->collapse(true);
+
+    size_t selectionLocation;
+    size_t selectionLength;
+    TextIterator::getLocationAndLengthFromRange(root, range.get(), selectionLocation, selectionLength);
+    selectionLocation += offset;
+
+    return {{ TextIterator::rangeFromLocationAndLength(root, selectionLocation, length), selectionLocation }};
+}
+
+void TextCheckingControllerProxy::replaceRelativeToSelection(AttributedString annotatedString, int64_t selectionOffset, uint64_t length, bool textChanged)
+{
+    Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
+    FrameSelection& frameSelection = frame.selection();
+    auto root = frameSelection.rootEditableElementOrDocumentElement();
+
+    auto rangeAndOffset = rangeAndOffsetRelativeToSelection(selectionOffset, length);
+    if (!rangeAndOffset)
+        return;
+    auto range = rangeAndOffset->range;
+    if (!range)
+        return;
+    auto locationInRoot = rangeAndOffset->locationInRoot;
+
+    auto& markers = frame.document()->markers();
+    markers.removeMarkers(*range, relevantMarkerTypes());
+
+    if (textChanged) {
+        bool restoreSelection = frameSelection.selection().isRange();
+
+        frame.editor().replaceRangeForSpellChecking(*range, [annotatedString.string string]);
+
+        size_t selectionLocationToRestore = locationInRoot - selectionOffset;
+        if (restoreSelection && selectionLocationToRestore > locationInRoot + length) {
+            selectionLocationToRestore -= locationInRoot - length;
+            auto selectionToRestore = TextIterator::rangeFromLocationAndLength(root, selectionLocationToRestore, 0);
+            frameSelection.moveTo(selectionToRestore.get());
+        }
+    }
+
+    [annotatedString.string enumerateAttributesInRange:NSMakeRange(0, [annotatedString.string length]) options:0 usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange attributeRange, BOOL *stop) {
+        auto attributeCoreRange = TextIterator::rangeFromLocationAndLength(root, locationInRoot + attributeRange.location, attributeRange.length);
+        if (!attributeCoreRange)
+            return;
+
+        [attrs enumerateKeysAndObjectsUsingBlock:^(NSAttributedStringKey key, id value, BOOL *stop) {
+            if (![value isKindOfClass:[NSString class]])
+                return;
+            markers.addPlatformTextCheckingMarker(*attributeCoreRange, key, (NSString *)value);
+
+            // FIXME: Switch to constants after rdar://problem/48914153 is resolved.
+            if ([key isEqualToString:@"NSSpellingState"]) {
+                NSSpellingState spellingState = (NSSpellingState)[value integerValue];
+                if (spellingState & NSSpellingStateSpellingFlag)
+                    markers.addMarker(*attributeCoreRange, DocumentMarker::Spelling);
+                if (spellingState & NSSpellingStateGrammarFlag) {
+                    NSString *userDescription = [attrs objectForKey:@"NSGrammarUserDescription"];
+                    markers.addMarker(*attributeCoreRange, DocumentMarker::Grammar, userDescription);
+                }
+            }
+        }];
+    }];
+}
+
+void TextCheckingControllerProxy::removeAnnotationRelativeToSelection(String annotation, int64_t selectionOffset, uint64_t length)
+{
+    Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
+    auto rangeAndOffset = rangeAndOffsetRelativeToSelection(selectionOffset, length);
+    if (!rangeAndOffset)
+        return;
+    auto range = rangeAndOffset->range;
+    if (!range)
+        return;
+
+    bool removeCoreSpellingMarkers = (annotation == String(@"NSSpellingState"));
+    frame.document()->markers().filterMarkers(*range, [&] (DocumentMarker* marker) {
+        if (!WTF::holds_alternative<DocumentMarker::PlatformTextCheckingData>(marker->data()))
+            return false;
+        auto& textCheckingData = WTF::get<DocumentMarker::PlatformTextCheckingData>(marker->data());
+        return textCheckingData.key != annotation;
+    }, removeCoreSpellingMarkers ? relevantMarkerTypes() : DocumentMarker::PlatformTextChecking);
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (243194 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-03-20 07:32:40 UTC (rev 243195)
@@ -252,6 +252,7 @@
 #include "PDFPlugin.h"
 #include "PlaybackSessionManager.h"
 #include "RemoteLayerTreeTransaction.h"
+#include "TextCheckingControllerProxy.h"
 #include "TouchBarMenuData.h"
 #include "TouchBarMenuItemData.h"
 #include "VideoFullscreenManager.h"
@@ -370,6 +371,9 @@
     , m_determinePrimarySnapshottedPlugInTimer(RunLoop::main(), this, &WebPage::determinePrimarySnapshottedPlugInTimerFired)
 #endif
     , m_layerHostingMode(parameters.layerHostingMode)
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    , m_textCheckingControllerProxy(makeUniqueRef<TextCheckingControllerProxy>(*this))
+#endif
 #if PLATFORM(COCOA) || PLATFORM(GTK)
     , m_viewGestureGeometryCollector(std::make_unique<ViewGestureGeometryCollector>(*this))
 #elif HAVE(ACCESSIBILITY) && PLATFORM(GTK)

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (243194 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2019-03-20 07:32:40 UTC (rev 243195)
@@ -207,6 +207,7 @@
 class PageBanner;
 class PluginView;
 class RemoteWebInspectorUI;
+class TextCheckingControllerProxy;
 class UserMediaPermissionRequestManager;
 class ViewGestureGeometryCollector;
 class VisibleContentRectUpdateInfo;
@@ -1167,6 +1168,10 @@
     WebPaymentCoordinator* paymentCoordinator();
 #endif
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    TextCheckingControllerProxy& textCheckingController() { return m_textCheckingControllerProxy.get(); }
+#endif
+
 private:
     WebPage(uint64_t pageID, WebPageCreationParameters&&);
 
@@ -1449,8 +1454,6 @@
 
     static PluginView* focusedPluginViewForFrame(WebCore::Frame&);
 
-    static RefPtr<WebCore::Range> rangeFromEditingRange(WebCore::Frame&, const EditingRange&, EditingRangeIsRelativeTo = EditingRangeIsRelativeTo::EditableRoot);
-
     void reportUsedFeatures();
 
     void updateWebsitePolicies(WebsitePoliciesData&&);
@@ -1612,6 +1615,10 @@
     RetainPtr<WKAccessibilityWebPageObject> m_mockAccessibilityElement;
 #endif
 
+#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
+    UniqueRef<TextCheckingControllerProxy> m_textCheckingControllerProxy;
+#endif
+
 #if PLATFORM(COCOA) || PLATFORM(GTK)
     std::unique_ptr<ViewGestureGeometryCollector> m_viewGestureGeometryCollector;
 #endif

Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/WKAccessibilityWebPageObjectMac.mm (243194 => 243195)


--- trunk/Source/WebKit/WebProcess/WebPage/mac/WKAccessibilityWebPageObjectMac.mm	2019-03-20 06:51:19 UTC (rev 243194)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/WKAccessibilityWebPageObjectMac.mm	2019-03-20 07:32:40 UTC (rev 243195)
@@ -119,7 +119,7 @@
 - (NSPoint)convertScreenPointToRootView:(NSPoint)point
 {
     return retrieveAccessibilityValueFromMainThread<NSPoint>([&self, &point] () -> NSPoint {
-        return m_page->screenToRootView(IntPoint(point.x, point.y));
+        return m_page->screenToRootView(WebCore::IntPoint(point.x, point.y));
     });
 }
 
@@ -238,11 +238,11 @@
 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
 - (id)accessibilityHitTest:(NSPoint)point
 {
-    auto convertedPoint = retrieveAccessibilityValueFromMainThread<IntPoint>([&self, &point] () -> IntPoint {
+    auto convertedPoint = retrieveAccessibilityValueFromMainThread<WebCore::IntPoint>([&self, &point] () -> WebCore::IntPoint {
         if (!m_page)
-            return IntPoint(point);
+            return WebCore::IntPoint(point);
         
-        auto convertedPoint = m_page->screenToRootView(IntPoint(point));
+        auto convertedPoint = m_page->screenToRootView(WebCore::IntPoint(point));
         
         // Some plugins may be able to figure out the scroll position and inset on their own.
         bool applyContentOffset = true;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to