Fixed crash in atom::api::SpellCheckClient

The class didn't save the V8 context for the spell checking JS function. When
it later tried to call the JS function and there was no active context, V8
crashed.

I also optimized the spell checking loop by introducing `SpellCheckScope` and
reusing the V8 handles throughout the whole loop.
This commit is contained in:
Ales Pergl 2017-12-06 12:55:28 +01:00
parent 4355f554cc
commit 7a73b1d523
2 changed files with 55 additions and 33 deletions

View file

@ -41,7 +41,10 @@ SpellCheckClient::SpellCheckClient(const std::string& language,
v8::Isolate* isolate, v8::Isolate* isolate,
v8::Local<v8::Object> provider) v8::Local<v8::Object> provider)
: isolate_(isolate), : isolate_(isolate),
context_(isolate, isolate->GetCurrentContext()),
provider_(isolate, provider) { provider_(isolate, provider) {
DCHECK(!context_.IsEmpty());
character_attributes_.SetDefaultLanguage(language); character_attributes_.SetDefaultLanguage(language);
// Persistent the method. // Persistent the method.
@ -49,7 +52,9 @@ SpellCheckClient::SpellCheckClient(const std::string& language,
dict.Get("spellCheck", &spell_check_); dict.Get("spellCheck", &spell_check_);
} }
SpellCheckClient::~SpellCheckClient() {} SpellCheckClient::~SpellCheckClient() {
context_.Reset();
}
void SpellCheckClient::CheckSpelling( void SpellCheckClient::CheckSpelling(
const blink::WebString& text, const blink::WebString& text,
@ -93,12 +98,9 @@ void SpellCheckClient::SpellCheckText(
const base::string16& text, const base::string16& text,
bool stop_at_first_result, bool stop_at_first_result,
std::vector<blink::WebTextCheckingResult>* results) { std::vector<blink::WebTextCheckingResult>* results) {
if (text.length() == 0 || spell_check_.IsEmpty()) if (text.empty() || spell_check_.IsEmpty())
return; return;
base::string16 word;
int word_start;
int word_length;
if (!text_iterator_.IsInitialized() && if (!text_iterator_.IsInitialized() &&
!text_iterator_.Initialize(&character_attributes_, true)) { !text_iterator_.Initialize(&character_attributes_, true)) {
// We failed to initialize text_iterator_, return as spelled correctly. // We failed to initialize text_iterator_, return as spelled correctly.
@ -106,17 +108,28 @@ void SpellCheckClient::SpellCheckText(
return; return;
} }
base::string16 in_word(text); if (!contraction_iterator_.IsInitialized() &&
text_iterator_.SetText(in_word.c_str(), in_word.size()); !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;
while (text_iterator_.GetNextWord(&word, &word_start, &word_length)) { while (text_iterator_.GetNextWord(&word, &word_start, &word_length)) {
// Found a word (or a contraction) that the spellchecker can check the // Found a word (or a contraction) that the spellchecker can check the
// spelling of. // spelling of.
if (SpellCheckWord(word)) if (SpellCheckWord(scope, word))
continue; continue;
// If the given word is a concatenated word of two or more valid words // 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. // (e.g. "hello:hello"), we should treat it as a valid word.
if (IsValidContraction(word)) if (IsValidContraction(scope, word))
continue; continue;
blink::WebTextCheckingResult result; blink::WebTextCheckingResult result;
@ -129,16 +142,16 @@ void SpellCheckClient::SpellCheckText(
} }
} }
bool SpellCheckClient::SpellCheckWord(const base::string16& word_to_check) { bool SpellCheckClient::SpellCheckWord(
if (spell_check_.IsEmpty()) const SpellCheckScope& scope,
return true; const base::string16& word_to_check) const {
DCHECK(!scope.spell_check_.IsEmpty());
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Value> word = mate::ConvertToV8(isolate_, word_to_check); v8::Local<v8::Value> word = mate::ConvertToV8(isolate_, word_to_check);
v8::Local<v8::Value> result = spell_check_.NewHandle()->Call( v8::Local<v8::Value> result =
provider_.NewHandle(), 1, &word); scope.spell_check_->Call(scope.provider_, 1, &word);
if (result->IsBoolean()) if (!result.IsEmpty() && result->IsBoolean())
return result->BooleanValue(); return result->BooleanValue();
else else
return true; return true;
@ -148,13 +161,9 @@ bool SpellCheckClient::SpellCheckWord(const base::string16& word_to_check) {
// This function is a fall-back when the SpellcheckWordIterator class // This function is a fall-back when the SpellcheckWordIterator class
// returns a concatenated word which is not in the selected dictionary // returns a concatenated word which is not in the selected dictionary
// (e.g. "in'n'out") but each word is valid. // (e.g. "in'n'out") but each word is valid.
bool SpellCheckClient::IsValidContraction(const base::string16& contraction) { bool SpellCheckClient::IsValidContraction(const SpellCheckScope& scope,
if (!contraction_iterator_.IsInitialized() && const base::string16& contraction) {
!contraction_iterator_.Initialize(&character_attributes_, false)) { DCHECK(contraction_iterator_.IsInitialized());
// We failed to initialize the word iterator, return as spelled correctly.
VLOG(1) << "Failed to initialize contraction_iterator_";
return true;
}
contraction_iterator_.SetText(contraction.c_str(), contraction.length()); contraction_iterator_.SetText(contraction.c_str(), contraction.length());
@ -163,12 +172,20 @@ bool SpellCheckClient::IsValidContraction(const base::string16& contraction) {
int word_length; int word_length;
while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) { while (contraction_iterator_.GetNextWord(&word, &word_start, &word_length)) {
if (!SpellCheckWord(word)) if (!SpellCheckWord(scope, word))
return false; return false;
} }
return true; return true;
} }
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()) {}
} // namespace api } // namespace api
} // namespace atom } // namespace atom

View file

@ -50,22 +50,28 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient,
void UpdateSpellingUIWithMisspelledWord( void UpdateSpellingUIWithMisspelledWord(
const blink::WebString& word) override; const blink::WebString& word) override;
struct SpellCheckScope {
v8::HandleScope handle_scope_;
v8::Context::Scope context_scope_;
v8::Local<v8::Object> provider_;
v8::Local<v8::Function> spell_check_;
explicit SpellCheckScope(const SpellCheckClient& client);
};
// Check the spelling of text. // Check the spelling of text.
void SpellCheckText(const base::string16& text, void SpellCheckText(const base::string16& text,
bool stop_at_first_result, bool stop_at_first_result,
std::vector<blink::WebTextCheckingResult>* results); std::vector<blink::WebTextCheckingResult>* results);
// Call JavaScript to check spelling a word. // Call JavaScript to check spelling a word.
bool SpellCheckWord(const base::string16& word_to_check); bool SpellCheckWord(const SpellCheckScope& scope,
const base::string16& word_to_check) const;
// Find a possible correctly spelled word for a misspelled word. Computes an
// empty string if input misspelled word is too long, there is ambiguity, or
// the correct spelling cannot be determined.
base::string16 GetAutoCorrectionWord(const base::string16& word);
// Returns whether or not the given word is a contraction of valid words // Returns whether or not the given word is a contraction of valid words
// (e.g. "word:word"). // (e.g. "word:word").
bool IsValidContraction(const base::string16& word); bool IsValidContraction(const SpellCheckScope& scope,
const base::string16& word);
// Represents character attributes used for filtering out characters which // Represents character attributes used for filtering out characters which
// are not supported by this SpellCheck object. // are not supported by this SpellCheck object.
@ -79,9 +85,8 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient,
SpellcheckWordIterator text_iterator_; SpellcheckWordIterator text_iterator_;
SpellcheckWordIterator contraction_iterator_; SpellcheckWordIterator contraction_iterator_;
bool auto_spell_correct_turned_on_;
v8::Isolate* isolate_; v8::Isolate* isolate_;
v8::Persistent<v8::Context> context_;
mate::ScopedPersistent<v8::Object> provider_; mate::ScopedPersistent<v8::Object> provider_;
mate::ScopedPersistent<v8::Function> spell_check_; mate::ScopedPersistent<v8::Function> spell_check_;