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