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