Skip to content

Commit c6a18b1

Browse files
committed
Split the words before sending it to spellCheck
1 parent b801a93 commit c6a18b1

File tree

7 files changed

+720
-19
lines changed

7 files changed

+720
-19
lines changed

atom.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@
323323
'chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm',
324324
'chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc',
325325
'chromium_src/chrome/renderer/printing/print_web_view_helper.h',
326+
'chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc',
327+
'chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h',
326328
'chromium_src/chrome/renderer/tts_dispatcher.cc',
327329
'chromium_src/chrome/renderer/tts_dispatcher.h',
328330
'chromium_src/library_loaders/libgio_loader.cc',

atom/renderer/api/atom_api_spell_check_client.cc

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,53 @@ bool HasWordCharacters(const base::string16& text, int index) {
5454
} // namespace
5555

5656
SpellCheckClient::SpellCheckClient(v8::Isolate* isolate,
57+
const std::string& language,
5758
v8::Handle<v8::Object> provider)
58-
: isolate_(isolate), provider_(isolate, provider) {}
59+
: isolate_(isolate), provider_(isolate, provider) {
60+
character_attributes_.SetDefaultLanguage(language);
61+
62+
// Persistent the method.
63+
mate::Dictionary dict(isolate, provider);
64+
dict.Get("spellCheck", &spell_check_);
65+
}
5966

6067
SpellCheckClient::~SpellCheckClient() {}
6168

6269
void SpellCheckClient::spellCheck(
6370
const blink::WebString& text,
64-
int& misspelledOffset,
65-
int& misspelledLength,
66-
blink::WebVector<blink::WebString>* optionalSuggestions) {
67-
blink::WebTextCheckingResult result;
68-
if (!CallProviderMethod("spellCheck", text, &result))
71+
int& misspelling_start,
72+
int& misspelling_len,
73+
blink::WebVector<blink::WebString>* optional_suggestions) {
74+
if (text.length() == 0 || spell_check_.IsEmpty())
6975
return;
7076

71-
misspelledOffset = result.location;
72-
misspelledLength = result.length;
77+
base::string16 word;
78+
int word_start;
79+
int word_length;
80+
if (!text_iterator_.IsInitialized() &&
81+
!text_iterator_.Initialize(&character_attributes_, true)) {
82+
// We failed to initialize text_iterator_, return as spelled correctly.
83+
VLOG(1) << "Failed to initialize SpellcheckWordIterator";
84+
return;
85+
}
86+
87+
base::string16 in_word(text);
88+
text_iterator_.SetText(in_word.c_str(), in_word.size());
89+
while (text_iterator_.GetNextWord(&word, &word_start, &word_length)) {
90+
// Found a word (or a contraction) that the spellchecker can check the
91+
// spelling of.
92+
if (CheckSpelling(word))
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))
98+
continue;
99+
100+
misspelling_start = word_start;
101+
misspelling_len = word_length;
102+
return;
103+
}
73104
}
74105

75106
void SpellCheckClient::checkTextOfParagraph(
@@ -90,13 +121,6 @@ void SpellCheckClient::requestCheckingOfText(
90121
const blink::WebVector<uint32_t>& markersInText,
91122
const blink::WebVector<unsigned>& markerOffsets,
92123
blink::WebTextCheckingCompletion* completionCallback) {
93-
v8::HandleScope handle_scope(isolate_);
94-
v8::Handle<v8::Object> provider = provider_.NewHandle();
95-
if (!provider->Has(mate::StringToV8(isolate_, "requestCheckingOfText"))) {
96-
completionCallback->didCancelCheckingText();
97-
return;
98-
}
99-
100124
base::string16 text(textToCheck);
101125
if (text.empty() || !HasWordCharacters(text, 0)) {
102126
completionCallback->didCancelCheckingText();
@@ -151,6 +175,46 @@ bool SpellCheckClient::CallProviderMethod(const char* method,
151175
return mate::ConvertFromV8(isolate_, v8_result, result);;
152176
}
153177

178+
bool SpellCheckClient::CheckSpelling(const base::string16& word_to_check) {
179+
if (spell_check_.IsEmpty())
180+
return true;
181+
182+
v8::HandleScope handle_scope(isolate_);
183+
v8::Handle<v8::Value> word = mate::ConvertToV8(isolate_, word_to_check);
184+
v8::Handle<v8::Value> result = spell_check_.NewHandle()->Call(
185+
provider_.NewHandle(), 1, &word);
186+
187+
if (result->IsBoolean())
188+
return result->BooleanValue();
189+
else
190+
return true;
191+
}
192+
193+
// Returns whether or not the given string is a valid contraction.
194+
// This function is a fall-back when the SpellcheckWordIterator class
195+
// returns a concatenated word which is not in the selected dictionary
196+
// (e.g. "in'n'out") but each word is valid.
197+
bool SpellCheckClient::IsValidContraction(const base::string16& contraction) {
198+
if (!contraction_iterator_.IsInitialized() &&
199+
!contraction_iterator_.Initialize(&character_attributes_, false)) {
200+
// We failed to initialize the word iterator, return as spelled correctly.
201+
VLOG(1) << "Failed to initialize contraction_iterator_";
202+
return true;
203+
}
204+
205+
contraction_iterator_.SetText(contraction.c_str(), contraction.length());
206+
207+
base::string16 word;
208+
int word_start;
209+
int word_length;
210+
211+
while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) {
212+
if (!CheckSpelling(word))
213+
return false;
214+
}
215+
return true;
216+
}
217+
154218
} // namespace api
155219

156220
} // namespace atom

