blob: 78d62802755748d4bb5fc8d1fed3612216d08236 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/spellcheck/renderer/hunspell_engine.h"
#include <stddef.h>
#include <algorithm>
#include <iterator>
#include <utility>
#include "base/files/memory_mapped_file.h"
#include "base/time/time.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/renderer/render_thread.h"
#include "services/service_manager/public/cpp/local_interface_provider.h"
#include "third_party/hunspell/src/hunspell/hunspell.hxx"
using content::RenderThread;
namespace {
// Maximum length of words we actually check.
// 64 is the observed limits for OSX system checker.
const size_t kMaxCheckedLen = 64;
// Maximum length of words we provide suggestions for.
// 24 is the observed limits for OSX system checker.
const size_t kMaxSuggestLen = 24;
static_assert(kMaxCheckedLen <= size_t(MAXWORDLEN),
"MaxCheckedLen too long");
static_assert(kMaxSuggestLen <= kMaxCheckedLen,
"MaxSuggestLen too long");
} // namespace
#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
SpellingEngine* CreateNativeSpellingEngine(
service_manager::LocalInterfaceProvider* embedder_provider) {
DCHECK(embedder_provider);
return new HunspellEngine(embedder_provider);
}
#endif
HunspellEngine::HunspellEngine(
service_manager::LocalInterfaceProvider* embedder_provider)
: hunspell_enabled_(false),
initialized_(false),
dictionary_requested_(false),
embedder_provider_(embedder_provider) {
// Wait till we check the first word before doing any initializing.
}
HunspellEngine::~HunspellEngine() {
}
void HunspellEngine::Init(base::File file) {
initialized_ = true;
hunspell_.reset();
bdict_file_.reset();
file_ = std::move(file);
hunspell_enabled_ = file_.IsValid();
// Delay the actual initialization of hunspell until it is needed.
}
void HunspellEngine::InitializeHunspell() {
if (hunspell_)
return;
bdict_file_.reset(new base::MemoryMappedFile);
if (bdict_file_->Initialize(std::move(file_))) {
hunspell_.reset(new Hunspell(bdict_file_->data(), bdict_file_->length()));
} else {
NOTREACHED() << "Could not mmap spellchecker dictionary.";
}
}
bool HunspellEngine::CheckSpelling(const base::string16& word_to_check,
int tag) {
// Assume all words that cannot be checked are valid. Since Chrome can't
// offer suggestions on them, either, there's no point in flagging them to
// the user.
bool word_correct = true;
std::string word_to_check_utf8(base::UTF16ToUTF8(word_to_check));
// Limit the size of checked words.
if (word_to_check_utf8.length() <= kMaxCheckedLen) {
// If |hunspell_| is NULL here, an error has occurred, but it's better
// to check rather than crash.
if (hunspell_) {
// |hunspell_->spell| returns 0 if the word is misspelled.
word_correct = (hunspell_->spell(word_to_check_utf8) != 0);
}
}
return word_correct;
}
void HunspellEngine::FillSuggestionList(
const base::string16& wrong_word,
std::vector<base::string16>* optional_suggestions) {
std::string wrong_word_utf8(base::UTF16ToUTF8(wrong_word));
if (wrong_word_utf8.length() > kMaxSuggestLen)
return;
// If |hunspell_| is NULL here, an error has occurred, but it's better
// to check rather than crash.
// TODO(groby): Technically, it's not. We should track down the issue.
if (!hunspell_)
return;
std::vector<std::string> suggestions =
hunspell_->suggest(wrong_word_utf8);
// Populate the vector of WideStrings.
for (size_t i = 0; i < suggestions.size(); ++i) {
if (i < spellcheck::kMaxSuggestions)
optional_suggestions->push_back(base::UTF8ToUTF16(suggestions[i]));
}
}
bool HunspellEngine::InitializeIfNeeded() {
if (!initialized_ && !dictionary_requested_) {
spellcheck::mojom::SpellCheckHostPtr spell_check_host;
embedder_provider_->GetInterface(&spell_check_host);
spell_check_host->RequestDictionary();
dictionary_requested_ = true;
return true;
}
// Don't initialize if hunspell is disabled.
if (file_.IsValid())
InitializeHunspell();
return !initialized_;
}
bool HunspellEngine::IsEnabled() {
return hunspell_enabled_;
}