Diff
Modified: trunk/LayoutTests/ChangeLog (233411 => 233412)
--- trunk/LayoutTests/ChangeLog 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/LayoutTests/ChangeLog 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1,3 +1,20 @@
+2018-07-01 Wenson Hsieh <[email protected]>
+
+ [macOS] Text replacements that end with symbols are expanded immediately
+ https://bugs.webkit.org/show_bug.cgi?id=187225
+ <rdar://problem/41112433>
+
+ Reviewed by Darin Adler.
+
+ Adds a layout test to check that if a user has configured a text replacement that ends with punctuation, then:
+ 1. Typing that text replacement won't immediately trigger replacement.
+ 2. Text replacement is triggered after pressing enter.
+
+ * editing/spelling/text-replacement-after-typing-to-word-expected.txt: Added.
+ * editing/spelling/text-replacement-after-typing-to-word.html: Added.
+ * platform/ios/TestExpectations:
+ * platform/mac-wk2/TestExpectations:
+
2018-06-29 Antoine Quint <[email protected]>
[Web Animations] Make WPT test at timing-model/timelines/document-timelines.html pass reliably
Added: trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word-expected.txt (0 => 233412)
--- trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word-expected.txt 2018-07-02 00:47:47 UTC (rev 233412)
@@ -0,0 +1,8 @@
+Before pressing enter:
+PASS editor.textContent is 'YT?'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+You there?
+
+
Added: trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word.html (0 => 233412)
--- trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word.html (rev 0)
+++ trunk/LayoutTests/editing/spelling/text-replacement-after-typing-to-word.html 2018-07-02 00:47:47 UTC (rev 233412)
@@ -0,0 +1,63 @@
+<html>
+<head>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+function zeroDelayTimer()
+{
+ return new Promise(resolve => setTimeout(resolve, 0));
+}
+
+async function runTest()
+{
+ if (window.internals) {
+ internals.settings.setUnifiedTextCheckerEnabled(true);
+ internals.settings.setAsynchronousSpellCheckingEnabled(false);
+ internals.setAutomaticTextReplacementEnabled(true);
+ internals.setAutomaticSpellingCorrectionEnabled(true);
+ testRunner.setSpellCheckerTextReplacements({
+ "YT?": {
+ "replacement": "You there?",
+ "type": "replacement",
+ "from": 0,
+ "to": 3
+ },
+ "YT?\n": {
+ "replacement": "You there?",
+ "type": "replacement",
+ "from": 0,
+ "to": 3
+ }
+ });
+ }
+
+ const editor = document.getElementById("editor");
+ editor.focus();
+ for (const character of "YT?")
+ typeCharacterCommand(character);
+ await zeroDelayTimer();
+
+ if (!window.testRunner) {
+ description.innerHTML = `To manually test, add an automatic text replacement mapping from the string "YT?" to
+ "You there?", and then type the string "YT?". It should not be immediately corrected to "You there?".
+ However, entering a newline should subsequently trigger autocorrection.`;
+ return;
+ }
+
+ debug("Before pressing enter:");
+ shouldBe("editor.textContent", "'YT?'");
+ insertParagraphCommand();
+ await zeroDelayTimer();
+ finishJSTest();
+}
+</script>
+</head>
+
+<body _onload_="runTest()">
+ <div id="description"></div>
+ <div contenteditable style="margin-bottom: 1em; border: 1px orange dashed;" id="editor"></div>
+ <script src=""
+</body>
+</html>
Modified: trunk/LayoutTests/platform/ios/TestExpectations (233411 => 233412)
--- trunk/LayoutTests/platform/ios/TestExpectations 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/LayoutTests/platform/ios/TestExpectations 2018-07-02 00:47:47 UTC (rev 233412)
@@ -110,6 +110,9 @@
fast/writing-mode/english-bt-text-with-spelling-marker.html [ WontFix ]
fast/writing-mode/english-rl-text-with-spelling-marker.html [ WontFix ]
+# Requires support for testing text replacement
+editing/spelling/text-replacement-after-typing-to-word.html [ Skip ]
+
# UIKit draws selection on iOS
fast/selectors/input-with-selection-pseudo-element.html [ WontFix ]
fast/selectors/selection-window-inactive.html [ WontFix ]
Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (233411 => 233412)
--- trunk/LayoutTests/platform/mac-wk2/TestExpectations 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations 2018-07-02 00:47:47 UTC (rev 233412)
@@ -180,6 +180,7 @@
webkit.org/b/105616 editing/spelling/grammar.html
webkit.org/b/105616 editing/spelling/markers.html
webkit.org/b/105616 editing/spelling/retro-correction-spelling-markers.html
+webkit.org/b/105616 editing/spelling/text-replacement-after-typing-to-word.html
webkit.org/b/105616 editing/spelling/spelling-markers-after-pasting-sentence.html
webkit.org/b/105616 editing/mac/spelling/autocorrection-delete.html [ Failure ]
webkit.org/b/105616 editing/mac/spelling/autocorrection-removing-underline-after-paste.html [ Failure ]
Modified: trunk/Source/WebCore/ChangeLog (233411 => 233412)
--- trunk/Source/WebCore/ChangeLog 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/ChangeLog 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1,3 +1,82 @@
+2018-07-01 Wenson Hsieh <[email protected]>
+
+ [macOS] Text replacements that end with symbols are expanded immediately
+ https://bugs.webkit.org/show_bug.cgi?id=187225
+ <rdar://problem/41112433>
+
+ Reviewed by Darin Adler.
+
+ In shipping Safari, enabling grammar correction causes text shortcuts that end with symbols or punctuation marks
+ to immediately trigger when typing; normally, when grammar correction is off, this is only triggered after the
+ user has additionally inserted a punctuation mark or whitespace character after the replaced text.
+
+ This bug happens because enabling grammar checking causes the spell checking range to expand to the range of the
+ full sentence, so any text checking results that replace an existing range are triggered as long as they end
+ anywhere in the sentence. In contrast, when grammar checking is disabled, the spell checking range is limited to
+ the nearest adjacent word, which prevents text replacement from occurring elsewhere in the sentence.
+
+ However, after r232530, we now always expand the spell checking range to the extent of the sentence when a word
+ is typed regardless of whether grammar checking is enabled, which means that the issue described above now
+ happens everywhere. To fix this recent regression and the existing bug, we:
+
+ - Augment our spellchecking codepaths to include a new automatic text replacement range, alongside
+ spellchecking and paragraph ranges.
+ - Let this automatic text replacement range be the range of the adjacent word in the case where the user has
+ finished typing a word.
+ - When marking and replacing text checking results, consult this new automatic text replacement instead of the
+ spellchecking range.
+
+ This keeps the behavior grammar and sentence retro correction results intact, while limiting the scope in which
+ text replacement results are applied.
+
+ Test: editing/spelling/text-replacement-after-typing-to-word.html
+
+ * editing/AlternativeTextController.cpp:
+ (WebCore::AlternativeTextController::timerFired):
+ * editing/Editor.cpp:
+ (WebCore::Editor::replaceSelectionWithFragment):
+ (WebCore::Editor::markMisspellingsAfterTypingToWord):
+
+ Pass in the adjacent word range for the `automaticReplacementRange`, instead of the spell checking range (which
+ may be extended to the full range of the sentence).
+
+ (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
+
+ Add an `automaticReplacementRange` argument to markAllMisspellingsAndBadGrammarInRanges, and adjust call sites
+ to pass in a range (generally the same as the spell checking range, but in the case where a word has been typed,
+ this is a narrower range).
+
+ (WebCore::correctSpellcheckingPreservingTextCheckingParagraph):
+ (WebCore::Editor::markAndReplaceFor):
+
+ When replacing text, only allow text replacement in the automatic replacement range rather than the spell
+ checking range.
+
+ (WebCore::Editor::markMisspellingsAndBadGrammar):
+ * editing/Editor.h:
+ * editing/SpellChecker.cpp:
+ (WebCore::SpellCheckRequest::SpellCheckRequest):
+
+ Add a new version of this constructor that takes a single Range representing both the spellchecking range and
+ the automatic text replacement range, for convenience.
+
+ (WebCore::SpellCheckRequest::create):
+ * editing/SpellChecker.h:
+
+ Add plumbing for the automatic replacement range.
+
+ (WebCore::SpellCheckRequest::automaticReplacementRange const):
+ * editing/TextCheckingHelper.cpp:
+
+ Add plumbing for the automatic replacement range, and new helpers to locate the range as offsets within the
+ text checking paragraph range.
+
+ (WebCore::TextCheckingParagraph::TextCheckingParagraph):
+ (WebCore::TextCheckingParagraph::invalidateParagraphRangeValues):
+ (WebCore::TextCheckingParagraph::automaticReplacementStart const):
+ (WebCore::TextCheckingParagraph::automaticReplacementLength const):
+ * editing/TextCheckingHelper.h:
+
2018-06-30 David Kilzer <[email protected]>
Follow-up: Fix clang static analyzer warnings: Garbage return value
Modified: trunk/Source/WebCore/editing/AlternativeTextController.cpp (233411 => 233412)
--- trunk/Source/WebCore/editing/AlternativeTextController.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/AlternativeTextController.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -283,7 +283,8 @@
VisiblePosition start(selection.start(), selection.affinity());
VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
VisibleSelection adjacentWords = VisibleSelection(p, start);
- m_frame.editor().markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeSpelling | TextCheckingTypeReplacement | TextCheckingTypeShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
+ auto adjacentWordRange = adjacentWords.toNormalizedRange();
+ m_frame.editor().markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeSpelling | TextCheckingTypeReplacement | TextCheckingTypeShowCorrectionPanel, adjacentWordRange.get(), adjacentWordRange.get(), nullptr);
}
break;
case AlternativeTextTypeReversion: {
Modified: trunk/Source/WebCore/editing/Editor.cpp (233411 => 233412)
--- trunk/Source/WebCore/editing/Editor.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/Editor.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -667,7 +667,7 @@
return;
auto rangeToCheck = Range::create(document(), firstPositionInNode(nodeToCheck), lastPositionInNode(nodeToCheck));
- if (auto request = SpellCheckRequest::create(resolveTextCheckingTypeMask(*nodeToCheck, TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck.copyRef(), rangeToCheck.copyRef()))
+ if (auto request = SpellCheckRequest::create(resolveTextCheckingTypeMask(*nodeToCheck, TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck.copyRef(), rangeToCheck.copyRef(), rangeToCheck.copyRef()))
m_spellChecker->requestCheckingFor(request.releaseNonNull());
}
@@ -2342,7 +2342,8 @@
return;
VisibleSelection adjacentWords = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary));
- markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWords.toNormalizedRange().get(), adjacentWords.toNormalizedRange().get());
+ auto adjacentWordRange = adjacentWords.toNormalizedRange();
+ markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, adjacentWordRange.get(), adjacentWordRange.get(), adjacentWordRange.get());
#else
#if !USE(AUTOMATIC_TEXT_REPLACEMENT)
UNUSED_PARAM(doReplacement);
@@ -2417,6 +2418,10 @@
if (!spellCheckingRange)
return;
+ auto adjacentWordRange = VisibleSelection(startOfWord(wordStart, LeftWordIfOnBoundary), endOfWord(wordStart, RightWordIfOnBoundary)).toNormalizedRange();
+ if (!adjacentWordRange)
+ return;
+
// The spelling and grammar markers in these ranges are recomputed. This is because typing a word may
// cause any other part of the current sentence to lose or gain spelling correction markers, due to
// sentence retro correction. As such, we expand the spell checking range to encompass as much of the
@@ -2423,7 +2428,7 @@
// full sentence as we can, respecting boundaries where spellchecking is disabled.
fullSentenceRange->ownerDocument().markers().removeMarkers(fullSentenceRange.get(), DocumentMarker::Grammar);
spellCheckingRange->ownerDocument().markers().removeMarkers(spellCheckingRange.get(), DocumentMarker::Spelling);
- markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellCheckingRange.get(), fullSentenceRange.get());
+ markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellCheckingRange.get(), adjacentWordRange.get(), fullSentenceRange.get());
return;
}
@@ -2547,7 +2552,7 @@
#endif
}
-void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
+void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* automaticReplacementRange, Range* grammarRange)
{
ASSERT(unifiedTextCheckerEnabled());
@@ -2579,7 +2584,8 @@
// In asynchronous mode, we intentionally check paragraph-wide sentence.
const auto resolvedOptions = resolveTextCheckingTypeMask(editableNode, textCheckingOptions);
- auto request = SpellCheckRequest::create(resolvedOptions, TextCheckingProcessIncremental, asynchronous ? paragraphRange.get() : rangeToCheck, paragraphRange.copyRef());
+ auto& textReplacementRange = automaticReplacementRange ? *automaticReplacementRange : rangeToCheck;
+ auto request = SpellCheckRequest::create(resolvedOptions, TextCheckingProcessIncremental, asynchronous ? paragraphRange.get() : rangeToCheck, textReplacementRange, paragraphRange.copyRef());
if (!request)
return;
@@ -2627,7 +2633,8 @@
RefPtr<Range> newParagraphRange = TextIterator::rangeFromLocationAndLength(&scope, paragraphLocation, paragraphLength + replacement.length() - resultLength);
- paragraph = TextCheckingParagraph(TextIterator::subrange(*newParagraphRange, resultLocation, replacement.length()), newParagraphRange.get());
+ auto spellCheckingRange = TextIterator::subrange(*newParagraphRange, resultLocation, replacement.length());
+ paragraph = TextCheckingParagraph(spellCheckingRange.copyRef(), spellCheckingRange.copyRef(), newParagraphRange.get());
}
void Editor::markAndReplaceFor(const SpellCheckRequest& request, const Vector<TextCheckingResult>& results)
@@ -2635,7 +2642,7 @@
Ref<Frame> protection(m_frame);
TextCheckingTypeMask textCheckingOptions = request.data().mask();
- TextCheckingParagraph paragraph(request.checkingRange(), &request.paragraphRange());
+ TextCheckingParagraph paragraph(request.checkingRange(), request.automaticReplacementRange(), &request.paragraphRange());
const bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
const bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
@@ -2675,6 +2682,7 @@
const int resultLocation = results[i].location + offsetDueToReplacement;
const int resultLength = results[i].length;
const int resultEndLocation = resultLocation + resultLength;
+ const int automaticReplacementEndLocation = paragraph.automaticReplacementStart() + paragraph.automaticReplacementLength() + offsetDueToReplacement;
const String& replacement = results[i].replacement;
const bool resultEndsAtAmbiguousBoundary = useAmbiguousBoundaryOffset && selectionOffset - 1 <= resultEndLocation;
@@ -2699,12 +2707,12 @@
badGrammarRange->startContainer().document().markers().addMarker(badGrammarRange.ptr(), DocumentMarker::Grammar, detail.userDescription);
}
}
- } else if (resultEndLocation <= spellingRangeEndOffset && resultEndLocation >= paragraph.checkingStart()
+ } else if (resultEndLocation <= automaticReplacementEndLocation && resultEndLocation >= paragraph.automaticReplacementStart()
&& isAutomaticTextReplacementType(resultType)) {
- // In this case the result range just has to touch the spelling range, so we can handle replacing non-word text such as punctuation.
+ // In this case the result range just has to touch the automatic replacement range, so we can handle replacing non-word text such as punctuation.
ASSERT(resultLength > 0 && resultLocation >= 0);
- if (shouldShowCorrectionPanel && (resultEndLocation < spellingRangeEndOffset
+ if (shouldShowCorrectionPanel && (resultEndLocation < automaticReplacementEndLocation
|| !(resultType & (TextCheckingTypeReplacement | TextCheckingTypeCorrection))))
continue;
@@ -2728,7 +2736,7 @@
continue;
if (shouldShowCorrectionPanel) {
- if (resultEndLocation == spellingRangeEndOffset) {
+ if (resultEndLocation == automaticReplacementEndLocation) {
// We only show the correction panel on the last word.
m_alternativeTextController->show(rangeToReplace, replacement);
break;
@@ -2829,7 +2837,8 @@
TextCheckingTypeMask textCheckingOptions = TextCheckingTypeSpelling;
if (markGrammar && isGrammarCheckingEnabled())
textCheckingOptions |= TextCheckingTypeGrammar;
- markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellingSelection.toNormalizedRange().get(), grammarSelection.toNormalizedRange().get());
+ auto spellCheckingRange = spellingSelection.toNormalizedRange();
+ markAllMisspellingsAndBadGrammarInRanges(textCheckingOptions, spellCheckingRange.get(), spellCheckingRange.get(), grammarSelection.toNormalizedRange().get());
return;
}
Modified: trunk/Source/WebCore/editing/Editor.h (233411 => 233412)
--- trunk/Source/WebCore/editing/Editor.h 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/Editor.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -293,7 +293,7 @@
bool isOverwriteModeEnabled() const { return m_overwriteModeEnabled; }
WEBCORE_EXPORT void toggleOverwriteModeEnabled();
- void markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask, Range* spellingRange, Range* grammarRange);
+ void markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask, Range* spellingRange, Range* automaticReplacementRange, Range* grammarRange);
#if PLATFORM(IOS)
NO_RETURN_DUE_TO_ASSERT
#endif
Modified: trunk/Source/WebCore/editing/SpellChecker.cpp (233411 => 233412)
--- trunk/Source/WebCore/editing/SpellChecker.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/SpellChecker.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -40,8 +40,9 @@
namespace WebCore {
-SpellCheckRequest::SpellCheckRequest(Ref<Range>&& checkingRange, Ref<Range>&& paragraphRange, const String& text, TextCheckingTypeMask mask, TextCheckingProcessType processType)
+SpellCheckRequest::SpellCheckRequest(Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange, const String& text, TextCheckingTypeMask mask, TextCheckingProcessType processType)
: m_checkingRange(WTFMove(checkingRange))
+ , m_automaticReplacementRange(WTFMove(automaticReplacementRange))
, m_paragraphRange(WTFMove(paragraphRange))
, m_rootEditableElement(m_checkingRange->startContainer().rootEditableElement())
, m_requestData(unrequestedTextCheckingSequence, text, mask, processType)
@@ -50,13 +51,13 @@
SpellCheckRequest::~SpellCheckRequest() = default;
-RefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, Ref<Range>&& checkingRange, Ref<Range>&& paragraphRange)
+RefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange)
{
String text = checkingRange->text();
if (!text.length())
return nullptr;
- return adoptRef(*new SpellCheckRequest(WTFMove(checkingRange), WTFMove(paragraphRange), text, textCheckingOptions, processType));
+ return adoptRef(*new SpellCheckRequest(WTFMove(checkingRange), WTFMove(automaticReplacementRange), WTFMove(paragraphRange), text, textCheckingOptions, processType));
}
const TextCheckingRequestData& SpellCheckRequest::data() const
Modified: trunk/Source/WebCore/editing/SpellChecker.h (233411 => 233412)
--- trunk/Source/WebCore/editing/SpellChecker.h 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/SpellChecker.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -44,11 +44,12 @@
class SpellCheckRequest : public TextCheckingRequest {
public:
- static RefPtr<SpellCheckRequest> create(TextCheckingTypeMask, TextCheckingProcessType, Ref<Range>&& checkingRange, Ref<Range>&& paragraphRange);
+ static RefPtr<SpellCheckRequest> create(TextCheckingTypeMask, TextCheckingProcessType, Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange);
virtual ~SpellCheckRequest();
Range& checkingRange() const { return m_checkingRange.get(); }
Range& paragraphRange() const { return m_paragraphRange.get(); }
+ Range& automaticReplacementRange() const { return m_automaticReplacementRange.get(); }
Element* rootEditableElement() const { return m_rootEditableElement.get(); }
void setCheckerAndSequence(SpellChecker*, int sequence);
@@ -60,10 +61,11 @@
void didCancel() override;
private:
- SpellCheckRequest(Ref<Range>&& checkingRange, Ref<Range>&& paragraphRange, const String&, TextCheckingTypeMask, TextCheckingProcessType);
+ SpellCheckRequest(Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange, const String&, TextCheckingTypeMask, TextCheckingProcessType);
SpellChecker* m_checker { nullptr };
Ref<Range> m_checkingRange;
+ Ref<Range> m_automaticReplacementRange;
Ref<Range> m_paragraphRange;
RefPtr<Element> m_rootEditableElement;
TextCheckingRequestData m_requestData;
Modified: trunk/Source/WebCore/editing/TextCheckingHelper.cpp (233411 => 233412)
--- trunk/Source/WebCore/editing/TextCheckingHelper.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/TextCheckingHelper.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -117,12 +117,16 @@
return paragraphRange;
}
-TextCheckingParagraph::TextCheckingParagraph(Ref<Range>&& checkingRange, Range* paragraphRange)
+TextCheckingParagraph::TextCheckingParagraph(Ref<Range>&& checkingAndAutomaticReplacementRange)
+ : m_checkingRange(checkingAndAutomaticReplacementRange.copyRef())
+ , m_automaticReplacementRange(checkingAndAutomaticReplacementRange.copyRef())
+{
+}
+
+TextCheckingParagraph::TextCheckingParagraph(Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, RefPtr<Range>&& paragraphRange)
: m_checkingRange(WTFMove(checkingRange))
- , m_paragraphRange(paragraphRange)
- , m_checkingStart(-1)
- , m_checkingEnd(-1)
- , m_checkingLength(-1)
+ , m_automaticReplacementRange(WTFMove(automaticReplacementRange))
+ , m_paragraphRange(WTFMove(paragraphRange))
{
}
@@ -135,6 +139,8 @@
void TextCheckingParagraph::invalidateParagraphRangeValues()
{
m_checkingStart = m_checkingEnd = -1;
+ m_automaticReplacementStart = -1;
+ m_automaticReplacementLength = -1;
m_offsetAsRange = nullptr;
m_text = String();
}
@@ -211,6 +217,26 @@
return m_checkingLength;
}
+int TextCheckingParagraph::automaticReplacementStart() const
+{
+ if (m_automaticReplacementStart != -1)
+ return m_automaticReplacementStart;
+
+ auto startOffsetRange = Range::create(paragraphRange().startContainer().document(), paragraphRange().startPosition(), m_automaticReplacementRange->startPosition());
+ m_automaticReplacementStart = TextIterator::rangeLength(startOffsetRange.ptr());
+ return m_automaticReplacementStart;
+}
+
+int TextCheckingParagraph::automaticReplacementLength() const
+{
+ if (m_automaticReplacementLength != -1)
+ return m_automaticReplacementLength;
+
+ auto endOffsetRange = Range::create(paragraphRange().startContainer().document(), paragraphRange().startPosition(), m_automaticReplacementRange->endPosition());
+ m_automaticReplacementLength = TextIterator::rangeLength(endOffsetRange.ptr()) - automaticReplacementStart();
+ return m_automaticReplacementLength;
+}
+
TextCheckingHelper::TextCheckingHelper(EditorClient& client, Range& range)
: m_client(client)
, m_range(range)
Modified: trunk/Source/WebCore/editing/TextCheckingHelper.h (233411 => 233412)
--- trunk/Source/WebCore/editing/TextCheckingHelper.h 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Source/WebCore/editing/TextCheckingHelper.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -33,7 +33,8 @@
class TextCheckingParagraph {
public:
- explicit TextCheckingParagraph(Ref<Range>&& checkingRange, Range* paragraphRange = nullptr);
+ explicit TextCheckingParagraph(Ref<Range>&& checkingAndAutomaticReplacementRange);
+ explicit TextCheckingParagraph(Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, RefPtr<Range>&& paragraphRange);
int rangeLength() const;
Ref<Range> subrange(int characterOffset, int characterCount) const;
@@ -55,6 +56,11 @@
int checkingLength() const;
String checkingSubstring() const { return textSubstring(checkingStart(), checkingLength()); }
+ // Determines the range in which we allow automatic text replacement. If an automatic replacement range is not passed to the
+ // text checking paragraph, this defaults to the spell checking range.
+ int automaticReplacementStart() const;
+ int automaticReplacementLength() const;
+
bool checkingRangeMatches(int location, int length) const { return location == checkingStart() && length == checkingLength(); }
bool isCheckingRangeCoveredBy(int location, int length) const { return location <= checkingStart() && location + length >= checkingStart() + checkingLength(); }
bool checkingRangeCovers(int location, int length) const { return location < checkingEnd() && location + length > checkingStart(); }
@@ -65,12 +71,15 @@
Range& offsetAsRange() const;
Ref<Range> m_checkingRange;
+ Ref<Range> m_automaticReplacementRange;
mutable RefPtr<Range> m_paragraphRange;
mutable RefPtr<Range> m_offsetAsRange;
mutable String m_text;
- mutable int m_checkingStart;
- mutable int m_checkingEnd;
- mutable int m_checkingLength;
+ mutable int m_checkingStart { -1 };
+ mutable int m_checkingEnd { -1 };
+ mutable int m_checkingLength { -1 };
+ mutable int m_automaticReplacementStart { -1 };
+ mutable int m_automaticReplacementLength { -1 };
};
class TextCheckingHelper {
Modified: trunk/Tools/ChangeLog (233411 => 233412)
--- trunk/Tools/ChangeLog 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/ChangeLog 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1,3 +1,87 @@
+2018-07-01 Wenson Hsieh <[email protected]>
+
+ [macOS] Text replacements that end with symbols are expanded immediately
+ https://bugs.webkit.org/show_bug.cgi?id=187225
+ <rdar://problem/41112433>
+
+ Reviewed by Darin Adler.
+
+ Adds testing support for mocking NSSpellChecker's text checking results. See below for more details, and the new
+ layout test for an example of its usage.
+
+ * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
+ * DumpRenderTree/TestRunner.cpp:
+ (setSpellCheckerTextReplacementsCallback):
+
+ Add bindings support in TestRunner to specify a set of text replacement mappings. Each entry in the dictionary
+ maps a string representing an input to the NSSpellChecker to some information describing the spell checking
+ result that LayoutTestSpellChecker will return.
+
+ (TestRunner::staticFunctions):
+ * DumpRenderTree/TestRunner.h:
+ * DumpRenderTree/mac/DumpRenderTree.mm:
+ (resetWebViewToConsistentStateBeforeTesting):
+
+ Restore the original shared NSSpellchecker before transitioning to the next layout test, if needed.
+
+ * DumpRenderTree/mac/DumpRenderTreeSpellChecker.mm: Removed.
+ * DumpRenderTree/mac/TestRunnerMac.mm:
+ (TestRunner::setSpellCheckerLoggingEnabled):
+ (TestRunner::setSpellCheckerTextReplacements):
+
+ These testRunner methods ensure that `-[NSSpellChecker sharedSpellChecker]` is swizzled to return our mock
+ LayoutTestSpellChecker instance before calling into it.
+
+ * DumpRenderTree/win/TestRunnerWin.cpp:
+ (TestRunner::setSpellCheckerTextReplacements):
+ * TestRunnerShared/cocoa/LayoutTestSpellChecker.h: Renamed from Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.h.
+ * TestRunnerShared/cocoa/LayoutTestSpellChecker.mm: Added.
+
+ Moved DumpRenderTreeSpellChecker to LayoutTestSpellChecker, and made it compile for both WebKitTestRunner
+ (modern WebKit) and DumpRenderTree (legacy WebKit) by putting it in TestRunnerShared.
+
+ (existingGlobalLayoutTestSpellChecker):
+ (ensureGlobalLayoutTestSpellChecker):
+ (stringForCorrectionResponse):
+ (nsTextCheckingType):
+ (-[LayoutTestTextCheckingResult initWithType:range:replacement:]):
+ (-[LayoutTestTextCheckingResult range]):
+ (-[LayoutTestTextCheckingResult resultType]):
+ (-[LayoutTestTextCheckingResult replacementString]):
+ (-[LayoutTestTextCheckingResult description]):
+
+ LayoutTestTextCheckingResult represents a fake NSTextCheckingResult containing spell checking results supplied
+ by the layout test.
+
+ (+[LayoutTestSpellChecker installIfNecessary]):
+ (+[LayoutTestSpellChecker uninstallAndReset]):
+
+ Helper methods to begin and end swizzling the shared NSSpellChecker.
+
+ (-[LayoutTestSpellChecker reset]):
+
+ Resets the state of the LayoutTestSpellChecker (this entails clearing the fake replacements dictionary and
+ turning off logging for recorded spellchecking responses).
+
+ (-[LayoutTestSpellChecker replacements]):
+ (-[LayoutTestSpellChecker setReplacements:]):
+ (-[LayoutTestSpellChecker setReplacementsFromJSObject:inContext:]):
+
+ Helper method to take a `JSObjectRef` supplied by the test runner and transform it into a map of spell checking
+ string inputs to LayoutTestTextCheckingResults.
+
+ (-[LayoutTestSpellChecker checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount:]):
+
+ Consult the text replacement map and bail early if a match is found, before calling into real NSSpellChecker
+ logic to perform spellchecking.
+
+ (-[LayoutTestSpellChecker recordResponse:toCorrection:forWord:language:inSpellDocumentWithTag:]):
+
+ Reimplement the original functionality in DumpRenderTreeSpellChecker to make `-recordResponse:…` print to stdout
+ by overriding the method and printing if the `-spellCheckerLoggingEnabled` property has been set to YES.
+
+ * WebKitTestRunner/WebKitTestRunner.xcodeproj/project.pbxproj:
+
2018-07-01 Thibault Saunier <[email protected]>
[WPE][GTK] Fix retrieving backtrace from within flatpak sandbox in test runner
Modified: trunk/Tools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj (233411 => 233412)
--- trunk/Tools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj 2018-07-02 00:47:47 UTC (rev 233412)
@@ -77,7 +77,6 @@
29CFBA2E12273A1000BC30C0 /* AccessibilityTextMarkerMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29CFBA2D12273A1000BC30C0 /* AccessibilityTextMarkerMac.mm */; };
2CE88FA217124D8C00734FC0 /* _javascript_Threading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CE88FA117124CEE00734FC0 /* _javascript_Threading.cpp */; };
2D403F1B15087209005358D2 /* LayoutTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D403EA215087142005358D2 /* LayoutTestHelper.m */; };
- 2DA2E3A51E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DA2E3A41E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.mm */; };
31117B3D15D9A56A00163BC8 /* MockWebNotificationProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 31117B3B15D9A56A00163BC8 /* MockWebNotificationProvider.mm */; };
312943F91E71F2B4001EE2CC /* IOSLayoutTestCommunication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3148A0551E6F90F400D3B316 /* IOSLayoutTestCommunication.cpp */; };
4464CABE1C20A08B00E5BB55 /* DumpRenderTreeAppMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4464CABD1C20A07000E5BB55 /* DumpRenderTreeAppMain.mm */; };
@@ -148,6 +147,7 @@
C23EA2081BC9F05100C980B7 /* FontWithFeatures.otf in Copy Font Files */ = {isa = PBXBuildFile; fileRef = C23EA2061BC9EABA00C980B7 /* FontWithFeatures.otf */; };
C23EA2091BC9F05100C980B7 /* FontWithFeatures.ttf in Copy Font Files */ = {isa = PBXBuildFile; fileRef = C23EA2071BC9EABA00C980B7 /* FontWithFeatures.ttf */; };
E1B7816511AF31B7007E1BC2 /* MockGeolocationProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = E1B7808711AF1669007E1BC2 /* MockGeolocationProvider.mm */; };
+ F4C3578D20E8444E00FA0748 /* LayoutTestSpellChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4C3578820E8442700FA0748 /* LayoutTestSpellChecker.mm */; };
F4D423611DD5048200678290 /* TextInputControllerIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D4235F1DD5045300678290 /* TextInputControllerIOS.m */; };
/* End PBXBuildFile section */
@@ -282,8 +282,6 @@
2CE88FA117124CEE00734FC0 /* _javascript_Threading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = _javascript_Threading.cpp; sourceTree = "<group>"; };
2D403EA215087142005358D2 /* LayoutTestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LayoutTestHelper.m; path = mac/LayoutTestHelper.m; sourceTree = "<group>"; };
2D403F19150871F9005358D2 /* LayoutTestHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = LayoutTestHelper; sourceTree = BUILT_PRODUCTS_DIR; };
- 2DA2E3A31E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DumpRenderTreeSpellChecker.h; path = mac/DumpRenderTreeSpellChecker.h; sourceTree = "<group>"; };
- 2DA2E3A41E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = DumpRenderTreeSpellChecker.mm; path = mac/DumpRenderTreeSpellChecker.mm; sourceTree = "<group>"; };
2EDE0DAA1F5131DE00D5F8DF /* AppKitTestSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppKitTestSPI.h; path = mac/AppKitTestSPI.h; sourceTree = "<group>"; };
31117B3A15D9A56A00163BC8 /* MockWebNotificationProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockWebNotificationProvider.h; path = mac/MockWebNotificationProvider.h; sourceTree = "<group>"; };
31117B3B15D9A56A00163BC8 /* MockWebNotificationProvider.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MockWebNotificationProvider.mm; path = mac/MockWebNotificationProvider.mm; sourceTree = "<group>"; };
@@ -412,6 +410,8 @@
C23EA2071BC9EABA00C980B7 /* FontWithFeatures.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = FontWithFeatures.ttf; path = fonts/FontWithFeatures.ttf; sourceTree = "<group>"; };
E1B7808511AF1643007E1BC2 /* MockGeolocationProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockGeolocationProvider.h; path = mac/MockGeolocationProvider.h; sourceTree = "<group>"; };
E1B7808711AF1669007E1BC2 /* MockGeolocationProvider.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MockGeolocationProvider.mm; path = mac/MockGeolocationProvider.mm; sourceTree = "<group>"; };
+ F4C3578820E8442700FA0748 /* LayoutTestSpellChecker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = LayoutTestSpellChecker.mm; path = ../TestRunnerShared/cocoa/LayoutTestSpellChecker.mm; sourceTree = "<group>"; };
+ F4C3578920E8442700FA0748 /* LayoutTestSpellChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LayoutTestSpellChecker.h; path = ../TestRunnerShared/cocoa/LayoutTestSpellChecker.h; sourceTree = "<group>"; };
F4D4235F1DD5045300678290 /* TextInputControllerIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TextInputControllerIOS.m; path = ios/TextInputControllerIOS.m; sourceTree = "<group>"; };
F4D423601DD5046900678290 /* TextInputController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextInputController.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -687,6 +687,7 @@
3148A0651E6F90F400D3B316 /* TestRunnerShared */ = {
isa = PBXGroup;
children = (
+ F4B6C31820E84382008AC225 /* cocoa */,
3148A0551E6F90F400D3B316 /* IOSLayoutTestCommunication.cpp */,
3148A0561E6F90F400D3B316 /* IOSLayoutTestCommunication.h */,
);
@@ -779,8 +780,6 @@
children = (
A8B91ADF0CF3B372008F91FF /* DumpRenderTreePasteboard.h */,
A8B91AD70CF3B32F008F91FF /* DumpRenderTreePasteboard.mm */,
- 2DA2E3A31E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.h */,
- 2DA2E3A41E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.mm */,
A8B91ADD0CF3B372008F91FF /* DumpRenderTreeWindow.h */,
A8B91AD90CF3B32F008F91FF /* DumpRenderTreeWindow.mm */,
);
@@ -817,6 +816,15 @@
name = PixelDump;
sourceTree = "<group>";
};
+ F4B6C31820E84382008AC225 /* cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ F4C3578920E8442700FA0748 /* LayoutTestSpellChecker.h */,
+ F4C3578820E8442700FA0748 /* LayoutTestSpellChecker.mm */,
+ );
+ name = cocoa;
+ sourceTree = "<group>";
+ };
F4D4235D1DD4F99900678290 /* mac */ = {
isa = PBXGroup;
children = (
@@ -1081,7 +1089,6 @@
BCA18B7B0C9B08F100114369 /* DumpRenderTreeDraggingInfo.mm in Sources */,
A8D79CEB0FC28B2C004AC8FE /* DumpRenderTreeFileDraggingSource.m in Sources */,
A8B91ADA0CF3B32F008F91FF /* DumpRenderTreePasteboard.mm in Sources */,
- 2DA2E3A51E1BA54100A3BBD0 /* DumpRenderTreeSpellChecker.mm in Sources */,
A8B91ADC0CF3B32F008F91FF /* DumpRenderTreeWindow.mm in Sources */,
BCA18B620C9B08C200114369 /* EditingDelegate.mm in Sources */,
BCA18B700C9B08DB00114369 /* EventSendingController.mm in Sources */,
@@ -1093,6 +1100,7 @@
2CE88FA217124D8C00734FC0 /* _javascript_Threading.cpp in Sources */,
0F18E7061D6BA0230027E547 /* JSUIScriptController.cpp in Sources */,
0F18E7131D6BC43A0027E547 /* JSWrapper.cpp in Sources */,
+ F4C3578D20E8444E00FA0748 /* LayoutTestSpellChecker.mm in Sources */,
E1B7816511AF31B7007E1BC2 /* MockGeolocationProvider.mm in Sources */,
31117B3D15D9A56A00163BC8 /* MockWebNotificationProvider.mm in Sources */,
BCA18B720C9B08DB00114369 /* NavigationController.m in Sources */,
Modified: trunk/Tools/DumpRenderTree/TestRunner.cpp (233411 => 233412)
--- trunk/Tools/DumpRenderTree/TestRunner.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/TestRunner.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1788,6 +1788,16 @@
return JSValueMakeUndefined(context);
}
+static JSValueRef setSpellCheckerTextReplacementsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ if (argumentCount < 1)
+ return JSValueMakeUndefined(context);
+
+ auto* runner = static_cast<TestRunner*>(JSObjectGetPrivate(thisObject));
+ runner->setSpellCheckerTextReplacements(context, JSValueToObject(context, arguments[0], nullptr));
+ return JSValueMakeUndefined(context);
+}
+
static JSValueRef setOpenPanelFilesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
if (argumentCount == 1)
@@ -2255,6 +2265,7 @@
{ "runUIScript", runUIScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "imageCountInGeneralPasteboard", imageCountInGeneralPasteboardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setSpellCheckerLoggingEnabled", setSpellCheckerLoggingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+ { "setSpellCheckerTextReplacements", setSpellCheckerTextReplacementsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "setOpenPanelFiles", setOpenPanelFilesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "forceImmediateCompletion", forceImmediateCompletionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ 0, 0, 0 }
Modified: trunk/Tools/DumpRenderTree/TestRunner.h (233411 => 233412)
--- trunk/Tools/DumpRenderTree/TestRunner.h 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/TestRunner.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -376,6 +376,7 @@
bool dumpJSConsoleLogInStdErr() const { return m_dumpJSConsoleLogInStdErr; }
void setSpellCheckerLoggingEnabled(bool);
+ void setSpellCheckerTextReplacements(JSContextRef, JSObjectRef replacements);
const std::vector<std::string>& openPanelFiles() const { return m_openPanelFiles; }
void setOpenPanelFiles(JSContextRef, JSValueRef);
Modified: trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm (233411 => 233412)
--- trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm 2018-07-02 00:47:47 UTC (rev 233412)
@@ -34,7 +34,6 @@
#import "DefaultPolicyDelegate.h"
#import "DumpRenderTreeDraggingInfo.h"
#import "DumpRenderTreePasteboard.h"
-#import "DumpRenderTreeSpellChecker.h"
#import "DumpRenderTreeWindow.h"
#import "EditingDelegate.h"
#import "EventSendingController.h"
@@ -41,6 +40,7 @@
#import "FrameLoadDelegate.h"
#import "HistoryDelegate.h"
#import "_javascript_Threading.h"
+#import "LayoutTestSpellChecker.h"
#import "MockGeolocationProvider.h"
#import "MockWebNotificationProvider.h"
#import "NavigationController.h"
@@ -1868,7 +1868,9 @@
[mainFrame _clearOpener];
- setSpellCheckerLoggingEnabled(false);
+#if PLATFORM(MAC)
+ [LayoutTestSpellChecker uninstallAndReset];
+#endif
resetAccumulatedLogs();
WebCoreTestSupport::initializeLogChannelsIfNecessary();
Deleted: trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.h (233411 => 233412)
--- trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.h 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-void setSpellCheckerLoggingEnabled(bool);
Deleted: trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.mm (233411 => 233412)
--- trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.mm 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.mm 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 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 "DumpRenderTreeSpellChecker.h"
-
-#if PLATFORM(MAC)
-
-#import <AppKit/NSSpellChecker.h>
-#import <wtf/Assertions.h>
-#import <wtf/ObjcRuntimeExtras.h>
-
-static bool spellCheckerLoggingEnabled;
-static IMP appKitRecordResponseIMP;
-
-static const char *stringForCorrectionResponse(NSCorrectionResponse correctionResponse)
-{
- switch (correctionResponse) {
- case NSCorrectionResponseNone:
- return "none";
- case NSCorrectionResponseAccepted:
- return "accepted";
- case NSCorrectionResponseRejected:
- return "rejected";
- case NSCorrectionResponseIgnored:
- return "ignored";
- case NSCorrectionResponseEdited:
- return "edited";
- case NSCorrectionResponseReverted:
- return "reverted";
- }
-
- return "invalid";
-}
-
-static void drt_NSSpellChecker_recordResponseToCorrection(id self, SEL _cmd, NSCorrectionResponse response, NSString *correction, NSString *word, NSString *language, NSInteger spellDocumentTag)
-{
- if (spellCheckerLoggingEnabled)
- printf("NSSpellChecker recordResponseToCorrection: %s -> %s (response: %s)\n", [word UTF8String], [correction UTF8String], stringForCorrectionResponse(response));
-
- wtfCallIMP<void, NSCorrectionResponse, NSString *, NSString *, NSString *, NSInteger>(appKitRecordResponseIMP, self, _cmd, response, correction, word, language, spellDocumentTag);
-}
-
-static void swizzleNSSpellCheckerMethodsIfNeeded()
-{
- static bool hasSwizzled;
- if (hasSwizzled)
- return;
- hasSwizzled = true;
-
- Method recordResponseMethod = class_getInstanceMethod(objc_getClass("NSSpellChecker"), @selector(recordResponse:toCorrection:forWord:language:inSpellDocumentWithTag:));
-
- appKitRecordResponseIMP = method_setImplementation(recordResponseMethod, (IMP)drt_NSSpellChecker_recordResponseToCorrection);
-}
-
-void setSpellCheckerLoggingEnabled(bool enabled)
-{
- swizzleNSSpellCheckerMethodsIfNeeded();
-
- spellCheckerLoggingEnabled = enabled;
-}
-
-#else // PLATFORM(MAC)
-
-void setSpellCheckerLoggingEnabled(bool)
-{
-}
-
-#endif // PLATFORM(MAC)
Modified: trunk/Tools/DumpRenderTree/mac/TestRunnerMac.mm (233411 => 233412)
--- trunk/Tools/DumpRenderTree/mac/TestRunnerMac.mm 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/mac/TestRunnerMac.mm 2018-07-02 00:47:47 UTC (rev 233412)
@@ -31,8 +31,8 @@
#import "TestRunner.h"
#import "DefaultPolicyDelegate.h"
-#import "DumpRenderTreeSpellChecker.h"
#import "EditingDelegate.h"
+#import "LayoutTestSpellChecker.h"
#import "MockGeolocationProvider.h"
#import "MockWebNotificationProvider.h"
#import "PolicyDelegate.h"
@@ -212,9 +212,23 @@
void TestRunner::setSpellCheckerLoggingEnabled(bool enabled)
{
- ::setSpellCheckerLoggingEnabled(enabled);
+#if PLATFORM(MAC)
+ [LayoutTestSpellChecker checker].spellCheckerLoggingEnabled = enabled;
+#else
+ UNUSED_PARAM(enabled);
+#endif
}
+void TestRunner::setSpellCheckerTextReplacements(JSContextRef context, JSObjectRef replacements)
+{
+#if PLATFORM(MAC)
+ [[LayoutTestSpellChecker checker] setReplacementsFromJSObject:replacements inContext:context];
+#else
+ UNUSED_PARAM(replacements);
+ UNUSED_PARAM(context);
+#endif
+}
+
void TestRunner::closeIdleLocalStorageDatabases()
{
[WebStorageManager closeIdleLocalStorageDatabases];
Modified: trunk/Tools/DumpRenderTree/win/TestRunnerWin.cpp (233411 => 233412)
--- trunk/Tools/DumpRenderTree/win/TestRunnerWin.cpp 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/DumpRenderTree/win/TestRunnerWin.cpp 2018-07-02 00:47:47 UTC (rev 233412)
@@ -1406,3 +1406,8 @@
{
fprintf(testResult, "ERROR: TestRunner::setSpellCheckerLoggingEnabled() not implemented\n");
}
+
+void TestRunner::setSpellCheckerTextReplacements(JSContextRef, JSObjectRef)
+{
+ fprintf(testResult, "ERROR: TestRunner::setSpellCheckerTextReplacements() not implemented\n");
+}
Copied: trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.h (from rev 233411, trunk/Tools/DumpRenderTree/mac/DumpRenderTreeSpellChecker.h) (0 => 233412)
--- trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.h (rev 0)
+++ trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.h 2018-07-02 00:47:47 UTC (rev 233412)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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
+
+#import <_javascript_Core/_javascript_Core.h>
+#import <wtf/RetainPtr.h>
+
+#if PLATFORM(MAC)
+
+#import <AppKit/NSSpellChecker.h>
+
+@class LayoutTestTextCheckingResult;
+
+@interface LayoutTestSpellChecker : NSSpellChecker {
+@private
+ RetainPtr<NSDictionary<NSString *, LayoutTestTextCheckingResult *>> _replacements;
+ BOOL _spellCheckerLoggingEnabled;
+}
+
++ (instancetype)checker;
++ (void)uninstallAndReset;
+
+- (void)setReplacementsFromJSObject:(JSObjectRef)replacements inContext:(JSContextRef)context;
+@property (nonatomic, copy) NSDictionary<NSString *, LayoutTestTextCheckingResult *> *replacements;
+@property (nonatomic) BOOL spellCheckerLoggingEnabled;
+@end
+
+#endif // PLATFORM(MAC)
Added: trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.mm (0 => 233412)
--- trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.mm (rev 0)
+++ trunk/Tools/TestRunnerShared/cocoa/LayoutTestSpellChecker.mm 2018-07-02 00:47:47 UTC (rev 233412)
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 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 "LayoutTestSpellChecker.h"
+
+#import <_javascript_Core/JSRetainPtr.h>
+#import <objc/runtime.h>
+#import <wtf/Assertions.h>
+
+#if PLATFORM(MAC)
+
+static LayoutTestSpellChecker *globalSpellChecker = nil;
+static BOOL hasSwizzledLayoutTestSpellChecker = NO;
+static IMP globallySwizzledSharedSpellCheckerImplementation;
+static Method originalSharedSpellCheckerMethod;
+
+static LayoutTestSpellChecker *ensureGlobalLayoutTestSpellChecker()
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ globalSpellChecker = [[LayoutTestSpellChecker alloc] init];
+ });
+ return globalSpellChecker;
+}
+
+static const char *stringForCorrectionResponse(NSCorrectionResponse correctionResponse)
+{
+ switch (correctionResponse) {
+ case NSCorrectionResponseNone:
+ return "none";
+ case NSCorrectionResponseAccepted:
+ return "accepted";
+ case NSCorrectionResponseRejected:
+ return "rejected";
+ case NSCorrectionResponseIgnored:
+ return "ignored";
+ case NSCorrectionResponseEdited:
+ return "edited";
+ case NSCorrectionResponseReverted:
+ return "reverted";
+ }
+ return "invalid";
+}
+
+static NSTextCheckingType nsTextCheckingType(JSRetainPtr<JSStringRef>&& jsType)
+{
+ auto cfType = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsType.get()));
+ if (CFStringCompare(cfType.get(), CFSTR("orthography"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeOrthography;
+
+ if (CFStringCompare(cfType.get(), CFSTR("spelling"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeSpelling;
+
+ if (CFStringCompare(cfType.get(), CFSTR("grammar"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeGrammar;
+
+ if (CFStringCompare(cfType.get(), CFSTR("date"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeDate;
+
+ if (CFStringCompare(cfType.get(), CFSTR("address"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeAddress;
+
+ if (CFStringCompare(cfType.get(), CFSTR("link"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeLink;
+
+ if (CFStringCompare(cfType.get(), CFSTR("quote"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeQuote;
+
+ if (CFStringCompare(cfType.get(), CFSTR("dash"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeDash;
+
+ if (CFStringCompare(cfType.get(), CFSTR("replacement"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeReplacement;
+
+ if (CFStringCompare(cfType.get(), CFSTR("correction"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeCorrection;
+
+ if (CFStringCompare(cfType.get(), CFSTR("regular-_expression_"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeRegularExpression;
+
+ if (CFStringCompare(cfType.get(), CFSTR("phone-number"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypePhoneNumber;
+
+ if (CFStringCompare(cfType.get(), CFSTR("transit-information"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ return NSTextCheckingTypeTransitInformation;
+
+ ASSERT_NOT_REACHED();
+ return NSTextCheckingTypeSpelling;
+}
+
+@interface LayoutTestTextCheckingResult : NSTextCheckingResult {
+@private
+ RetainPtr<NSString> _replacement;
+ NSTextCheckingType _type;
+ NSRange _range;
+}
+
+- (instancetype)initWithType:(NSTextCheckingType)type range:(NSRange)range replacement:(NSString *)replacement;
+@end
+
+@implementation LayoutTestTextCheckingResult
+
+- (instancetype)initWithType:(NSTextCheckingType)type range:(NSRange)range replacement:(NSString *)replacement
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _type = type;
+ _range = range;
+ _replacement = replacement;
+
+ return self;
+}
+
+- (NSRange)range
+{
+ return _range;
+}
+
+- (NSTextCheckingType)resultType
+{
+ return _type;
+}
+
+- (NSString *)replacementString
+{
+ return _replacement.get();
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@ %p type=%tu range=[%tu, %tu] replacement='%@'>", self.class, self, _type, _range.location, _range.location + _range.length, _replacement.get()];
+}
+
+@end
+
+@implementation LayoutTestSpellChecker
+
+@synthesize spellCheckerLoggingEnabled=_spellCheckerLoggingEnabled;
+
++ (instancetype)checker
+{
+ auto *spellChecker = ensureGlobalLayoutTestSpellChecker();
+ if (hasSwizzledLayoutTestSpellChecker)
+ return spellChecker;
+
+ originalSharedSpellCheckerMethod = class_getClassMethod(objc_getMetaClass("NSSpellChecker"), @selector(sharedSpellChecker));
+ globallySwizzledSharedSpellCheckerImplementation = method_setImplementation(originalSharedSpellCheckerMethod, reinterpret_cast<IMP>(ensureGlobalLayoutTestSpellChecker));
+ hasSwizzledLayoutTestSpellChecker = YES;
+ return spellChecker;
+}
+
++ (void)uninstallAndReset
+{
+ [globalSpellChecker reset];
+ if (!hasSwizzledLayoutTestSpellChecker)
+ return;
+
+ method_setImplementation(originalSharedSpellCheckerMethod, globallySwizzledSharedSpellCheckerImplementation);
+ hasSwizzledLayoutTestSpellChecker = NO;
+}
+
+- (void)reset
+{
+ self.replacements = nil;
+ self.spellCheckerLoggingEnabled = NO;
+}
+
+- (NSDictionary<NSString *, LayoutTestTextCheckingResult *> *)replacements
+{
+ return _replacements.get();
+}
+
+- (void)setReplacements:(NSDictionary<NSString *, LayoutTestTextCheckingResult *> *)replacements
+{
+ _replacements = adoptNS(replacements.copy);
+}
+
+- (void)setReplacementsFromJSObject:(JSObjectRef)replacements inContext:(JSContextRef)context
+{
+ auto fromPropertyName = adopt(JSStringCreateWithUTF8CString("from"));
+ auto toPropertyName = adopt(JSStringCreateWithUTF8CString("to"));
+ auto typePropertyName = adopt(JSStringCreateWithUTF8CString("type"));
+ auto replacementPropertyName = adopt(JSStringCreateWithUTF8CString("replacement"));
+ auto properties = JSObjectCopyPropertyNames(context, replacements);
+ auto result = adoptNS([[NSMutableDictionary alloc] init]);
+ for (size_t index = 0; index < JSPropertyNameArrayGetCount(properties); ++index) {
+ JSStringRef wordToReplace = JSPropertyNameArrayGetNameAtIndex(properties, index);
+ JSObjectRef replacement = JSValueToObject(context, JSObjectGetProperty(context, replacements, wordToReplace, nullptr), nullptr);
+ long fromValue = lroundl(JSValueToNumber(context, JSObjectGetProperty(context, replacement, fromPropertyName.get(), nullptr), nullptr));
+ long toValue = lroundl(JSValueToNumber(context, JSObjectGetProperty(context, replacement, toPropertyName.get(), nullptr), nullptr));
+ auto typeValue = adopt(JSValueToStringCopy(context, JSObjectGetProperty(context, replacement, typePropertyName.get(), nullptr), nullptr));
+ auto replacementValue = JSObjectGetProperty(context, replacement, replacementPropertyName.get(), nullptr);
+ RetainPtr<CFStringRef> replacementText;
+ if (!JSValueIsUndefined(context, replacementValue)) {
+ auto replacementJSString = adopt(JSValueToStringCopy(context, replacementValue, nullptr));
+ replacementText = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, replacementJSString.get()));
+ }
+ auto spellCheckResult = adoptNS([[LayoutTestTextCheckingResult alloc] initWithType:nsTextCheckingType(WTFMove(typeValue)) range:NSMakeRange(fromValue, toValue - fromValue) replacement:(NSString *)replacementText.get()]);
+ auto cfWordToReplace = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, wordToReplace));
+ [result setObject:spellCheckResult.get() forKey:(NSString *)cfWordToReplace.get()];
+ }
+ JSPropertyNameArrayRelease(properties);
+
+ _replacements = WTFMove(result);
+}
+
+- (NSArray<NSTextCheckingResult *> *)checkString:(NSString *)stringToCheck range:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary<NSString *, id> *)options inSpellDocumentWithTag:(NSInteger)tag orthography:(NSOrthography **)orthography wordCount:(NSInteger *)wordCount
+{
+ if (auto *result = [_replacements objectForKey:stringToCheck])
+ return @[ result ];
+
+ return [super checkString:stringToCheck range:range types:checkingTypes options:options inSpellDocumentWithTag:tag orthography:orthography wordCount:wordCount];
+}
+
+- (void)recordResponse:(NSCorrectionResponse)response toCorrection:(NSString *)correction forWord:(NSString *)word language:(NSString *)language inSpellDocumentWithTag:(NSInteger)tag
+{
+ if (_spellCheckerLoggingEnabled)
+ printf("NSSpellChecker recordResponseToCorrection: %s -> %s (response: %s)\n", [word UTF8String], [correction UTF8String], stringForCorrectionResponse(response));
+
+ [super recordResponse:response toCorrection:correction forWord:word language:language inSpellDocumentWithTag:tag];
+}
+
+@end
+
+#endif // PLATFORM(MAC)
Modified: trunk/Tools/WebKitTestRunner/WebKitTestRunner.xcodeproj/project.pbxproj (233411 => 233412)
--- trunk/Tools/WebKitTestRunner/WebKitTestRunner.xcodeproj/project.pbxproj 2018-07-01 23:51:40 UTC (rev 233411)
+++ trunk/Tools/WebKitTestRunner/WebKitTestRunner.xcodeproj/project.pbxproj 2018-07-02 00:47:47 UTC (rev 233412)
@@ -133,6 +133,7 @@
E132AA3D17CE776F00611DF0 /* WebKitTestRunnerEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = E132AA3B17CE776F00611DF0 /* WebKitTestRunnerEvent.mm */; };
E1C642C317CBCC7300D66A3C /* PoseAsClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = E1C642C117CBCC7300D66A3C /* PoseAsClass.mm */; };
E1C642C617CBCD4C00D66A3C /* WebKitTestRunnerPasteboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = E1C642C417CBCD4C00D66A3C /* WebKitTestRunnerPasteboard.mm */; };
+ F4C3578C20E8444600FA0748 /* LayoutTestSpellChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4C3578A20E8444000FA0748 /* LayoutTestSpellChecker.mm */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -372,6 +373,8 @@
E1C642C217CBCC7300D66A3C /* PoseAsClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PoseAsClass.h; sourceTree = "<group>"; };
E1C642C417CBCD4C00D66A3C /* WebKitTestRunnerPasteboard.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebKitTestRunnerPasteboard.mm; sourceTree = "<group>"; };
E1C642C517CBCD4C00D66A3C /* WebKitTestRunnerPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebKitTestRunnerPasteboard.h; sourceTree = "<group>"; };
+ F4C3578A20E8444000FA0748 /* LayoutTestSpellChecker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = LayoutTestSpellChecker.mm; path = ../TestRunnerShared/cocoa/LayoutTestSpellChecker.mm; sourceTree = "<group>"; };
+ F4C3578B20E8444000FA0748 /* LayoutTestSpellChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LayoutTestSpellChecker.h; path = ../TestRunnerShared/cocoa/LayoutTestSpellChecker.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -479,6 +482,7 @@
isa = PBXGroup;
children = (
0F18E71B1D6BC4E60027E547 /* Bindings */,
+ F4B6C31620E84369008AC225 /* cocoa */,
0F73B5471BA782FE004B3EF4 /* UIScriptContext */,
3148A0531E6F85B600D3B316 /* IOSLayoutTestCommunication.cpp */,
3148A0541E6F85B600D3B316 /* IOSLayoutTestCommunication.h */,
@@ -822,6 +826,15 @@
name = Shared;
sourceTree = "<group>";
};
+ F4B6C31620E84369008AC225 /* cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ F4C3578B20E8444000FA0748 /* LayoutTestSpellChecker.h */,
+ F4C3578A20E8444000FA0748 /* LayoutTestSpellChecker.mm */,
+ );
+ name = cocoa;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -1046,6 +1059,7 @@
31DA8A3D1E7205CC00E1DF2F /* IOSLayoutTestCommunication.cpp in Sources */,
0F73B5511BA78968004B3EF4 /* JSUIScriptController.cpp in Sources */,
0F18E7181D6BC4560027E547 /* JSWrapper.cpp in Sources */,
+ F4C3578C20E8444600FA0748 /* LayoutTestSpellChecker.mm in Sources */,
A185103C1B9AE0FE00744AEB /* Options.cpp in Sources */,
A18510401B9AE13100744AEB /* PixelDumpSupport.cpp in Sources */,
2DFA98491D7F70CF00AFF2C9 /* SharedEventStreamsMac.mm in Sources */,