OLD | NEW |
| (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 #include "chrome/renderer/spellchecker/spellcheck_language.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/logging.h" | |
10 #include "chrome/renderer/spellchecker/spellcheck_worditerator.h" | |
11 #include "chrome/renderer/spellchecker/spelling_engine.h" | |
12 | |
13 | |
14 SpellcheckLanguage::SpellcheckLanguage() | |
15 : platform_spelling_engine_(CreateNativeSpellingEngine()) { | |
16 } | |
17 | |
18 SpellcheckLanguage::~SpellcheckLanguage() { | |
19 } | |
20 | |
21 void SpellcheckLanguage::Init(base::File file, const std::string& language) { | |
22 DCHECK(platform_spelling_engine_.get()); | |
23 platform_spelling_engine_->Init(std::move(file)); | |
24 | |
25 character_attributes_.SetDefaultLanguage(language); | |
26 text_iterator_.Reset(); | |
27 contraction_iterator_.Reset(); | |
28 } | |
29 | |
30 bool SpellcheckLanguage::InitializeIfNeeded() { | |
31 DCHECK(platform_spelling_engine_.get()); | |
32 return platform_spelling_engine_->InitializeIfNeeded(); | |
33 } | |
34 | |
35 SpellcheckLanguage::SpellcheckWordResult SpellcheckLanguage::SpellCheckWord( | |
36 const base::char16* text_begin, | |
37 int position_in_text, | |
38 int text_length, | |
39 int tag, | |
40 int* skip_or_misspelling_start, | |
41 int* skip_or_misspelling_len, | |
42 std::vector<base::string16>* optional_suggestions) { | |
43 int remaining_text_len = text_length - position_in_text; | |
44 DCHECK(remaining_text_len >= 0); | |
45 DCHECK(skip_or_misspelling_start && skip_or_misspelling_len) | |
46 << "Out vars must be given."; | |
47 | |
48 // Do nothing if we need to delay initialization. (Rather than blocking, | |
49 // report the word as correctly spelled.) | |
50 if (InitializeIfNeeded()) | |
51 return IS_CORRECT; | |
52 | |
53 // Do nothing if spell checking is disabled. | |
54 if (!platform_spelling_engine_.get() || | |
55 !platform_spelling_engine_->IsEnabled()) | |
56 return IS_CORRECT; | |
57 | |
58 *skip_or_misspelling_start = 0; | |
59 *skip_or_misspelling_len = 0; | |
60 if (remaining_text_len == 0) | |
61 return IS_CORRECT; // No input means always spelled correctly. | |
62 | |
63 base::string16 word; | |
64 int word_start; | |
65 int word_length; | |
66 if (!text_iterator_.IsInitialized() && | |
67 !text_iterator_.Initialize(&character_attributes_, true)) { | |
68 // We failed to initialize text_iterator_, return as spelled correctly. | |
69 VLOG(1) << "Failed to initialize SpellcheckWordIterator"; | |
70 return IS_CORRECT; | |
71 } | |
72 | |
73 text_iterator_.SetText(text_begin + position_in_text, remaining_text_len); | |
74 DCHECK(platform_spelling_engine_.get()); | |
75 for (SpellcheckWordIterator::WordIteratorStatus status = | |
76 text_iterator_.GetNextWord(&word, &word_start, &word_length); | |
77 status != SpellcheckWordIterator::IS_END_OF_TEXT; | |
78 status = text_iterator_.GetNextWord(&word, &word_start, &word_length)) { | |
79 // Found a character that is not able to be spellchecked so determine how | |
80 // long the sequence of uncheckable characters is and then return. | |
81 if (status == SpellcheckWordIterator::IS_SKIPPABLE) { | |
82 *skip_or_misspelling_start = position_in_text + word_start; | |
83 while (status == SpellcheckWordIterator::IS_SKIPPABLE) { | |
84 *skip_or_misspelling_len += word_length; | |
85 status = text_iterator_.GetNextWord(&word, &word_start, &word_length); | |
86 } | |
87 return IS_SKIPPABLE; | |
88 } | |
89 | |
90 // Found a word (or a contraction) that the spellchecker can check the | |
91 // spelling of. | |
92 if (platform_spelling_engine_->CheckSpelling(word, tag)) | |
93 continue; | |
94 | |
95 // If the given word is a concatenated word of two or more valid words | |
96 // (e.g. "hello:hello"), we should treat it as a valid word. | |
97 if (IsValidContraction(word, tag)) | |
98 continue; | |
99 | |
100 *skip_or_misspelling_start = position_in_text + word_start; | |
101 *skip_or_misspelling_len = word_length; | |
102 | |
103 // Get the list of suggested words. | |
104 if (optional_suggestions) { | |
105 platform_spelling_engine_->FillSuggestionList(word, | |
106 optional_suggestions); | |
107 } | |
108 return IS_MISSPELLED; | |
109 } | |
110 | |
111 return IS_CORRECT; | |
112 } | |
113 | |
114 // Returns whether or not the given string is a valid contraction. | |
115 // This function is a fall-back when the SpellcheckWordIterator class | |
116 // returns a concatenated word which is not in the selected dictionary | |
117 // (e.g. "in'n'out") but each word is valid. | |
118 bool SpellcheckLanguage::IsValidContraction(const base::string16& contraction, | |
119 int tag) { | |
120 if (!contraction_iterator_.IsInitialized() && | |
121 !contraction_iterator_.Initialize(&character_attributes_, false)) { | |
122 // We failed to initialize the word iterator, return as spelled correctly. | |
123 VLOG(1) << "Failed to initialize contraction_iterator_"; | |
124 return true; | |
125 } | |
126 | |
127 contraction_iterator_.SetText(contraction.c_str(), contraction.length()); | |
128 | |
129 base::string16 word; | |
130 int word_start; | |
131 int word_length; | |
132 | |
133 DCHECK(platform_spelling_engine_.get()); | |
134 for (SpellcheckWordIterator::WordIteratorStatus status = | |
135 contraction_iterator_.GetNextWord(&word, &word_start, &word_length); | |
136 status != SpellcheckWordIterator::IS_END_OF_TEXT; | |
137 status = contraction_iterator_.GetNextWord(&word, &word_start, | |
138 &word_length)) { | |
139 if (status == SpellcheckWordIterator::IS_SKIPPABLE) | |
140 continue; | |
141 | |
142 if (!platform_spelling_engine_->CheckSpelling(word, tag)) | |
143 return false; | |
144 } | |
145 return true; | |
146 } | |
147 | |
148 bool SpellcheckLanguage::IsEnabled() { | |
149 DCHECK(platform_spelling_engine_.get()); | |
150 return platform_spelling_engine_->IsEnabled(); | |
151 } | |
OLD | NEW |