| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/renderer/spellchecker/spellcheck_provider.h" | 5 #include "chrome/renderer/spellchecker/spellcheck_provider.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "chrome/common/chrome_switches.h" | 8 #include "chrome/common/chrome_switches.h" |
| 9 #include "chrome/common/spellcheck_messages.h" | 9 #include "chrome/common/spellcheck_messages.h" |
| 10 #include "chrome/common/spellcheck_result.h" | 10 #include "chrome/common/spellcheck_result.h" |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 96 // Text check (unified request for grammar and spell check) is only | 96 // Text check (unified request for grammar and spell check) is only |
| 97 // available for browser process, so we ask the system spellchecker | 97 // available for browser process, so we ask the system spellchecker |
| 98 // over IPC or return an empty result if the checker is not | 98 // over IPC or return an empty result if the checker is not |
| 99 // available. | 99 // available. |
| 100 Send(new SpellCheckHostMsg_RequestTextCheck( | 100 Send(new SpellCheckHostMsg_RequestTextCheck( |
| 101 routing_id(), | 101 routing_id(), |
| 102 text_check_completions_.Add(completion), | 102 text_check_completions_.Add(completion), |
| 103 document_tag, | 103 document_tag, |
| 104 text)); | 104 text)); |
| 105 #else | 105 #else |
| 106 // Send this text to a browser. A browser checks the user profile and send | 106 if (text.isEmpty() || !HasWordCharacters(text, 0)) { |
| 107 // this text to the Spelling service only if a user enables this feature. | |
| 108 // TODO(hbono) Implement a cache to avoid sending IPC messages. | |
| 109 string16 line; | |
| 110 int offset = -1; | |
| 111 if (!GetRequestLine(text, &line, &offset)) { | |
| 112 completion->didCancelCheckingText(); | 107 completion->didCancelCheckingText(); |
| 113 return; | 108 return; |
| 114 } | 109 } |
| 115 | 110 // Cancel this spellcheck request if the cached text is a substring of the |
| 116 last_line_ = line; | 111 // given text and the given text is the middle of a possible word. |
| 112 // TODO(hbono): Move this cache code to a new function and add its unit test. |
| 113 string16 request(text); |
| 114 size_t text_length = request.length(); |
| 115 size_t last_length = last_request_.length(); |
| 116 if (text_length >= last_length && |
| 117 !request.compare(0, last_length, last_request_)) { |
| 118 if (text_length == last_length || !HasWordCharacters(text, last_length)) { |
| 119 completion->didCancelCheckingText(); |
| 120 return; |
| 121 } |
| 122 int code = 0; |
| 123 int length = static_cast<int>(text_length); |
| 124 U16_PREV(text.data(), 0, length, code); |
| 125 UErrorCode error = U_ZERO_ERROR; |
| 126 if (uscript_getScript(code, &error) != USCRIPT_COMMON) { |
| 127 completion->didCancelCheckingText(); |
| 128 return; |
| 129 } |
| 130 } |
| 131 // Create a subset of the cached results and return it if the given text is a |
| 132 // substring of the cached text. |
| 133 if (text_length < last_length && |
| 134 !last_request_.compare(0, text_length, request)) { |
| 135 size_t result_size = 0; |
| 136 for (size_t i = 0; i < last_results_.size(); ++i) { |
| 137 size_t start = last_results_[i].location; |
| 138 size_t end = start + last_results_[i].length; |
| 139 if (start <= text_length && end <= text_length) |
| 140 ++result_size; |
| 141 } |
| 142 if (result_size > 0) { |
| 143 WebKit::WebVector<WebKit::WebTextCheckingResult> results(result_size); |
| 144 for (size_t i = 0; i < result_size; ++i) { |
| 145 results[i].type = last_results_[i].type; |
| 146 results[i].location = last_results_[i].location; |
| 147 results[i].length = last_results_[i].length; |
| 148 results[i].replacement = last_results_[i].replacement; |
| 149 } |
| 150 completion->didFinishCheckingText(results); |
| 151 return; |
| 152 } |
| 153 } |
| 154 // Send this text to a browser. A browser checks the user profile and send |
| 155 // this text to the Spelling service only if a user enables this feature. |
| 156 last_request_.clear(); |
| 157 last_results_.assign(WebKit::WebVector<WebKit::WebTextCheckingResult>()); |
| 117 Send(new SpellCheckHostMsg_CallSpellingService( | 158 Send(new SpellCheckHostMsg_CallSpellingService( |
| 118 routing_id(), | 159 routing_id(), |
| 119 text_check_completions_.Add(completion), | 160 text_check_completions_.Add(completion), |
| 120 offset, | 161 0, |
| 121 line)); | 162 request)); |
| 122 #endif // !OS_MACOSX | 163 #endif // !OS_MACOSX |
| 123 } | 164 } |
| 124 | 165 |
| 125 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) { | 166 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) { |
| 126 bool handled = true; | 167 bool handled = true; |
| 127 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message) | 168 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message) |
| 128 #if !defined(OS_MACOSX) | 169 #if !defined(OS_MACOSX) |
| 129 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService, | 170 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService, |
| 130 OnRespondSpellingService) | 171 OnRespondSpellingService) |
| 131 #endif | 172 #endif |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 // Double-check the returned spellchecking results with our spellchecker to | 312 // Double-check the returned spellchecking results with our spellchecker to |
| 272 // visualize the differences between ours and the on-line spellchecker. | 313 // visualize the differences between ours and the on-line spellchecker. |
| 273 WebKit::WebVector<WebKit::WebTextCheckingResult> textcheck_results; | 314 WebKit::WebVector<WebKit::WebTextCheckingResult> textcheck_results; |
| 274 if (chrome_content_renderer_client_) { | 315 if (chrome_content_renderer_client_) { |
| 275 chrome_content_renderer_client_->spellcheck()->CreateTextCheckingResults( | 316 chrome_content_renderer_client_->spellcheck()->CreateTextCheckingResults( |
| 276 offset, line, results, &textcheck_results); | 317 offset, line, results, &textcheck_results); |
| 277 } else { | 318 } else { |
| 278 CreateTextCheckingResults(offset, results, &textcheck_results); | 319 CreateTextCheckingResults(offset, results, &textcheck_results); |
| 279 } | 320 } |
| 280 completion->didFinishCheckingText(textcheck_results); | 321 completion->didFinishCheckingText(textcheck_results); |
| 322 |
| 323 // Cache the request and the converted results. |
| 324 last_request_ = line; |
| 325 last_results_.swap(textcheck_results); |
| 281 } | 326 } |
| 282 | 327 |
| 283 bool SpellCheckProvider::HasWordCharacters(const string16& text, | 328 bool SpellCheckProvider::HasWordCharacters( |
| 284 int index) const { | 329 const WebKit::WebString& text, |
| 285 const char16* data = text.c_str(); | 330 int index) const { |
| 331 const char16* data = text.data(); |
| 286 int length = text.length(); | 332 int length = text.length(); |
| 287 while (index < length) { | 333 while (index < length) { |
| 288 uint32 code = 0; | 334 uint32 code = 0; |
| 289 U16_NEXT(data, index, length, code); | 335 U16_NEXT(data, index, length, code); |
| 290 UErrorCode err = U_ZERO_ERROR; | 336 UErrorCode error = U_ZERO_ERROR; |
| 291 if (uscript_getScript(code, &err) == USCRIPT_LATIN) | 337 if (uscript_getScript(code, &error) == USCRIPT_LATIN) |
| 292 return true; | 338 return true; |
| 293 } | 339 } |
| 294 return false; | 340 return false; |
| 295 } | 341 } |
| 296 | |
| 297 bool SpellCheckProvider::GetRequestLine(const string16& text, | |
| 298 string16* line, | |
| 299 int* offset) const { | |
| 300 // WebKit sends the line being edited by a user to this class. (It also sends | |
| 301 // the previous line when the user is typing its first word.) We send the line | |
| 302 // being edited by a user when the input text satisfies all conditions listed | |
| 303 // below. | |
| 304 // * There is a non-word character at the end of of the input line so this | |
| 305 // class can send a request only when a user finishes typing a word. | |
| 306 // * There are word characters in the input line. | |
| 307 // * There are word characters in the difference between the input line and | |
| 308 // the previously-spellchecked line. | |
| 309 if (text.empty()) | |
| 310 return false; | |
| 311 UErrorCode err = U_ZERO_ERROR; | |
| 312 if (uscript_getScript(*(text.rbegin()), &err) != USCRIPT_COMMON) | |
| 313 return false; | |
| 314 size_t input_offset = text.find('\n'); | |
| 315 string16 input_line; | |
| 316 if (input_offset != string16::npos && HasWordCharacters(text, input_offset)) { | |
| 317 ++input_offset; | |
| 318 *offset = static_cast<int>(input_offset); | |
| 319 input_line = text.substr(input_offset); | |
| 320 } else { | |
| 321 if (!HasWordCharacters(text, 0)) | |
| 322 return false; | |
| 323 *offset = 0; | |
| 324 input_line = text; | |
| 325 } | |
| 326 size_t length = last_line_.length(); | |
| 327 if (length > 0 && !input_line.compare(0, length, last_line_)) { | |
| 328 if (!HasWordCharacters(input_line, static_cast<int>(length))) | |
| 329 return false; | |
| 330 } | |
| 331 line->assign(input_line); | |
| 332 return true; | |
| 333 } | |
| 334 #endif | 342 #endif |
| 335 | 343 |
| 336 #if defined(OS_MACOSX) | 344 #if defined(OS_MACOSX) |
| 337 void SpellCheckProvider::OnAdvanceToNextMisspelling() { | 345 void SpellCheckProvider::OnAdvanceToNextMisspelling() { |
| 338 if (!render_view()->GetWebView()) | 346 if (!render_view()->GetWebView()) |
| 339 return; | 347 return; |
| 340 render_view()->GetWebView()->focusedFrame()->executeCommand( | 348 render_view()->GetWebView()->focusedFrame()->executeCommand( |
| 341 WebString::fromUTF8("AdvanceToNextMisspelling")); | 349 WebString::fromUTF8("AdvanceToNextMisspelling")); |
| 342 } | 350 } |
| 343 | 351 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 // TODO(darin): There's actually no reason for this to be here. We should | 387 // TODO(darin): There's actually no reason for this to be here. We should |
| 380 // have the browser side manage the document tag. | 388 // have the browser side manage the document tag. |
| 381 #if defined(OS_MACOSX) | 389 #if defined(OS_MACOSX) |
| 382 if (!has_document_tag_) { | 390 if (!has_document_tag_) { |
| 383 // Make the call to get the tag. | 391 // Make the call to get the tag. |
| 384 Send(new SpellCheckHostMsg_GetDocumentTag(routing_id(), &document_tag_)); | 392 Send(new SpellCheckHostMsg_GetDocumentTag(routing_id(), &document_tag_)); |
| 385 has_document_tag_ = true; | 393 has_document_tag_ = true; |
| 386 } | 394 } |
| 387 #endif | 395 #endif |
| 388 } | 396 } |
| OLD | NEW |