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 string16 request(text); | |
Hajime Morrita
2012/06/11 06:53:21
I hope this early-return-with-cache part to be a m
Hironori Bono
2012/06/11 07:24:14
Unfortunately, this code is a temporary workaround
| |
113 size_t text_length = request.length(); | |
114 size_t last_length = last_request_.length(); | |
115 if (text_length >= last_length && | |
116 !request.compare(0, last_length, last_request_)) { | |
117 if (text_length == last_length || !HasWordCharacters(text, last_length)) { | |
118 completion->didCancelCheckingText(); | |
Hajime Morrita
2012/06/11 06:53:21
This means that there should be no misspelling, ri
Hironori Bono
2012/06/11 07:24:14
This code means we should not change existing mark
| |
119 return; | |
120 } | |
121 int code = 0; | |
122 int length = static_cast<int>(text_length); | |
123 U16_PREV(text.data(), 0, length, code); | |
124 UErrorCode error = U_ZERO_ERROR; | |
125 if (uscript_getScript(code, &error) != USCRIPT_COMMON) { | |
126 completion->didCancelCheckingText(); | |
127 return; | |
128 } | |
129 } | |
130 // Create a subset of the cached results and return it if the given text is a | |
131 // substring of the cached text. | |
132 if (text_length < last_length && | |
133 !last_request_.compare(0, text_length, request)) { | |
134 size_t result_size = 0; | |
135 for (size_t i = 0; i < last_results_.size(); ++i) { | |
136 size_t start = last_results_[i].location; | |
137 size_t end = start + last_results_[i].length; | |
138 if (start <= text_length && end <= text_length) | |
139 ++result_size; | |
140 } | |
141 if (result_size > 0) { | |
142 WebKit::WebVector<WebKit::WebTextCheckingResult> results(result_size); | |
143 for (size_t i = 0; i < result_size; ++i) { | |
144 results[i].type = last_results_[i].type; | |
145 results[i].location = last_results_[i].location; | |
146 results[i].length = last_results_[i].length; | |
147 results[i].replacement = last_results_[i].replacement; | |
148 } | |
149 completion->didFinishCheckingText(results); | |
150 return; | |
151 } | |
152 } | |
153 // Send this text to a browser. A browser checks the user profile and send | |
154 // this text to the Spelling service only if a user enables this feature. | |
155 last_request_.clear(); | |
156 last_results_.assign(WebKit::WebVector<WebKit::WebTextCheckingResult>()); | |
117 Send(new SpellCheckHostMsg_CallSpellingService( | 157 Send(new SpellCheckHostMsg_CallSpellingService( |
118 routing_id(), | 158 routing_id(), |
119 text_check_completions_.Add(completion), | 159 text_check_completions_.Add(completion), |
120 offset, | 160 0, |
121 line)); | 161 request)); |
122 #endif // !OS_MACOSX | 162 #endif // !OS_MACOSX |
123 } | 163 } |
124 | 164 |
125 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) { | 165 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) { |
126 bool handled = true; | 166 bool handled = true; |
127 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message) | 167 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message) |
128 #if !defined(OS_MACOSX) | 168 #if !defined(OS_MACOSX) |
129 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService, | 169 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService, |
130 OnRespondSpellingService) | 170 OnRespondSpellingService) |
131 #endif | 171 #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 | 311 // Double-check the returned spellchecking results with our spellchecker to |
272 // visualize the differences between ours and the on-line spellchecker. | 312 // visualize the differences between ours and the on-line spellchecker. |
273 WebKit::WebVector<WebKit::WebTextCheckingResult> textcheck_results; | 313 WebKit::WebVector<WebKit::WebTextCheckingResult> textcheck_results; |
274 if (chrome_content_renderer_client_) { | 314 if (chrome_content_renderer_client_) { |
275 chrome_content_renderer_client_->spellcheck()->CreateTextCheckingResults( | 315 chrome_content_renderer_client_->spellcheck()->CreateTextCheckingResults( |
276 offset, line, results, &textcheck_results); | 316 offset, line, results, &textcheck_results); |
277 } else { | 317 } else { |
278 CreateTextCheckingResults(offset, results, &textcheck_results); | 318 CreateTextCheckingResults(offset, results, &textcheck_results); |
279 } | 319 } |
280 completion->didFinishCheckingText(textcheck_results); | 320 completion->didFinishCheckingText(textcheck_results); |
321 | |
322 // Cache the request and the converted results. | |
323 last_request_ = line; | |
324 last_results_.swap(textcheck_results); | |
281 } | 325 } |
282 | 326 |
283 bool SpellCheckProvider::HasWordCharacters(const string16& text, | 327 bool SpellCheckProvider::HasWordCharacters( |
284 int index) const { | 328 const WebKit::WebString& text, |
285 const char16* data = text.c_str(); | 329 int index) const { |
330 const char16* data = text.data(); | |
286 int length = text.length(); | 331 int length = text.length(); |
287 while (index < length) { | 332 while (index < length) { |
288 uint32 code = 0; | 333 uint32 code = 0; |
289 U16_NEXT(data, index, length, code); | 334 U16_NEXT(data, index, length, code); |
290 UErrorCode err = U_ZERO_ERROR; | 335 UErrorCode error = U_ZERO_ERROR; |
291 if (uscript_getScript(code, &err) == USCRIPT_LATIN) | 336 if (uscript_getScript(code, &error) == USCRIPT_LATIN) |
292 return true; | 337 return true; |
293 } | 338 } |
294 return false; | 339 return false; |
295 } | 340 } |
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 | 341 #endif |
335 | 342 |
336 #if defined(OS_MACOSX) | 343 #if defined(OS_MACOSX) |
337 void SpellCheckProvider::OnAdvanceToNextMisspelling() { | 344 void SpellCheckProvider::OnAdvanceToNextMisspelling() { |
338 if (!render_view()->GetWebView()) | 345 if (!render_view()->GetWebView()) |
339 return; | 346 return; |
340 render_view()->GetWebView()->focusedFrame()->executeCommand( | 347 render_view()->GetWebView()->focusedFrame()->executeCommand( |
341 WebString::fromUTF8("AdvanceToNextMisspelling")); | 348 WebString::fromUTF8("AdvanceToNextMisspelling")); |
342 } | 349 } |
343 | 350 |
(...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 | 386 // TODO(darin): There's actually no reason for this to be here. We should |
380 // have the browser side manage the document tag. | 387 // have the browser side manage the document tag. |
381 #if defined(OS_MACOSX) | 388 #if defined(OS_MACOSX) |
382 if (!has_document_tag_) { | 389 if (!has_document_tag_) { |
383 // Make the call to get the tag. | 390 // Make the call to get the tag. |
384 Send(new SpellCheckHostMsg_GetDocumentTag(routing_id(), &document_tag_)); | 391 Send(new SpellCheckHostMsg_GetDocumentTag(routing_id(), &document_tag_)); |
385 has_document_tag_ = true; | 392 has_document_tag_ = true; |
386 } | 393 } |
387 #endif | 394 #endif |
388 } | 395 } |
OLD | NEW |