Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: chrome/browser/spellchecker/spellcheck_platform_mac.mm

Issue 2244083002: Componentize spellcheck [4]: spellcheck/browser and android java-side. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Integration with OS X built-in spellchecker.
6
7 #include "chrome/browser/spellchecker/spellcheck_platform.h"
8
9 #import <Cocoa/Cocoa.h>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/logging.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/time/time.h"
17 #include "components/spellcheck/common/spellcheck_common.h"
18 #include "components/spellcheck/common/spellcheck_result.h"
19 #include "content/public/browser/browser_message_filter.h"
20 #include "content/public/browser/browser_thread.h"
21
22 using base::TimeTicks;
23 using content::BrowserMessageFilter;
24 using content::BrowserThread;
25
26 namespace {
27 // The number of characters in the first part of the language code.
28 const unsigned int kShortLanguageCodeSize = 2;
29
30 // +[NSSpellChecker sharedSpellChecker] can throw exceptions depending
31 // on the state of the pasteboard, or possibly as a result of
32 // third-party code (when setting up services entries). The following
33 // receives nil if an exception is thrown, in which case
34 // spell-checking will not work, but it also will not crash the
35 // browser.
36 NSSpellChecker* SharedSpellChecker() {
37 @try {
38 return [NSSpellChecker sharedSpellChecker];
39 } @catch (id exception) {
40 return nil;
41 }
42 }
43
44 // A private utility function to convert hunspell language codes to OS X
45 // language codes.
46 NSString* ConvertLanguageCodeToMac(const std::string& hunspell_lang_code) {
47 NSString* whole_code = base::SysUTF8ToNSString(hunspell_lang_code);
48
49 if ([whole_code length] > kShortLanguageCodeSize) {
50 NSString* lang_code = [whole_code
51 substringToIndex:kShortLanguageCodeSize];
52 // Add 1 here to skip the underscore.
53 NSString* region_code = [whole_code
54 substringFromIndex:(kShortLanguageCodeSize + 1)];
55
56 // Check for the special case of en-US and pt-PT, since OS X lists these
57 // as just en and pt respectively.
58 // TODO(pwicks): Find out if there are other special cases for languages
59 // not installed on the system by default. Are there others like pt-PT?
60 if (([lang_code isEqualToString:@"en"] &&
61 [region_code isEqualToString:@"US"]) ||
62 ([lang_code isEqualToString:@"pt"] &&
63 [region_code isEqualToString:@"PT"])) {
64 return lang_code;
65 }
66
67 // Otherwise, just build a string that uses an underscore instead of a
68 // dash between the language and the region code, since this is the
69 // format that OS X uses.
70 NSString* os_x_language =
71 [NSString stringWithFormat:@"%@_%@", lang_code, region_code];
72 return os_x_language;
73 } else {
74 // Special case for Polish.
75 if ([whole_code isEqualToString:@"pl"]) {
76 return @"pl_PL";
77 }
78 // This is just a language code with the same format as OS X
79 // language code.
80 return whole_code;
81 }
82 }
83
84 std::string ConvertLanguageCodeFromMac(NSString* lang_code) {
85 // TODO(pwicks):figure out what to do about Multilingual
86 // Guards for strange cases.
87 if ([lang_code isEqualToString:@"en"]) return std::string("en-US");
88 if ([lang_code isEqualToString:@"pt"]) return std::string("pt-PT");
89 if ([lang_code isEqualToString:@"pl_PL"]) return std::string("pl");
90
91 if ([lang_code length] > kShortLanguageCodeSize &&
92 [lang_code characterAtIndex:kShortLanguageCodeSize] == '_') {
93 return base::SysNSStringToUTF8([NSString stringWithFormat:@"%@-%@",
94 [lang_code substringToIndex:kShortLanguageCodeSize],
95 [lang_code substringFromIndex:(kShortLanguageCodeSize + 1)]]);
96 }
97 return base::SysNSStringToUTF8(lang_code);
98 }
99
100 } // namespace
101
102 namespace spellcheck_platform {
103
104 void GetAvailableLanguages(std::vector<std::string>* spellcheck_languages) {
105 NSArray* availableLanguages = [SharedSpellChecker() availableLanguages];
106 for (NSString* lang_code in availableLanguages) {
107 spellcheck_languages->push_back(
108 ConvertLanguageCodeFromMac(lang_code));
109 }
110 }
111
112 std::string GetSpellCheckerLanguage() {
113 return ConvertLanguageCodeFromMac([SharedSpellChecker() language]);
114 }
115
116 bool SpellCheckerAvailable() {
117 // If this file was compiled, then we know that we are on OS X 10.5 at least
118 // and can safely return true here.
119 return true;
120 }
121
122 bool SpellCheckerProvidesPanel() {
123 // OS X has a Spelling Panel, so we can return true here.
124 return true;
125 }
126
127 bool SpellingPanelVisible() {
128 // This should only be called from the main thread.
129 DCHECK([NSThread currentThread] == [NSThread mainThread]);
130 return [[SharedSpellChecker() spellingPanel] isVisible];
131 }
132
133 void ShowSpellingPanel(bool show) {
134 if (show) {
135 [[SharedSpellChecker() spellingPanel]
136 performSelectorOnMainThread:@selector(makeKeyAndOrderFront:)
137 withObject:nil
138 waitUntilDone:YES];
139 } else {
140 [[SharedSpellChecker() spellingPanel]
141 performSelectorOnMainThread:@selector(close)
142 withObject:nil
143 waitUntilDone:YES];
144 }
145 }
146
147 void UpdateSpellingPanelWithMisspelledWord(const base::string16& word) {
148 NSString * word_to_display = base::SysUTF16ToNSString(word);
149 [SharedSpellChecker()
150 performSelectorOnMainThread:
151 @selector(updateSpellingPanelWithMisspelledWord:)
152 withObject:word_to_display
153 waitUntilDone:YES];
154 }
155
156 bool PlatformSupportsLanguage(const std::string& current_language) {
157 // First, convert the language to an OS X language code.
158 NSString* mac_lang_code = ConvertLanguageCodeToMac(current_language);
159
160 // Then grab the languages available.
161 NSArray* availableLanguages = [SharedSpellChecker() availableLanguages];
162
163 // Return true if the given language is supported by OS X.
164 return [availableLanguages containsObject:mac_lang_code];
165 }
166
167 void SetLanguage(const std::string& lang_to_set) {
168 // Do not set any language right now, since Chrome should honor the
169 // system spellcheck settings. (http://crbug.com/166046)
170 // Fix this once Chrome actually allows setting a spellcheck language
171 // in chrome://settings.
172 // NSString* NS_lang_to_set = ConvertLanguageCodeToMac(lang_to_set);
173 // [SharedSpellChecker() setLanguage:NS_lang_to_set];
174 }
175
176 static int last_seen_tag_;
177
178 bool CheckSpelling(const base::string16& word_to_check, int tag) {
179 last_seen_tag_ = tag;
180
181 // -[NSSpellChecker checkSpellingOfString] returns an NSRange that
182 // we can look at to determine if a word is misspelled.
183 NSRange spell_range = {0,0};
184
185 // Convert the word to an NSString.
186 NSString* NS_word_to_check = base::SysUTF16ToNSString(word_to_check);
187 // Check the spelling, starting at the beginning of the word.
188 spell_range = [SharedSpellChecker()
189 checkSpellingOfString:NS_word_to_check startingAt:0
190 language:nil wrap:NO inSpellDocumentWithTag:tag
191 wordCount:NULL];
192
193 // If the length of the misspelled word == 0,
194 // then there is no misspelled word.
195 bool word_correct = (spell_range.length == 0);
196 return word_correct;
197 }
198
199 void FillSuggestionList(const base::string16& wrong_word,
200 std::vector<base::string16>* optional_suggestions) {
201 NSString* ns_wrong_word = base::SysUTF16ToNSString(wrong_word);
202 NSSpellChecker* checker = SharedSpellChecker();
203 NSString* language = [checker language];
204 NSArray* guesses =
205 [checker guessesForWordRange:NSMakeRange(0, [ns_wrong_word length])
206 inString:ns_wrong_word
207 language:language
208 inSpellDocumentWithTag:last_seen_tag_];
209 int i = 0;
210 for (NSString* guess in guesses) {
211 optional_suggestions->push_back(base::SysNSStringToUTF16(guess));
212 if (++i >= spellcheck::kMaxSuggestions)
213 break;
214 }
215 }
216
217 void AddWord(const base::string16& word) {
218 NSString* word_to_add = base::SysUTF16ToNSString(word);
219 [SharedSpellChecker() learnWord:word_to_add];
220 }
221
222 void RemoveWord(const base::string16& word) {
223 NSString *word_to_remove = base::SysUTF16ToNSString(word);
224 [SharedSpellChecker() unlearnWord:word_to_remove];
225 }
226
227 int GetDocumentTag() {
228 NSInteger doc_tag = [NSSpellChecker uniqueSpellDocumentTag];
229 return static_cast<int>(doc_tag);
230 }
231
232 void IgnoreWord(const base::string16& word) {
233 [SharedSpellChecker() ignoreWord:base::SysUTF16ToNSString(word)
234 inSpellDocumentWithTag:last_seen_tag_];
235 }
236
237 void CloseDocumentWithTag(int tag) {
238 [SharedSpellChecker() closeSpellDocumentWithTag:static_cast<NSInteger>(tag)];
239 }
240
241 void RequestTextCheck(int document_tag,
242 const base::string16& text,
243 TextCheckCompleteCallback callback) {
244 NSString* text_to_check = base::SysUTF16ToNSString(text);
245 NSRange range_to_check = NSMakeRange(0, [text_to_check length]);
246
247 [SharedSpellChecker()
248 requestCheckingOfString:text_to_check
249 range:range_to_check
250 types:NSTextCheckingTypeSpelling
251 options:nil
252 inSpellDocumentWithTag:document_tag
253 completionHandler:^(NSInteger,
254 NSArray *results,
255 NSOrthography*,
256 NSInteger) {
257 std::vector<SpellCheckResult> check_results;
258 for (NSTextCheckingResult* result in results) {
259 // Deliberately ignore non-spelling results. OSX at the very least
260 // delivers a result of NSTextCheckingTypeOrthography for the
261 // whole fragment, which underlines the entire checked range.
262 if ([result resultType] != NSTextCheckingTypeSpelling)
263 continue;
264
265 // In this use case, the spell checker should never
266 // return anything but a single range per result.
267 check_results.push_back(SpellCheckResult(
268 SpellCheckResult::SPELLING,
269 [result range].location,
270 [result range].length));
271 }
272 // TODO(groby): Verify we don't need to post from here.
273 callback.Run(check_results);
274 }];
275 }
276
277 class SpellcheckerStateInternal {
278 public:
279 SpellcheckerStateInternal();
280 ~SpellcheckerStateInternal();
281
282 private:
283 BOOL automaticallyIdentifiesLanguages_;
284 NSString* language_;
285 };
286
287 SpellcheckerStateInternal::SpellcheckerStateInternal() {
288 language_ = [SharedSpellChecker() language];
289 automaticallyIdentifiesLanguages_ =
290 [SharedSpellChecker() automaticallyIdentifiesLanguages];
291 [SharedSpellChecker() setLanguage:@"en"];
292 [SharedSpellChecker() setAutomaticallyIdentifiesLanguages:NO];
293 }
294
295 SpellcheckerStateInternal::~SpellcheckerStateInternal() {
296 [SharedSpellChecker() setLanguage:language_];
297 [SharedSpellChecker() setAutomaticallyIdentifiesLanguages:
298 automaticallyIdentifiesLanguages_];
299 }
300
301 ScopedEnglishLanguageForTest::ScopedEnglishLanguageForTest()
302 : state_(new SpellcheckerStateInternal) {
303 }
304
305 ScopedEnglishLanguageForTest::~ScopedEnglishLanguageForTest() {
306 delete state_;
307 }
308
309 } // namespace spellcheck_platform
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698