c4e9c25f9dc14fe77a4df2f71612a9a92c7cdbc7
[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 // FIXME: this needs to be removed and replaced with NSTextCheckingSuppressInitialCapitalizationKey as soon as
52 // rdar://problem/26800924 is fixed.
53
54 static NSString* const WebTextCheckingSuppressInitialCapitalizationKey = @"SuppressInitialCapitalization";
55
56 namespace WebKit {
57 using namespace WebCore;
58
59 static bool shouldAutomaticTextReplacementBeEnabled()
60 {
61     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
62     if (![defaults objectForKey:WebAutomaticTextReplacementEnabled])
63         return [NSSpellChecker isAutomaticTextReplacementEnabled];
64     return [defaults boolForKey:WebAutomaticTextReplacementEnabled];
65 }
66
67 static bool shouldAutomaticSpellingCorrectionBeEnabled()
68 {
69     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
70     if (![defaults objectForKey:WebAutomaticSpellingCorrectionEnabled])
71         return [NSSpellChecker isAutomaticTextReplacementEnabled];
72     return [defaults boolForKey:WebAutomaticSpellingCorrectionEnabled];
73 }
74
75 static bool shouldAutomaticQuoteSubstitutionBeEnabled()
76 {
77     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
78     if (![defaults objectForKey:WebAutomaticQuoteSubstitutionEnabled])
79         return [NSSpellChecker isAutomaticQuoteSubstitutionEnabled];
80
81     return [defaults boolForKey:WebAutomaticQuoteSubstitutionEnabled];
82 }
83
84 static bool shouldAutomaticDashSubstitutionBeEnabled()
85 {
86     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
87     if (![defaults objectForKey:WebAutomaticDashSubstitutionEnabled])
88         return [NSSpellChecker isAutomaticDashSubstitutionEnabled];
89
90     return [defaults boolForKey:WebAutomaticDashSubstitutionEnabled];
91 }
92
93 static TextCheckerState& mutableState()
94 {
95     static NeverDestroyed<TextCheckerState> state = makeNeverDestroyed([] {
96         TextCheckerState initialState;
97         initialState.isContinuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled] && TextChecker::isContinuousSpellCheckingAllowed();
98         initialState.isGrammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled];
99         initialState.isAutomaticTextReplacementEnabled = shouldAutomaticTextReplacementBeEnabled();
100         initialState.isAutomaticSpellingCorrectionEnabled = shouldAutomaticSpellingCorrectionBeEnabled();
101         initialState.isAutomaticQuoteSubstitutionEnabled = shouldAutomaticQuoteSubstitutionBeEnabled();
102         initialState.isAutomaticDashSubstitutionEnabled = shouldAutomaticDashSubstitutionBeEnabled();
103         initialState.isAutomaticLinkDetectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticLinkDetectionEnabled];
104         return initialState;
105     }());
106     return state;
107 }
108
109 const TextCheckerState& TextChecker::state()
110 {
111     return mutableState();
112 }
113     
114 static bool testingModeEnabled = false;
115
116 void TextChecker::setTestingMode(bool enabled)
117 {
118     if (enabled && !testingModeEnabled) {
119         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isContinuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
120         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isGrammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
121         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled];
122         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled];
123         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled];
124         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled];
125         [[NSUserDefaults standardUserDefaults] setBool:mutableState().isAutomaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled];
126         [[NSUserDefaults standardUserDefaults] setBool:isSmartInsertDeleteEnabled() forKey:WebSmartInsertDeleteEnabled];
127     }
128     testingModeEnabled = enabled;
129 }
130
131 bool TextChecker::isTestingMode()
132 {
133     return testingModeEnabled;
134 }
135
136 bool TextChecker::isContinuousSpellCheckingAllowed()
137 {
138     static bool allowContinuousSpellChecking = true;
139     static bool readAllowContinuousSpellCheckingDefault = false;
140
141     if (!readAllowContinuousSpellCheckingDefault) {
142         if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"])
143             allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
144
145         readAllowContinuousSpellCheckingDefault = true;
146     }
147
148     return allowContinuousSpellChecking;
149 }
150
151 void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled)
152 {
153     if (state().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled)
154         return;
155                                                                                       
156     mutableState().isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled;
157     if (!testingModeEnabled)
158         [[NSUserDefaults standardUserDefaults] setBool:isContinuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
159
160     // FIXME: preflight the spell checker.
161 }
162
163 void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled)
164 {
165     if (state().isGrammarCheckingEnabled == isGrammarCheckingEnabled)
166         return;
167
168     mutableState().isGrammarCheckingEnabled = isGrammarCheckingEnabled;
169     if (!testingModeEnabled)
170         [[NSUserDefaults standardUserDefaults] setBool:isGrammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
171     [[NSSpellChecker sharedSpellChecker] updatePanels];
172     
173     // We call preflightSpellChecker() when turning continuous spell checking on, but we don't need to do that here
174     // because grammar checking only occurs on code paths that already preflight spell checking appropriately.
175 }
176
177 void TextChecker::setAutomaticSpellingCorrectionEnabled(bool isAutomaticSpellingCorrectionEnabled)
178 {
179     if (state().isAutomaticSpellingCorrectionEnabled == isAutomaticSpellingCorrectionEnabled)
180         return;
181
182     mutableState().isAutomaticSpellingCorrectionEnabled = isAutomaticSpellingCorrectionEnabled;
183     if (!testingModeEnabled)
184         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled];
185
186     [[NSSpellChecker sharedSpellChecker] updatePanels];
187 }
188
189 void TextChecker::setAutomaticQuoteSubstitutionEnabled(bool isAutomaticQuoteSubstitutionEnabled)
190 {
191     if (state().isAutomaticQuoteSubstitutionEnabled == isAutomaticQuoteSubstitutionEnabled)
192         return;
193
194     mutableState().isAutomaticQuoteSubstitutionEnabled = isAutomaticQuoteSubstitutionEnabled;
195     if (!testingModeEnabled)
196         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled];
197     
198     [[NSSpellChecker sharedSpellChecker] updatePanels];
199 }
200
201 void TextChecker::setAutomaticDashSubstitutionEnabled(bool isAutomaticDashSubstitutionEnabled)
202 {
203     if (state().isAutomaticDashSubstitutionEnabled == isAutomaticDashSubstitutionEnabled)
204         return;
205
206     mutableState().isAutomaticDashSubstitutionEnabled = isAutomaticDashSubstitutionEnabled;
207     if (!testingModeEnabled)
208         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled];
209
210     [[NSSpellChecker sharedSpellChecker] updatePanels];
211 }
212
213 void TextChecker::setAutomaticLinkDetectionEnabled(bool isAutomaticLinkDetectionEnabled)
214 {
215     if (state().isAutomaticLinkDetectionEnabled == isAutomaticLinkDetectionEnabled)
216         return;
217     
218     mutableState().isAutomaticLinkDetectionEnabled = isAutomaticLinkDetectionEnabled;
219     if (!testingModeEnabled)
220         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled];
221     
222     [[NSSpellChecker sharedSpellChecker] updatePanels];
223 }
224
225 void TextChecker::setAutomaticTextReplacementEnabled(bool isAutomaticTextReplacementEnabled)
226 {
227     if (state().isAutomaticTextReplacementEnabled == isAutomaticTextReplacementEnabled)
228         return;
229     
230     mutableState().isAutomaticTextReplacementEnabled = isAutomaticTextReplacementEnabled;
231     if (!testingModeEnabled)
232         [[NSUserDefaults standardUserDefaults] setBool:isAutomaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled];
233
234     [[NSSpellChecker sharedSpellChecker] updatePanels];
235 }
236
237 static bool smartInsertDeleteEnabled;
238     
239 bool TextChecker::isSmartInsertDeleteEnabled()
240 {
241     static bool readSmartInsertDeleteEnabledDefault;
242
243     if (!readSmartInsertDeleteEnabledDefault) {
244         smartInsertDeleteEnabled = ![[NSUserDefaults standardUserDefaults] objectForKey:WebSmartInsertDeleteEnabled] || [[NSUserDefaults standardUserDefaults] boolForKey:WebSmartInsertDeleteEnabled];
245
246         readSmartInsertDeleteEnabledDefault = true;
247     }
248
249     return smartInsertDeleteEnabled;
250 }
251
252 void TextChecker::setSmartInsertDeleteEnabled(bool flag)
253 {
254     if (flag == isSmartInsertDeleteEnabled())
255         return;
256
257     smartInsertDeleteEnabled = flag;
258
259     if (!testingModeEnabled)
260         [[NSUserDefaults standardUserDefaults] setBool:flag forKey:WebSmartInsertDeleteEnabled];
261 }
262
263 void TextChecker::didChangeAutomaticTextReplacementEnabled()
264 {
265     mutableState().isAutomaticTextReplacementEnabled = shouldAutomaticTextReplacementBeEnabled();
266     [[NSSpellChecker sharedSpellChecker] updatePanels];
267 }
268
269 void TextChecker::didChangeAutomaticSpellingCorrectionEnabled()
270 {
271     mutableState().isAutomaticSpellingCorrectionEnabled = shouldAutomaticSpellingCorrectionBeEnabled();
272     [[NSSpellChecker sharedSpellChecker] updatePanels];
273 }
274
275 void TextChecker::didChangeAutomaticQuoteSubstitutionEnabled()
276 {
277     mutableState().isAutomaticQuoteSubstitutionEnabled = shouldAutomaticQuoteSubstitutionBeEnabled();
278     [[NSSpellChecker sharedSpellChecker] updatePanels];
279 }
280
281 void TextChecker::didChangeAutomaticDashSubstitutionEnabled()
282 {
283     mutableState().isAutomaticDashSubstitutionEnabled = shouldAutomaticDashSubstitutionBeEnabled();
284     [[NSSpellChecker sharedSpellChecker] updatePanels];
285 }
286
287 bool TextChecker::substitutionsPanelIsShowing()
288 {
289     return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
290 }
291
292 void TextChecker::toggleSubstitutionsPanelIsShowing()
293 {
294     NSPanel *substitutionsPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
295     if ([substitutionsPanel isVisible]) {
296         [substitutionsPanel orderOut:nil];
297         return;
298     }
299     [substitutionsPanel orderFront:nil];
300 }
301
302 void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled)
303 {
304     mutableState().isContinuousSpellCheckingEnabled = enabled;
305 }
306
307 void TextChecker::grammarCheckingEnabledStateChanged(bool enabled)
308 {
309     mutableState().isGrammarCheckingEnabled = enabled;
310 }
311
312 SpellDocumentTag TextChecker::uniqueSpellDocumentTag(WebPageProxy*)
313 {
314     return [NSSpellChecker uniqueSpellDocumentTag];
315 }
316
317 void TextChecker::closeSpellDocumentWithTag(SpellDocumentTag tag)
318 {
319     [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:tag];
320 }
321
322 #if USE(UNIFIED_TEXT_CHECKING)
323
324 Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(SpellDocumentTag spellDocumentTag, StringView text, int32_t insertionPoint, OptionSet<TextCheckingType> checkingTypes, bool initialCapitalizationEnabled)
325 {
326     Vector<TextCheckingResult> results;
327
328     RetainPtr<NSString> textString = text.createNSStringWithoutCopying();
329     NSDictionary *options = nil;
330 #if HAVE(ADVANCED_SPELL_CHECKING)
331     options = @{ NSTextCheckingInsertionPointKey : @(insertionPoint),
332                  WebTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
333 #else
334     options = @{ WebTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
335 #endif
336     NSArray *incomingResults = [[NSSpellChecker sharedSpellChecker] checkString:textString.get()
337                                                                           range:NSMakeRange(0, text.length())
338                                                                           types:nsTextCheckingTypes(checkingTypes) | NSTextCheckingTypeOrthography
339                                                                         options:options
340                                                          inSpellDocumentWithTag:spellDocumentTag 
341                                                                     orthography:NULL
342                                                                       wordCount:NULL];
343     for (NSTextCheckingResult *incomingResult in incomingResults) {
344         NSRange resultRange = [incomingResult range];
345         NSTextCheckingType resultType = [incomingResult resultType];
346         ASSERT(resultRange.location != NSNotFound);
347         ASSERT(resultRange.length > 0);
348         if (resultType == NSTextCheckingTypeSpelling && checkingTypes.contains(TextCheckingType::Spelling)) {
349             TextCheckingResult result;
350             result.type = TextCheckingType::Spelling;
351             result.location = resultRange.location;
352             result.length = resultRange.length;
353             results.append(result);
354         } else if (resultType == NSTextCheckingTypeGrammar && checkingTypes.contains(TextCheckingType::Grammar)) {
355             TextCheckingResult result;
356             NSArray *details = [incomingResult grammarDetails];
357             result.type = TextCheckingType::Grammar;
358             result.location = resultRange.location;
359             result.length = resultRange.length;
360             for (NSDictionary *incomingDetail in details) {
361                 ASSERT(incomingDetail);
362                 GrammarDetail detail;
363                 NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
364                 ASSERT(detailRangeAsNSValue);
365                 NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
366                 ASSERT(detailNSRange.location != NSNotFound);
367                 ASSERT(detailNSRange.length > 0);
368                 detail.location = detailNSRange.location;
369                 detail.length = detailNSRange.length;
370                 detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
371                 NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
372                 for (NSString *guess in guesses)
373                     detail.guesses.append(String(guess));
374                 result.details.append(detail);
375             }
376             results.append(result);
377         } else if (resultType == NSTextCheckingTypeLink && checkingTypes.contains(TextCheckingType::Link)) {
378             TextCheckingResult result;
379             result.type = TextCheckingType::Link;
380             result.location = resultRange.location;
381             result.length = resultRange.length;
382             result.replacement = [[incomingResult URL] absoluteString];
383             results.append(result);
384         } else if (resultType == NSTextCheckingTypeQuote && checkingTypes.contains(TextCheckingType::Quote)) {
385             TextCheckingResult result;
386             result.type = TextCheckingType::Quote;
387             result.location = resultRange.location;
388             result.length = resultRange.length;
389             result.replacement = [incomingResult replacementString];
390             results.append(result);
391         } else if (resultType == NSTextCheckingTypeDash && checkingTypes.contains(TextCheckingType::Dash)) {
392             TextCheckingResult result;
393             result.type = TextCheckingType::Dash;
394             result.location = resultRange.location;
395             result.length = resultRange.length;
396             result.replacement = [incomingResult replacementString];
397             results.append(result);
398         } else if (resultType == NSTextCheckingTypeReplacement && checkingTypes.contains(TextCheckingType::Replacement)) {
399             TextCheckingResult result;
400             result.type = TextCheckingType::Replacement;
401             result.location = resultRange.location;
402             result.length = resultRange.length;
403             result.replacement = [incomingResult replacementString];
404             results.append(result);
405         } else if (resultType == NSTextCheckingTypeCorrection && checkingTypes.contains(TextCheckingType::Correction)) {
406             TextCheckingResult result;
407             result.type = TextCheckingType::Correction;
408             result.location = resultRange.location;
409             result.length = resultRange.length;
410             result.replacement = [incomingResult replacementString];
411             results.append(result);
412         }
413     }
414
415     return results;
416 }
417
418 #endif // USE(UNIFIED_TEXT_CHECKING)
419
420 void TextChecker::checkSpellingOfString(SpellDocumentTag, StringView, int32_t&, int32_t&)
421 {
422     // Mac uses checkTextOfParagraph instead.
423     notImplemented();
424 }
425
426 void TextChecker::checkGrammarOfString(SpellDocumentTag, StringView, Vector<WebCore::GrammarDetail>&, int32_t&, int32_t&)
427 {
428     // Mac uses checkTextOfParagraph instead.
429     notImplemented();
430 }
431
432 bool TextChecker::spellingUIIsShowing()
433 {
434     return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
435 }
436
437 void TextChecker::toggleSpellingUIIsShowing()
438 {
439     NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
440     if ([spellingPanel isVisible])
441         [spellingPanel orderOut:nil];
442     else
443         [spellingPanel orderFront:nil];
444 }
445
446 void TextChecker::updateSpellingUIWithMisspelledWord(SpellDocumentTag, const String& misspelledWord)
447 {
448     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
449 }
450
451 void TextChecker::updateSpellingUIWithGrammarString(SpellDocumentTag, const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
452 {
453     RetainPtr<NSMutableArray> corrections = adoptNS([[NSMutableArray alloc] init]);
454     for (size_t i = 0; i < grammarDetail.guesses.size(); ++i) {
455         NSString *guess = grammarDetail.guesses[i];
456         [corrections addObject:guess];
457     }
458
459     NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
460     NSString *grammarUserDescription = grammarDetail.userDescription;
461     RetainPtr<NSDictionary> grammarDetailDict = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections.get(), NSGrammarCorrections, nil]);
462
463     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict.get()];
464 }
465
466 void TextChecker::getGuessesForWord(SpellDocumentTag spellDocumentTag, const String& word, const String& context, int32_t insertionPoint, Vector<String>& guesses, bool initialCapitalizationEnabled)
467 {
468     NSString* language = nil;
469     NSOrthography* orthography = nil;
470     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
471     NSDictionary *options = nil;
472 #if HAVE(ADVANCED_SPELL_CHECKING)
473     options = @{ NSTextCheckingInsertionPointKey : @(insertionPoint),
474                  WebTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
475 #else
476     options = @{ WebTextCheckingSuppressInitialCapitalizationKey : @(!initialCapitalizationEnabled) };
477 #endif
478     if (context.length()) {
479         [checker checkString:context range:NSMakeRange(0, context.length()) types:NSTextCheckingTypeOrthography options:options inSpellDocumentWithTag:spellDocumentTag orthography:&orthography wordCount:0];
480         language = [checker languageForWordRange:NSMakeRange(0, context.length()) inString:context orthography:orthography];
481     }
482     NSArray* stringsArray = [checker guessesForWordRange:NSMakeRange(0, word.length()) inString:word language:language inSpellDocumentWithTag:spellDocumentTag];
483
484     for (NSString *guess in stringsArray)
485         guesses.append(guess);
486 }
487
488 void TextChecker::learnWord(SpellDocumentTag, const String& word)
489 {
490     [[NSSpellChecker sharedSpellChecker] learnWord:word];
491 }
492
493 void TextChecker::ignoreWord(SpellDocumentTag spellDocumentTag, const String& word)
494 {
495     [[NSSpellChecker sharedSpellChecker] ignoreWord:word inSpellDocumentWithTag:spellDocumentTag];
496 }
497
498 void TextChecker::requestCheckingOfString(Ref<TextCheckerCompletion>&&, int32_t)
499 {
500     notImplemented();
501 }
502
503 } // namespace WebKit
504
505 #endif // PLATFORM(MAC)