atom/renderer/api/atom_api_spell_check_client.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#ifndef ATOM_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_
66
#define ATOM_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_
77

8+
#include <string>
9+
10+
#include "base/callback.h"
11+
#include "chrome/renderer/spellchecker/spellcheck_worditerator.h"
812
#include "native_mate/scoped_persistent.h"
913
#include "third_party/WebKit/public/web/WebSpellCheckClient.h"
1014

@@ -14,7 +18,9 @@ namespace api {
1418

1519
class SpellCheckClient : public blink::WebSpellCheckClient {
1620
public:
17-
SpellCheckClient(v8::Isolate* isolate, v8::Handle<v8::Object> provider);
21+
SpellCheckClient(v8::Isolate* isolate,
22+
const std::string& language,
23+
v8::Handle<v8::Object> provider);
1824
virtual ~SpellCheckClient();
1925

2026
private:
@@ -44,8 +50,28 @@ class SpellCheckClient : public blink::WebSpellCheckClient {
4450
bool CallProviderMethod(const char* method, const blink::WebString& text,
4551
T* result);
4652

53+
// Call JavaScript to check spelling.
54+
bool CheckSpelling(const base::string16& word_to_check);
55+
56+
// Returns whether or not the given word is a contraction of valid words
57+
// (e.g. "word:word").
58+
bool IsValidContraction(const base::string16& word);
59+
60+
// Represents character attributes used for filtering out characters which
61+
// are not supported by this SpellCheck object.
62+
SpellcheckCharAttribute character_attributes_;
63+
64+
// Represents word iterators used in this spellchecker. The |text_iterator_|
65+
// splits text provided by WebKit into words, contractions, or concatenated
66+
// words. The |contraction_iterator_| splits a concatenated word extracted by
67+
// |text_iterator_| into word components so we can treat a concatenated word
68+
// consisting only of correct words as a correct word.
69+
SpellcheckWordIterator text_iterator_;
70+
SpellcheckWordIterator contraction_iterator_;
71+
4772
v8::Isolate* isolate_;
4873
mate::ScopedPersistent<v8::Object> provider_;
74+
mate::ScopedPersistent<v8::Function> spell_check_;
4975

5076
DISALLOW_COPY_AND_ASSIGN(SpellCheckClient);
5177
};

atom/renderer/api/atom_api_web_frame.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ void WebFrame::AttachGuest(int id) {
5757
content::RenderFrame::FromWebFrame(web_frame_)->AttachGuest(id);
5858
}
5959

60-
void WebFrame::SetSpellCheckProvider(v8::Isolate* isolate,
60+
void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
61+
const std::string& language,
6162
v8::Handle<v8::Object> provider) {
62-
spell_check_client_.reset(new SpellCheckClient(isolate, provider));
63+
v8::Isolate* isolate = args->isolate();
64+
if (!provider->Has(mate::StringToV8(isolate, "spellCheck"))) {
65+
args->ThrowError("\"spellCheck\" has to be defined");
66+
return;
67+
}
68+
69+
spell_check_client_.reset(new SpellCheckClient(isolate, language, provider));
6370
web_frame_->view()->setSpellCheckClient(spell_check_client_.get());
6471
}
6572

atom/renderer/api/atom_api_web_frame.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ namespace blink {
1515
class WebLocalFrame;
1616
}
1717

18+
namespace mate {
19+
class Arguments;
20+
}
21+
1822
namespace atom {
1923

2024
namespace api {
@@ -41,7 +45,8 @@ class WebFrame : public mate::Wrappable {
4145
void AttachGuest(int element_instance_id);
4246

4347
// Set the provider that will be used by SpellCheckClient for spell check.
44-
void SetSpellCheckProvider(v8::Isolate* isolate,
48+
void SetSpellCheckProvider(mate::Arguments* args,
49+
const std::string& language,
4550
v8::Handle<v8::Object> provider);
4651

4752
// mate::Wrappable:

0 commit comments

Comments
 (0)