Adopt NSTextCheckingSuppressInitialCapitalizationKey
[WebKit-https.git] / Source / WebKit / UIProcess / mac / TextCheckerMac.mm
1 /*
2  * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "TextChecker.h"
28
29 #if PLATFORM(MAC)
30
31 #import "TextCheckerState.h"
32 #import <WebCore/NotImplemented.h>
33 #import <pal/spi/mac/NSSpellCheckerSPI.h>
34 #import <wtf/NeverDestroyed.h>
35 #import <wtf/RetainPtr.h>
36 #import <wtf/text/StringView.h>
37
38 @interface NSSpellChecker (WebNSSpellCheckerDetails)
39 - (NSString *)languageForWordRange:(NSRange)range inString:(NSString *)string orthography:(NSOrthography *)orthography;
40 @end
41
42 static NSString* const WebAutomaticSpellingCorrectionEnabled = @"WebAutomaticSpellingCorrectionEnabled";
43 static NSString* const WebContinuousSpellCheckingEnabled = @"WebContinuousSpellCheckingEnabled";
44 static NSString* const WebGrammarCheckingEnabled = @"WebGrammarCheckingEnabled";
45 static NSString* const WebSmartInsertDeleteEnabled = @"WebSmartInsertDeleteEnabled";
46 static NSString* const WebAutomaticQuoteSubstitutionEnabled = @"WebAutomaticQuoteSubstitutionEnabled";
47 static NSString* const WebAutomaticDashSubstitutionEnabled = @"WebAutomaticDashSubstitutionEnabled";
48 static NSString* const WebAutomaticLinkDetectionEnabled = @"WebAutomaticLinkDetectionEnabled";
49 static NSString* const WebAutomaticTextReplacementEnabled = @"WebAutomaticTextReplacementEnabled";
50
51 namespace WebKit {
52 using namespace WebCore;
53
54 static bool shouldAutomaticTextReplacementBeEnabled()
55 {
56     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
57     if (![defaults objectForKey:WebAutomaticTextReplacementEnabled])
58         return [NSSpellChecker isAutomaticTextReplacementEnabled];
59     return [defaults boolForKey:WebAutomaticTextReplacementEnabled];
60 }
61
62 static bool shouldAutomaticSpellingCorrectionBeEnabled()
63 {
64     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
65     if (![defaults objectForKey:WebAutomaticSpellingCorrectionEnabled])
66         return [NSSpellChecker isAutomaticTextReplacementEnabled];
67     return [defaults boolForKey:WebAutomaticSpellingCorrectionEnabled];
68 }
69
70 static bool shouldAutomaticQuoteSubstitutionBeEnabled()
71 {
72     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
73     if (![defaults objectForKey:WebAutomaticQuoteSubstitutionEnabled])
74         return [NSSpellChecker isAutomaticQuoteSubstitutionEnabled];
75
76     return [defaults boolForKey:WebAutomaticQuoteSubstitutionEnabled];
77 }
78
79 static bool shouldAutomaticDashSubstitutionBeEnabled()
80 {
81     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
82     if (![defaults objectForKey:WebAutomaticDashSubstitutionEnabled])
83         return [NSSpellChecker isAutomaticDashSubstitutionEnabled];
84
85     return [defaults boolForKey:WebAutomaticDashSubstitutionEnabled];
86 }
87
88 static TextCheckerState& mutableState()
89 {
90     static NeverDestroyed<TextCheckerState> state = makeNeverDestroyed([] {
91         TextCheckerState initialState;
92         initialState.isContinuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled] && TextChecker::isContinuousSpellCheckingAllowed();
93         initialState.isGrammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled];
94         initialState.isAutomaticTextReplacementEnabled = shouldAutomaticTextReplacementBeEnabled();
95         initialState.isAutomaticSpellingCorrectionEnabled = shouldAutomaticSpellingCorrectionBeEnabled();
96         initialState.isAutomaticQuoteSubstitutionEnabled = shouldAutomaticQuoteSubstitutionBeEnabled();
97         initialState.isAutomaticDashSubstitutionEnabled = shouldAutomaticDashSubstitutionBeEnabled();
98         initialState.isAutomaticLinkDetectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticLinkDetectionEnabled];
99         return initialState;
100     }());
101     return state;
102 }
103
104 const TextCheckerState& TextChecker::state()
105 {
106     return mutableState();
107 }
108     
109 static bool testingModeEnabled = false;
110
111 void TextChecker::setTestingMode(bool enabled)
112 {
113     if (enabled && !testingModeEnabled) {
114         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isContinuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
115         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isGrammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
116         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled];
117         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled];
118         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled];
119         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled];
120         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled];
121         [[NSUserDefaults standardUserDefaults] setBool:isSmartInsertDeleteEnabled() forKey:WebSmartInsertDeleteEnabled];
122     }
123     testingModeEnabled = enabled;
124 }
125
126 bool TextChecker::isTestingMode()
127 {
128     return testingModeEnabled;
129 }
130
131 bool TextChecker::isContinuousSpellCheckingAllowed()
132 {
133     static bool allowContinuousSpellChecking = true;
134     static bool readAllowContinuousSpellCheckingDefault = false;
135
136     if (!readAllowContinuousSpellCheckingDefault) {
137         if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"])
138             allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
139
140         readAllowContinuousSpellCheckingDefault = true;
141     }
142
143     return allowContinuousSpellChecking;
144 }
145
146 void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled)
147 {
148     if (state().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled)
149         return;
150                                                                                       
151     mutableState().isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled;
152     if (!testingModeEnabled)
153         [[NSUserDefaults standardUserDefaults] setBool:isContinuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
154
155     // FIXME: preflight the spell checker.
156 }
157
158 void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled)
159 {
160     if (state().isGrammarCheckingEnabled == isGrammarCheckingEnabled)
161         return;
162
163     mutableState().isGrammarCheckingEnabled = isGrammarCheckingEnabled;
164     if (!testingModeEnabled)
165         [[NSUserDefaults standardUserDefaults] setBool:isGrammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
166     [[NSSpellChecker sharedSpellChecker] updatePanels];
167     
168     // We call preflightSpellChecker() when turning continuous spell checking on, but we don't need to do that here
169     // because grammar checking only occurs on code paths that already preflight spell checking appropriately.
170 }
171
172 void TextChecker::setAutomaticSpellingCorrectionEnabled(bool isAutomaticSpellingCorrectionEnabled)
173 {
174     if (state().isAutomaticSpellingCorrectionEnabled == isAutomaticSpellingCorrectionEnabled)
175         return;
176
177     mutableState().isAutomaticSpellingCorrectionEnabled = isAutomaticSpellingCorrectionEnabled;
178     if (!testingModeEnabled)
179         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled];
180
181     [[NSSpellChecker sharedSpellChecker] updatePanels];
182 }
183
184 void TextChecker::setAutomaticQuoteSubstitutionEnabled(bool isAutomaticQuoteSubstitutionEnabled)
185 {
186     if (state().isAutomaticQuoteSubstitutionEnabled == isAutomaticQuoteSubstitutionEnabled)
187         return;
188
189     mutableState().isAutomaticQuoteSubstitutionEnabled = isAutomaticQuoteSubstitutionEnabled;
190     if (!testingModeEnabled)
191         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled];
192     
193     [[NSSpellChecker sharedSpellChecker] updatePanels];
194 }
195
196 void TextChecker::setAutomaticDashSubstitutionEnabled(bool isAutomaticDashSubstitutionEnabled)
197 {
198     if (state().isAutomaticDashSubstitutionEnabled == isAutomaticDashSubstitutionEnabled)
199         return;
200
201     mutableState().isAutomaticDashSubstitutionEnabled = isAutomaticDashSubstitutionEnabled;
202     if (!testingModeEnabled)
203         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled];
204
205     [[NSSpellChecker sharedSpellChecker] updatePanels];
206 }
207
208 void TextChecker::setAutomaticLinkDetectionEnabled(bool isAutomaticLinkDetectionEnabled)
209 {
210     if (state().isAutomaticLinkDetectionEnabled == isAutomaticLinkDetectionEnabled)
211         return;
212     
213     mutableState().isAutomaticLinkDetectionEnabled = isAutomaticLinkDetectionEnabled;
214     if (!testingModeEnabled)
215         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled];
216     
217     [[NSSpellChecker sharedSpellChecker] updatePanels];
218 }
219
220 void TextChecker::setAutomaticTextReplacementEnabled(bool isAutomaticTextReplacementEnabled)
221 {
222     if (state().isAutomaticTextReplacementEnabled == isAutomaticTextReplacementEnabled)
223         return;
224     
225     mutableState().isAutomaticTextReplacementEnabled = isAutomaticTextReplacementEnabled;
226     if (!testingModeEnabled)
227         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled];
228
229     [[NSSpellChecker sharedSpellChecker] updatePanels];
230 }
231
232 static bool smartInsertDeleteEnabled;
233     
234 bool TextChecker::isSmartInsertDeleteEnabled()
235 {
236     static bool readSmartInsertDeleteEnabledDefault;
237
238     if (!readSmartInsertDeleteEnabledDefault) {
239         smartInsertDeleteEnabled = ![[NSUserDefaults standardUserDefaults] objectForKey:WebSmartInsertDeleteEnabled] || [[NSUserDefaults standardUserDefaults] boolForKey:WebSmartInsertDeleteEnabled];
240
241         readSmartInsertDeleteEnabledDefault = true;
242     }
243
244     return smartInsertDeleteEnabled;
245 }
246
247 void TextChecker::setSmartInsertDeleteEnabled(bool flag)
248 {
249     if (flag == isSmartInsertDeleteEnabled())
250         return;
251
252     smartInsertDeleteEnabled = flag;
253
254     if (!testingModeEnabled)
255         [[NSUserDefaults standardUserDefaults] setBool:flag forKey:WebSmartInsertDeleteEnabled];
256 }
257
258 void TextChecker::didChangeAutomaticTextReplacementEnabled()
259 {
260     mutableState().isAutomaticTextReplacementEnabled = shouldAutomaticTextReplacementBeEnabled();
261     [[NSSpellChecker sharedSpellChecker] updatePanels];
262 }
263
264 void TextChecker::didChangeAutomaticSpellingCorrectionEnabled()
265 {
266     mutableState().isAutomaticSpellingCorrectionEnabled = shouldAutomaticSpellingCorrectionBeEnabled();
267     [[NSSpellChecker sharedSpellChecker] updatePanels];
268 }
269
270 void TextChecker::didChangeAutomaticQuoteSubstitutionEnabled()
271 {
272     mutableState().isAutomaticQuoteSubstitutionEnabled = shouldAutomaticQuoteSubstitutionBeEnabled();
273     [[NSSpellChecker sharedSpellChecker] updatePanels];
274 }
275
276 void TextChecker::didChangeAutomaticDashSubstitutionEnabled()
277 {
278     mutableState().isAutomaticDashSubstitutionEnabled = shouldAutomaticDashSubstitutionBeEnabled();
279     [[NSSpellChecker sharedSpellChecker] updatePanels];
280 }
281
282 bool TextChecker::substitutionsPanelIsShowing()
283 {
284     return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
285 }
286
287 void TextChecker::toggleSubstitutionsPanelIsShowing()
288 {
289     NSPanel *substitutionsPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
290     if ([substitutionsPanel isVisible]) {
291         [substitutionsPanel orderOut:nil];
292         return;
293     }
294     [substitutionsPanel orderFront:nil];
295 }
296
297 void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled)
298 {
299     mutableState().isContinuousSpellCheckingEnabled = enabled;
300 }
301
302 void TextChecker::grammarCheckingEnabledStateChanged(bool enabled)
303 {
304     mutableState().isGrammarCheckingEnabled = enabled;
305 }
306
307 SpellDocumentTag TextChecker::uniqueSpellDocumentTag(WebPageProxy*)
308 {
309     return [NSSpellChecker uniqueSpellDocumentTag];
310 }
311
312 void TextChecker::closeSpellDocumentWithTag(SpellDocumentTag tag)
313 {
314     [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:tag];
315 }
316
317 #if USE(UNIFIED_TEXT_CHECKING)
318
319 Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(SpellDocumentTag spellDocumentTag, StringView text, int32_t insertionPoint, OptionSet<TextCheckingType> checkingTypes, bool initialCapitalizationEnabled)
320 {
321     Vector<TextCheckingResult> results;
322
323     RetainPtr<NSString> textString = text.createNSStringWithoutCopying();
324     NSDictionary *options = nil;
325 #if HAVE(ADVANCED_SPELL_CHECKING)
326     options = @{
327         NSTextCheckingInsertionPointKey : @(insertionPoint),
328         NSTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled)
329     };
330 #else
331     options = @{ NSTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
332 #endif
333     NSArray *incomingResults = [[NSSpellChecker sharedSpellChecker] checkString:textString.get()
334                                                                           range:NSMakeRange(0, text.length())
335                                                                           types:nsTextCheckingTypes(checkingTypes) | NSTextCheckingTypeOrthography
336                                                                         options:options
337                                                          inSpellDocumentWithTag:spellDocumentTag 
338                                                                     orthography:NULL
339                                                                       wordCount:NULL];
340     for (NSTextCheckingResult *incomingResult in incomingResults) {
341         NSRange resultRange = [incomingResult range];
342         NSTextCheckingType resultType = [incomingResult resultType];
343         ASSERT(resultRange.location != NSNotFound);
344         ASSERT(resultRange.length > 0);
345         if (resultType == NSTextCheckingTypeSpelling && checkingTypes.contains(TextCheckingType::Spelling)) {
346             TextCheckingResult result;
347             result.type = TextCheckingType::Spelling;
348             result.location = resultRange.location;
349             result.length = resultRange.length;
350             results.append(result);
351         } else if (resultType == NSTextCheckingTypeGrammar && checkingTypes.contains(TextCheckingType::Grammar)) {
352             TextCheckingResult result;
353             NSArray *details = [incomingResult grammarDetails];
354             result.type = TextCheckingType::Grammar;
355             result.location = resultRange.location;
356             result.length = resultRange.length;
357             for (NSDictionary *incomingDetail in details) {
358                 ASSERT(incomingDetail);
359                 GrammarDetail detail;
360                 NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
361                 ASSERT(detailRangeAsNSValue);
362                 NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
363                 ASSERT(detailNSRange.location != NSNotFound);
364                 ASSERT(detailNSRange.length > 0);
365                 detail.location = detailNSRange.location;
366                 detail.length = detailNSRange.length;
367                 detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
368                 NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
369                 for (NSString *guess in guesses)
370                     detail.guesses.append(String(guess));
371                 result.details.append(detail);
372             }
373             results.append(result);
374         } else if (resultType == NSTextCheckingTypeLink && checkingTypes.contains(TextCheckingType::Link)) {
375             TextCheckingResult result;
376             result.type = TextCheckingType::Link;
377             result.location = resultRange.location;
378             result.length = resultRange.length;
379             result.replacement = [[incomingResult URL] absoluteString];
380             results.append(result);
381         } else if (resultType == NSTextCheckingTypeQuote && checkingTypes.contains(TextCheckingType::Quote)) {
382             TextCheckingResult result;
383             result.type = TextCheckingType::Quote;
384             result.location = resultRange.location;
385             result.length = resultRange.length;
386             result.replacement = [incomingResult replacementString];
387             results.append(result);
388         } else if (resultType == NSTextCheckingTypeDash && checkingTypes.contains(TextCheckingType::Dash)) {
389             TextCheckingResult result;
390             result.type = TextCheckingType::Dash;
391             result.location = resultRange.location;
392             result.length = resultRange.length;
393             result.replacement = [incomingResult replacementString];
394             results.append(result);
395         } else if (resultType == NSTextCheckingTypeReplacement && checkingTypes.contains(TextCheckingType::Replacement)) {
396             TextCheckingResult result;
397             result.type = TextCheckingType::Replacement;
398             result.location = resultRange.location;
399             result.length = resultRange.length;
400             result.replacement = [incomingResult replacementString];
401             results.append(result);
402         } else if (resultType == NSTextCheckingTypeCorrection && checkingTypes.contains(TextCheckingType::Correction)) {
403             TextCheckingResult result;
404             result.type = TextCheckingType::Correction;
405             result.location = resultRange.location;
406             result.length = resultRange.length;
407             result.replacement = [incomingResult replacementString];
408             results.append(result);
409         }
410     }
411
412     return results;
413 }
414
415 #endif // USE(UNIFIED_TEXT_CHECKING)
416
417 void TextChecker::checkSpellingOfString(SpellDocumentTag, StringView, int32_t&, int32_t&)
418 {
419     // Mac uses checkTextOfParagraph instead.
420     notImplemented();
421 }
422
423 void TextChecker::checkGrammarOfString(SpellDocumentTag, StringView, Vector<WebCore::GrammarDetail>&, int32_t&, int32_t&)
424 {
425     // Mac uses checkTextOfParagraph instead.
426     notImplemented();
427 }
428
429 bool TextChecker::spellingUIIsShowing()
430 {
431     return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
432 }
433
434 void TextChecker::toggleSpellingUIIsShowing()
435 {
436     NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
437     if ([spellingPanel isVisible])
438         [spellingPanel orderOut:nil];
439     else
440         [spellingPanel orderFront:nil];
441 }
442
443 void TextChecker::updateSpellingUIWithMisspelledWord(SpellDocumentTag, const String& misspelledWord)
444 {
445     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
446 }
447
448 void TextChecker::updateSpellingUIWithGrammarString(SpellDocumentTag, const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
449 {
450     RetainPtr<NSMutableArray> corrections = adoptNS([[NSMutableArray alloc] init]);
451     for (size_t i = 0; i < grammarDetail.guesses.size(); ++i) {
452         NSString *guess = grammarDetail.guesses[i];
453         [corrections addObject:guess];
454     }
455
456     NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
457     NSString *grammarUserDescription = grammarDetail.userDescription;
458     RetainPtr<NSDictionary> grammarDetailDict = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections.get(), NSGrammarCorrections, nil]);
459
460     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict.get()];
461 }
462
463 void TextChecker::getGuessesForWord(SpellDocumentTag spellDocumentTag, const String& word, const String& context, int32_t insertionPoint, Vector<String>& guesses, bool initialCapitalizationEnabled)
464 {
465     NSString* language = nil;
466     NSOrthography* orthography = nil;
467     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
468     NSDictionary *options = nil;
469 #if HAVE(ADVANCED_SPELL_CHECKING)
470     options = @{
471         NSTextCheckingInsertionPointKey : @(insertionPoint),
472         NSTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled)
473     };
474 #else
475     options = @{ NSTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
476 #endif
477     if (context.length()) {
478         [checker checkString:context range:NSMakeRange(0, context.length()) types:NSTextCheckingTypeOrthography options:options inSpellDocumentWithTag:spellDocumentTag orthography:&orthography wordCount:0];
479         language = [checker languageForWordRange:NSMakeRange(0, context.length()) inString:context orthography:orthography];
480     }
481     NSArray* stringsArray = [checker guessesForWordRange:NSMakeRange(0, word.length()) inString:word language:language inSpellDocumentWithTag:spellDocumentTag];
482
483     for (NSString *guess in stringsArray)
484         guesses.append(guess);
485 }
486
487 void TextChecker::learnWord(SpellDocumentTag, const String& word)
488 {
489     [[NSSpellChecker sharedSpellChecker] learnWord:word];
490 }
491
492 void TextChecker::ignoreWord(SpellDocumentTag spellDocumentTag, const String& word)
493 {
494     [[NSSpellChecker sharedSpellChecker] ignoreWord:word inSpellDocumentWithTag:spellDocumentTag];
495 }
496
497 void TextChecker::requestCheckingOfString(Ref<TextCheckerCompletion>&&, int32_t)
498 {
499     notImplemented();
500 }
501
502 } // namespace WebKit
503
504 #endif // PLATFORM(MAC)