| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | // Copyright (c) 2014 GitHub, Inc.
 | 
					
						
							|  |  |  | // Use of this source code is governed by the MIT license that can be
 | 
					
						
							|  |  |  | // found in the LICENSE file.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "atom/renderer/api/atom_api_spell_check_client.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "atom/common/native_mate_converters/string16_converter.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  | #include "base/logging.h"
 | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  | #include "chrome/renderer/spellchecker/spellcheck_worditerator.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | #include "native_mate/converter.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  | #include "native_mate/dictionary.h"
 | 
					
						
							|  |  |  | #include "third_party/icu/source/common/unicode/uscript.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  | #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | namespace atom { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace api { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool HasWordCharacters(const base::string16& text, int index) { | 
					
						
							|  |  |  |   const base::char16* data = text.data(); | 
					
						
							|  |  |  |   int length = text.length(); | 
					
						
							|  |  |  |   while (index < length) { | 
					
						
							| 
									
										
										
										
											2016-03-07 20:40:10 -08:00
										 |  |  |     uint32_t code = 0; | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  |     U16_NEXT(data, index, length, code); | 
					
						
							|  |  |  |     UErrorCode error = U_ZERO_ERROR; | 
					
						
							|  |  |  |     if (uscript_getScript(code, &error) != USCRIPT_COMMON) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 21:01:47 -08:00
										 |  |  | SpellCheckClient::SpellCheckClient(const std::string& language, | 
					
						
							|  |  |  |                                    bool auto_spell_correct_turned_on, | 
					
						
							|  |  |  |                                    v8::Isolate* isolate, | 
					
						
							| 
									
										
										
										
											2015-05-22 19:11:22 +08:00
										 |  |  |                                    v8::Local<v8::Object> provider) | 
					
						
							| 
									
										
										
										
											2016-03-08 23:28:53 +09:00
										 |  |  |     : isolate_(isolate), | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |       context_(isolate, isolate->GetCurrentContext()), | 
					
						
							| 
									
										
										
										
											2014-12-19 21:01:47 -08:00
										 |  |  |       provider_(isolate, provider) { | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |   DCHECK(!context_.IsEmpty()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  |   character_attributes_.SetDefaultLanguage(language); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Persistent the method.
 | 
					
						
							|  |  |  |   mate::Dictionary dict(isolate, provider); | 
					
						
							|  |  |  |   dict.Get("spellCheck", &spell_check_); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  | SpellCheckClient::~SpellCheckClient() { | 
					
						
							|  |  |  |   context_.Reset(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  | void SpellCheckClient::CheckSpelling( | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  |     const blink::WebString& text, | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  |     int& misspelling_start, | 
					
						
							|  |  |  |     int& misspelling_len, | 
					
						
							|  |  |  |     blink::WebVector<blink::WebString>* optional_suggestions) { | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |   std::vector<blink::WebTextCheckingResult> results; | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  |   SpellCheckText(text.Utf16(), true, &results); | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |   if (results.size() == 1) { | 
					
						
							|  |  |  |     misspelling_start = results[0].location; | 
					
						
							|  |  |  |     misspelling_len = results[0].length; | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  | void SpellCheckClient::RequestCheckingOfText( | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  |     const blink::WebString& textToCheck, | 
					
						
							|  |  |  |     blink::WebTextCheckingCompletion* completionCallback) { | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  |   base::string16 text(textToCheck.Utf16()); | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  |   if (text.empty() || !HasWordCharacters(text, 0)) { | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  |     completionCallback->DidCancelCheckingText(); | 
					
						
							| 
									
										
										
										
											2014-12-18 17:41:42 -08:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |   std::vector<blink::WebTextCheckingResult> results; | 
					
						
							|  |  |  |   SpellCheckText(text, false, &results); | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  |   completionCallback->DidFinishCheckingText(results); | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  | void SpellCheckClient::ShowSpellingUI(bool show) {} | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  | bool SpellCheckClient::IsShowingSpellingUI() { | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-16 23:42:33 +03:00
										 |  |  | void SpellCheckClient::UpdateSpellingUIWithMisspelledWord( | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  |     const blink::WebString& word) {} | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  | void SpellCheckClient::SpellCheckText( | 
					
						
							|  |  |  |     const base::string16& text, | 
					
						
							|  |  |  |     bool stop_at_first_result, | 
					
						
							|  |  |  |     std::vector<blink::WebTextCheckingResult>* results) { | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |   if (text.empty() || spell_check_.IsEmpty()) | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |   if (!text_iterator_.IsInitialized() && | 
					
						
							|  |  |  |       !text_iterator_.Initialize(&character_attributes_, true)) { | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  |     // We failed to initialize text_iterator_, return as spelled correctly.
 | 
					
						
							|  |  |  |     VLOG(1) << "Failed to initialize SpellcheckWordIterator"; | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |   if (!contraction_iterator_.IsInitialized() && | 
					
						
							|  |  |  |       !contraction_iterator_.Initialize(&character_attributes_, false)) { | 
					
						
							|  |  |  |     // We failed to initialize the word iterator, return as spelled correctly.
 | 
					
						
							|  |  |  |     VLOG(1) << "Failed to initialize contraction_iterator_"; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   text_iterator_.SetText(text.c_str(), text.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SpellCheckScope scope(*this); | 
					
						
							|  |  |  |   base::string16 word; | 
					
						
							|  |  |  |   int word_start; | 
					
						
							|  |  |  |   int word_length; | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  |   for (auto status = | 
					
						
							|  |  |  |            text_iterator_.GetNextWord(&word, &word_start, &word_length); | 
					
						
							|  |  |  |        status != SpellcheckWordIterator::IS_END_OF_TEXT; | 
					
						
							|  |  |  |        status = text_iterator_.GetNextWord(&word, &word_start, &word_length)) { | 
					
						
							|  |  |  |     if (status == SpellcheckWordIterator::IS_SKIPPABLE) | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |     // Found a word (or a contraction) that the spellchecker can check the
 | 
					
						
							|  |  |  |     // spelling of.
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |     if (SpellCheckWord(scope, word)) | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |       continue; | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |     // If the given word is a concatenated word of two or more valid words
 | 
					
						
							|  |  |  |     // (e.g. "hello:hello"), we should treat it as a valid word.
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |     if (IsValidContraction(scope, word)) | 
					
						
							| 
									
										
										
										
											2014-12-19 22:13:07 -08:00
										 |  |  |       continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     blink::WebTextCheckingResult result; | 
					
						
							|  |  |  |     result.location = word_start; | 
					
						
							|  |  |  |     result.length = word_length; | 
					
						
							|  |  |  |     results->push_back(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (stop_at_first_result) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-18 16:44:38 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  | bool SpellCheckClient::SpellCheckWord( | 
					
						
							|  |  |  |     const SpellCheckScope& scope, | 
					
						
							|  |  |  |     const base::string16& word_to_check) const { | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  |   DCHECK(!scope.spell_check_.IsEmpty()); | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-22 19:11:22 +08:00
										 |  |  |   v8::Local<v8::Value> word = mate::ConvertToV8(isolate_, word_to_check); | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |   v8::Local<v8::Value> result = | 
					
						
							|  |  |  |       scope.spell_check_->Call(scope.provider_, 1, &word); | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |   if (!result.IsEmpty() && result->IsBoolean()) | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  |     return result->BooleanValue(); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns whether or not the given string is a valid contraction.
 | 
					
						
							|  |  |  | // This function is a fall-back when the SpellcheckWordIterator class
 | 
					
						
							|  |  |  | // returns a concatenated word which is not in the selected dictionary
 | 
					
						
							|  |  |  | // (e.g. "in'n'out") but each word is valid.
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  | bool SpellCheckClient::IsValidContraction(const SpellCheckScope& scope, | 
					
						
							|  |  |  |                                           const base::string16& contraction) { | 
					
						
							|  |  |  |   DCHECK(contraction_iterator_.IsInitialized()); | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   contraction_iterator_.SetText(contraction.c_str(), contraction.length()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   base::string16 word; | 
					
						
							|  |  |  |   int word_start; | 
					
						
							|  |  |  |   int word_length; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-06 09:18:52 -08:00
										 |  |  |   for (auto status = | 
					
						
							|  |  |  |            contraction_iterator_.GetNextWord(&word, &word_start, &word_length); | 
					
						
							|  |  |  |        status != SpellcheckWordIterator::IS_END_OF_TEXT; | 
					
						
							|  |  |  |        status = contraction_iterator_.GetNextWord(&word, &word_start, | 
					
						
							|  |  |  |                                                   &word_length)) { | 
					
						
							|  |  |  |     if (status == SpellcheckWordIterator::IS_SKIPPABLE) | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  |     if (!SpellCheckWord(scope, word)) | 
					
						
							| 
									
										
										
										
											2014-12-19 20:42:19 -08:00
										 |  |  |       return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 12:55:28 +01:00
										 |  |  | SpellCheckClient::SpellCheckScope::SpellCheckScope( | 
					
						
							|  |  |  |     const SpellCheckClient& client) | 
					
						
							|  |  |  |     : handle_scope_(client.isolate_), | 
					
						
							|  |  |  |       context_scope_( | 
					
						
							|  |  |  |           v8::Local<v8::Context>::New(client.isolate_, client.context_)), | 
					
						
							|  |  |  |       provider_(client.provider_.NewHandle()), | 
					
						
							|  |  |  |       spell_check_(client.spell_check_.NewHandle()) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-18 13:43:51 -08:00
										 |  |  | }  // namespace api
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace atom
 |