chromium/components/spellcheck/renderer/spellcheck_provider.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "components/spellcheck/renderer/spellcheck_provider.h"

#include <unordered_map>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "components/spellcheck/renderer/spellcheck.h"
#include "components/spellcheck/renderer/spellcheck_language.h"
#include "components/spellcheck/renderer/spellcheck_renderer_metrics.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "services/service_manager/public/cpp/local_interface_provider.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_text_checking_completion.h"
#include "third_party/blink/public/web/web_text_checking_result.h"
#include "third_party/blink/public/web/web_text_decoration_type.h"

WebElement;
WebLocalFrame;
WebString;
WebTextCheckingCompletion;
WebTextCheckingResult;
WebTextDecorationType;
WebVector;

static_assert;
static_assert;

class SpellCheckProvider::DictionaryUpdateObserverImpl
    : public DictionaryUpdateObserver {};

SpellCheckProvider::DictionaryUpdateObserverImpl::DictionaryUpdateObserverImpl(
    SpellCheckProvider* owner)
    :{}

SpellCheckProvider::DictionaryUpdateObserverImpl::
    ~DictionaryUpdateObserverImpl() {}

void SpellCheckProvider::DictionaryUpdateObserverImpl::OnDictionaryUpdated(
    const WebVector<WebString>& words_added) {}

SpellCheckProvider::SpellCheckProvider(content::RenderFrame* render_frame,
                                       SpellCheck* spellcheck)
    :{}

SpellCheckProvider::~SpellCheckProvider() {}

void SpellCheckProvider::ResetDictionaryUpdateObserverForTesting() {}

void SpellCheckProvider::RequestTextChecking(
    const std::u16string& text,
    std::unique_ptr<WebTextCheckingCompletion> completion) {}

#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckProvider::RequestTextCheckingFromBrowser(
    const std::u16string& text) {
  DCHECK(spellcheck::UseBrowserSpellChecker());
#if BUILDFLAG(IS_WIN)

  // Determine whether a hybrid check is needed.
  bool use_hunspell = spellcheck_->EnabledLanguageCount() > 0;
  bool use_native =
      spellcheck_->EnabledLanguageCount() != spellcheck_->LanguageCount();

  if (!use_hunspell && !use_native) {
    OnRespondTextCheck(last_identifier_, text, /*results=*/{});
    return;
  }

  if (!use_native) {
    // No language can be handled by the native spell checker. Use the regular
    // Hunspell code path.
    GetSpellCheckHost().CallSpellingService(
        text,
        base::BindOnce(&SpellCheckProvider::OnRespondSpellingService,
                       weak_factory_.GetWeakPtr(), last_identifier_, text));
    return;
  }

  // Some languages can be handled by the native spell checker. Use the
  // regular browser spell check code path. If hybrid spell check is
  // required (i.e. some locales must be checked by Hunspell), misspellings
  // from the native spell checker will be double-checked with Hunspell in
  // the |OnRespondTextCheck| callback.
  hybrid_requests_info_[last_identifier_] = {/*used_hunspell=*/use_hunspell,
                                             /*used_native=*/use_native,
                                             base::TimeTicks::Now()};
#endif  // BUILDFLAG(IS_WIN)

  // Text check (unified request for grammar and spell check) is only
  // available for browser process, so we ask the system spellchecker
  // over mojo or return an empty result if the checker is not available.
  GetSpellCheckHost().RequestTextCheck(
      text, base::BindOnce(&SpellCheckProvider::OnRespondTextCheck,
                           weak_factory_.GetWeakPtr(), last_identifier_, text));
}

#if BUILDFLAG(IS_WIN)
void SpellCheckProvider::OnRespondInitializeDictionaries(
    const std::u16string& text,
    std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
    const std::vector<std::string>& custom_words,
    bool enable) {
  DCHECK(!dictionaries_loaded_);
  dictionaries_loaded_ = true;

  // Because the SpellChecker and SpellCheckHost mojo interfaces use different
  // channels, there is no guarantee that the SpellChecker::Initialize response
  // will be received before the SpellCheckHost::InitializeDictionaries
  // callback. If the order is reversed, no spellcheck will be performed since
  // the renderer side thinks there are no dictionaries available. Ensure that
  // the SpellChecker is initialized before performing a spellcheck.
  spellcheck_->Initialize(std::move(dictionaries), custom_words, enable);

  RequestTextCheckingFromBrowser(text);
}
#endif  // BUILDFLAG(IS_WIN)
#endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)

void SpellCheckProvider::FocusedElementChanged(
    const blink::WebElement& unused) {}

spellcheck::mojom::SpellCheckHost& SpellCheckProvider::GetSpellCheckHost() {}

bool SpellCheckProvider::IsSpellCheckingEnabled() const {}

void SpellCheckProvider::CheckSpelling(
    const WebString& text,
    size_t& offset,
    size_t& length,
    blink::WebVector<blink::WebString>* optional_suggestions) {}

void SpellCheckProvider::RequestCheckingOfText(
    const WebString& text,
    std::unique_ptr<WebTextCheckingCompletion> completion) {}

#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
void SpellCheckProvider::OnRespondSpellingService(
    int identifier,
    const std::u16string& line,
    bool success,
    const std::vector<SpellCheckResult>& results) {}
#endif

bool SpellCheckProvider::HasWordCharacters(const std::u16string& text,
                                           size_t index) const {}

#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckProvider::OnRespondTextCheck(
    int identifier,
    const std::u16string& line,
    const std::vector<SpellCheckResult>& results) {
  DCHECK(spellcheck_);
  if (!text_check_completions_.Lookup(identifier))
    return;
  std::unique_ptr<WebTextCheckingCompletion> completion(
      text_check_completions_.Replace(identifier, nullptr));
  text_check_completions_.Remove(identifier);
  blink::WebVector<blink::WebTextCheckingResult> textcheck_results;

  SpellCheck::ResultFilter result_filter = SpellCheck::DO_NOT_MODIFY;

#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
  const auto& request_info = hybrid_requests_info_.find(identifier);
  if (spellcheck::UseBrowserSpellChecker() &&
      request_info != hybrid_requests_info_.end() &&
      request_info->second.used_hunspell && request_info->second.used_native) {
    // Not all locales could be checked by the native spell checker. Verify each
    // mistake against Hunspell in the locales that weren't checked.
    result_filter = SpellCheck::USE_HUNSPELL_FOR_HYBRID_CHECK;
  }
#endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)

  spellcheck_->CreateTextCheckingResults(result_filter, GetSpellCheckHost(),
                                         /*line_offset=*/0, line, results,
                                         &textcheck_results);

#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
  if (request_info != hybrid_requests_info_.end()) {
    spellcheck_renderer_metrics::RecordSpellcheckDuration(
        base::TimeTicks::Now() - request_info->second.request_start_ticks,
        request_info->second.used_hunspell, request_info->second.used_native);
    hybrid_requests_info_.erase(request_info);
  }
#endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)

  completion->DidFinishCheckingText(textcheck_results);

  // Cache the request and the converted results.
  last_request_ = line;
  last_results_.swap(textcheck_results);
}
#endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)

bool SpellCheckProvider::SatisfyRequestFromCache(
    const std::u16string& text,
    WebTextCheckingCompletion* completion) {}

void SpellCheckProvider::OnDestruct() {}