Convert Spellcheck host MessageFilter IPC to mojo

Remove chrome/browser/spellchecker/spellcheck_message_filter and its
unittest. Replace it with a mojo implementation of same that exposes
an interface to renderers for calling spell-check-host API available
in the browser process:

  - RequestDictionary, NotifyChecked, CallSpellingService.

Update browser tests / unit tests to use the new mojo implementation
and add a mojom::SpellCheckHost unit test:

  - chrome_site_per_process_browsertest.cc
  - spellcheck_service_browsertest.cc
  - spellcheck_provider_hunspell_unittest.cc
  - spell_check_host_impl_unittest.cc

Bug: 714480
Change-Id: I7e6a46245a908fe4600b1d3d86f2d9dc884f79ea
Reviewed-on: https://chromium-review.googlesource.com/505648
Reviewed-by: Mike West <[email protected]>
Reviewed-by: Jochen Eisinger <[email protected]>
Reviewed-by: Sam McNally <[email protected]>
Reviewed-by: Rouslan Solomakhin <[email protected]>
Reviewed-by: Johan Tibell <[email protected]>
Commit-Queue: Noel Gordon <[email protected]>
Cr-Commit-Position: refs/heads/master@{#475395}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b259642..74fb59d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2025,14 +2025,14 @@
   }
   if (enable_spellcheck) {
     sources += [
+      "spellchecker/spell_check_host_impl.cc",
+      "spellchecker/spell_check_host_impl.h",
       "spellchecker/spellcheck_custom_dictionary.cc",
       "spellchecker/spellcheck_custom_dictionary.h",
       "spellchecker/spellcheck_factory.cc",
       "spellchecker/spellcheck_factory.h",
       "spellchecker/spellcheck_hunspell_dictionary.cc",
       "spellchecker/spellcheck_hunspell_dictionary.h",
-      "spellchecker/spellcheck_message_filter.cc",
-      "spellchecker/spellcheck_message_filter.h",
       "spellchecker/spellcheck_message_filter_platform_mac.cc",
       "spellchecker/spellcheck_service.cc",
       "spellchecker/spellcheck_service.h",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e90776d..840d9a1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -359,7 +359,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
 #endif
 
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
@@ -1205,9 +1205,6 @@
 #if BUILDFLAG(ENABLE_PRINTING)
   host->AddFilter(new printing::PrintingMessageFilter(id, profile));
 #endif
-#if BUILDFLAG(ENABLE_SPELLCHECK)
-  host->AddFilter(new SpellCheckMessageFilter(id));
-#endif
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   host->AddFilter(new SpellCheckMessageFilterPlatform(id));
 #endif
@@ -3071,6 +3068,11 @@
   registry->AddInterface(
       base::Bind(&BudgetServiceImpl::Create, render_process_host->GetID()),
       ui_task_runner);
+#if BUILDFLAG(ENABLE_SPELLCHECK)
+  registry->AddInterface(
+      base::Bind(&SpellCheckHostImpl::Create, render_process_host->GetID()),
+      ui_task_runner);
+#endif
   registry->AddInterface(
       base::Bind(&rappor::RapporRecorderImpl::Create,
                  g_browser_process->rappor_service()),
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 887404b..8f8676b 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -13,6 +13,7 @@
           "metrics::mojom::LeakDetector",
           "mojom::ModuleEventSink",
           "rappor::mojom::RapporRecorder",
+          "spellcheck::mojom::SpellCheckHost",
           "startup_metric_utils::mojom::StartupMetricHost",
           "translate::mojom::ContentTranslateDriver"
         ],
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index 1c6047f..27e813c 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -38,7 +38,9 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_messages.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
 #endif
 
 class ChromeSitePerProcessTest : public InProcessBrowserTest {
@@ -560,58 +562,87 @@
 }
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
-// Class to sniff incoming IPCs for spell check messages.
-class TestSpellCheckMessageFilter : public content::BrowserMessageFilter {
+// Class to sniff incoming spellcheck IPC / Mojo SpellCheckHost messages.
+class TestSpellCheckMessageFilter : public content::BrowserMessageFilter,
+                                    spellcheck::mojom::SpellCheckHost {
  public:
   explicit TestSpellCheckMessageFilter(content::RenderProcessHost* process_host)
       : content::BrowserMessageFilter(SpellCheckMsgStart),
         process_host_(process_host),
         text_received_(false),
         message_loop_runner_(
-            base::MakeRefCounted<content::MessageLoopRunner>()) {}
+            base::MakeRefCounted<content::MessageLoopRunner>()),
+        binding_(this) {}
 
-  bool OnMessageReceived(const IPC::Message& message) override {
-    IPC_BEGIN_MESSAGE_MAP(TestSpellCheckMessageFilter, message)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService, HandleMessage)
-#else
-      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck, HandleMessage)
-#endif
-    IPC_END_MESSAGE_MAP()
-    return false;
-  }
+  content::RenderProcessHost* process() const { return process_host_; }
 
-  base::string16 last_text() const { return last_text_; }
+  const base::string16& text() const { return text_; }
 
   void Wait() {
     if (!text_received_)
       message_loop_runner_->Run();
   }
 
-  content::RenderProcessHost* process() const { return process_host_; }
+  bool OnMessageReceived(const IPC::Message& message) override {
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    IPC_BEGIN_MESSAGE_MAP(TestSpellCheckMessageFilter, message)
+      // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
+      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck, HandleMessage)
+    IPC_END_MESSAGE_MAP()
+#endif
+    return false;
+  }
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  void ShellCheckHostRequest(const service_manager::BindSourceInfo& source_info,
+                             spellcheck::mojom::SpellCheckHostRequest request) {
+    EXPECT_FALSE(binding_.is_bound());
+    binding_.Bind(std::move(request));
+  }
+#endif
 
  private:
   ~TestSpellCheckMessageFilter() override {}
 
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   void HandleMessage(int, int, const base::string16& text) {
     content::BrowserThread::PostTask(
         content::BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&TestSpellCheckMessageFilter::HandleMessageOnUI, this,
-                       text));
+        base::BindOnce(&TestSpellCheckMessageFilter::HandleMessageOnUIThread,
+                       this, text));
   }
+#endif
 
-  void HandleMessageOnUI(const base::string16& text) {
-    last_text_ = text;
+  void HandleMessageOnUIThread(const base::string16& text) {
     if (!text_received_) {
       text_received_ = true;
+      text_ = text;
       message_loop_runner_->Quit();
+    } else {
+      NOTREACHED();
     }
   }
 
+  // spellcheck::mojom::SpellCheckHost:
+  void RequestDictionary() override {}
+
+  void NotifyChecked(const base::string16& word, bool misspelled) override {}
+
+  void CallSpellingService(const base::string16& text,
+                           CallSpellingServiceCallback callback) override {
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    std::move(callback).Run(true, std::vector<SpellCheckResult>());
+    binding_.Close();
+    HandleMessageOnUIThread(text);
+#endif
+  }
+
   content::RenderProcessHost* process_host_;
   bool text_received_;
-  base::string16 last_text_;
+  base::string16 text_;
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+  mojo::Binding<spellcheck::mojom::SpellCheckHost> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSpellCheckMessageFilter);
 };
@@ -629,6 +660,28 @@
     ChromeContentBrowserClient::RenderProcessWillLaunch(process_host);
   }
 
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  void ExposeInterfacesToRenderer(
+      service_manager::BinderRegistry* registry,
+      content::AssociatedInterfaceRegistry* associated_registry,
+      content::RenderProcessHost* render_process_host) override {
+    // Expose the default interfaces.
+    ChromeContentBrowserClient::ExposeInterfacesToRenderer(
+        registry, associated_registry, render_process_host);
+
+    scoped_refptr<TestSpellCheckMessageFilter> filter =
+        GetSpellCheckMessageFilterForProcess(render_process_host);
+    CHECK(filter);
+
+    // Override the default SpellCheckHost interface.
+    auto ui_task_runner = content::BrowserThread::GetTaskRunnerForThread(
+        content::BrowserThread::UI);
+    registry->AddInterface(
+        base::Bind(&TestSpellCheckMessageFilter::ShellCheckHostRequest, filter),
+        ui_task_runner);
+  }
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
   // Retrieves the registered filter for the given RenderProcessHost. It will
   // return nullptr if the RenderProcessHost was initialized while a different
   // instance of ContentBrowserClient was in action.
@@ -661,14 +714,15 @@
 
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  content::RenderFrameHost* subframe =
+  content::RenderFrameHost* cross_site_subframe =
       ChildFrameAt(web_contents->GetMainFrame(), 0);
+
   scoped_refptr<TestSpellCheckMessageFilter> filter =
       browser_client.GetSpellCheckMessageFilterForProcess(
-          subframe->GetProcess());
+          cross_site_subframe->GetProcess());
   filter->Wait();
 
-  EXPECT_EQ(base::ASCIIToUTF16("zz."), filter->last_text());
+  EXPECT_EQ(base::ASCIIToUTF16("zz."), filter->text());
 
   content::SetBrowserClientForTesting(old_browser_client);
 }
diff --git a/chrome/browser/spellchecker/spell_check_host_impl.cc b/chrome/browser/spellchecker/spell_check_host_impl.cc
new file mode 100644
index 0000000..788a19c
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl.cc
@@ -0,0 +1,133 @@
+// Copyright 2017 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 "chrome/browser/spellchecker/spell_check_host_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "components/spellcheck/browser/spellcheck_host_metrics.h"
+#include "components/spellcheck/common/spellcheck_result.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+SpellCheckHostImpl::SpellCheckHostImpl(int render_process_id)
+    : render_process_id_(render_process_id) {}
+
+SpellCheckHostImpl::~SpellCheckHostImpl() = default;
+
+// static
+void SpellCheckHostImpl::Create(
+    int render_process_id,
+    const service_manager::BindSourceInfo& source_info,
+    spellcheck::mojom::SpellCheckHostRequest request) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<SpellCheckHostImpl>(render_process_id),
+      std::move(request));
+}
+
+void SpellCheckHostImpl::RequestDictionary() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // The renderer has requested that we initialize its spellchecker. This
+  // generally should only be called once per session, as after the first
+  // call, future renderers will be passed the initialization information
+  // on startup (or when the dictionary changes in some way).
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck)
+    return;  // Teardown.
+
+  // The spellchecker initialization already started and finished; just
+  // send it to the renderer.
+  spellcheck->InitForRenderer(
+      content::RenderProcessHost::FromID(render_process_id_));
+
+  // TODO(rlp): Ensure that we do not initialize the hunspell dictionary
+  // more than once if we get requests from different renderers.
+}
+
+void SpellCheckHostImpl::NotifyChecked(const base::string16& word,
+                                       bool misspelled) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck)
+    return;  // Teardown.
+  if (spellcheck->GetMetrics())
+    spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
+}
+
+void SpellCheckHostImpl::CallSpellingService(
+    const base::string16& text,
+    CallSpellingServiceCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (text.empty()) {
+    std::move(callback).Run(false, std::vector<SpellCheckResult>());
+    mojo::ReportBadMessage(__FUNCTION__);
+    return;
+  }
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  content::RenderProcessHost* host =
+      content::RenderProcessHost::FromID(render_process_id_);
+
+  // Checks the user profile and sends a JSON-RPC request to the Spelling
+  // service if a user enables the "Ask Google for suggestions" option. When
+  // a response is received (including an error) from the remote Spelling
+  // service, calls CallSpellingServiceDone.
+  client_.RequestTextCheck(
+      host ? host->GetBrowserContext() : nullptr,
+      SpellingServiceClient::SPELLCHECK, text,
+      base::Bind(&SpellCheckHostImpl::CallSpellingServiceDone,
+                 base::Unretained(this), base::Passed(&callback)));
+#else
+  std::move(callback).Run(false, std::vector<SpellCheckResult>());
+#endif
+}
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+void SpellCheckHostImpl::CallSpellingServiceDone(
+    CallSpellingServiceCallback callback,
+    bool success,
+    const base::string16& text,
+    const std::vector<SpellCheckResult>& service_results) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck) {  // Teardown.
+    std::move(callback).Run(false, std::vector<SpellCheckResult>());
+    return;
+  }
+
+  std::vector<SpellCheckResult> results = FilterCustomWordResults(
+      base::UTF16ToUTF8(text), *spellcheck->GetCustomDictionary(),
+      service_results);
+
+  std::move(callback).Run(success, results);
+}
+
+// static
+std::vector<SpellCheckResult> SpellCheckHostImpl::FilterCustomWordResults(
+    const std::string& text,
+    const SpellcheckCustomDictionary& custom_dictionary,
+    const std::vector<SpellCheckResult>& service_results) {
+  std::vector<SpellCheckResult> results;
+  for (const auto& result : service_results) {
+    const std::string word = text.substr(result.location, result.length);
+    if (!custom_dictionary.HasWord(word))
+      results.push_back(result);
+  }
+
+  return results;
+}
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+SpellcheckService* SpellCheckHostImpl::GetSpellcheckService() const {
+  return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+}
diff --git a/chrome/browser/spellchecker/spell_check_host_impl.h b/chrome/browser/spellchecker/spell_check_host_impl.h
new file mode 100644
index 0000000..9349fa1
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl.h
@@ -0,0 +1,74 @@
+// Copyright 2017 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.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
+#define CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
+
+#include "base/macros.h"
+#include "components/spellcheck/browser/spelling_service_client.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/spellcheck_build_features.h"
+
+#if !BUILDFLAG(ENABLE_SPELLCHECK)
+#error "Spellcheck should be enabled."
+#endif
+
+class SpellcheckCustomDictionary;
+class SpellcheckService;
+
+struct SpellCheckResult;
+
+namespace service_manager {
+struct BindSourceInfo;
+}
+
+class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
+ public:
+  explicit SpellCheckHostImpl(int render_process_id);
+  ~SpellCheckHostImpl() override;
+
+  static void Create(int render_process_id,
+                     const service_manager::BindSourceInfo& source_info,
+                     spellcheck::mojom::SpellCheckHostRequest request);
+
+ private:
+  friend class TestSpellCheckHostImpl;
+
+  // spellcheck::mojom::SpellCheckHost:
+  void RequestDictionary() override;
+  void NotifyChecked(const base::string16& word, bool misspelled) override;
+  void CallSpellingService(const base::string16& text,
+                           CallSpellingServiceCallback callback) override;
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // Invoked when the remote Spelling service has finished checking the
+  // text of a CallSpellingService request.
+  void CallSpellingServiceDone(
+      CallSpellingServiceCallback callback,
+      bool success,
+      const base::string16& text,
+      const std::vector<SpellCheckResult>& service_results) const;
+
+  // Filter out spelling corrections of custom dictionary words from the
+  // Spelling service results.
+  static std::vector<SpellCheckResult> FilterCustomWordResults(
+      const std::string& text,
+      const SpellcheckCustomDictionary& custom_dictionary,
+      const std::vector<SpellCheckResult>& service_results);
+#endif
+
+  // Returns the SpellcheckService of our |render_process_id_|. The return
+  // is null if the render process is being shut down.
+  virtual SpellcheckService* GetSpellcheckService() const;
+
+  // The process ID of our render process host.
+  const int render_process_id_;
+
+  // A JSON-RPC client that calls the remote Spelling service.
+  SpellingServiceClient client_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpellCheckHostImpl);
+};
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
diff --git a/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc b/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc
new file mode 100644
index 0000000..22cadb6
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2017 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 "chrome/browser/spellchecker/spell_check_host_impl.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/spellcheck/common/spellcheck_result.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#error !BUILDFLAG(USE_BROWSER_SPELLCHECKER) is required for these tests.
+#endif
+
+class TestSpellCheckHostImpl {
+ public:
+  TestSpellCheckHostImpl()
+      : spellcheck_(base::MakeUnique<SpellcheckService>(&testing_profile_)) {}
+
+  SpellcheckCustomDictionary& GetCustomDictionary() const {
+    EXPECT_NE(nullptr, spellcheck_.get());
+    SpellcheckCustomDictionary* custom_dictionary =
+        spellcheck_->GetCustomDictionary();
+    return *custom_dictionary;
+  }
+
+  std::vector<SpellCheckResult> FilterCustomWordResults(
+      const std::string& text,
+      const std::vector<SpellCheckResult>& service_results) const {
+    return SpellCheckHostImpl::FilterCustomWordResults(
+        text, GetCustomDictionary(), service_results);
+  }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  TestingProfile testing_profile_;
+  std::unique_ptr<SpellcheckService> spellcheck_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSpellCheckHostImpl);
+};
+
+// Spelling corrections of custom dictionary words should be removed from the
+// results returned by the remote Spelling service.
+TEST(SpellCheckHostImplTest, CustomSpellingResults) {
+  std::vector<SpellCheckResult> service_results;
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 0, 6,
+                                             base::ASCIIToUTF16("Hello")));
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 7, 5,
+                                             base::ASCIIToUTF16("World")));
+  TestSpellCheckHostImpl host_impl;
+  host_impl.GetCustomDictionary().AddWord("Helllo");
+  std::vector<SpellCheckResult> results =
+      host_impl.FilterCustomWordResults("Helllo Warld", service_results);
+  ASSERT_EQ(1u, results.size());
+
+  EXPECT_EQ(service_results[1].decoration, results[0].decoration);
+  EXPECT_EQ(service_results[1].location, results[0].location);
+  EXPECT_EQ(service_results[1].length, results[0].length);
+  EXPECT_EQ(service_results[1].replacement, results[0].replacement);
+}
+
+// Spelling corrections of words that are not in the custom dictionary should
+// be retained in the results returned by the remote Spelling service.
+TEST(SpellCheckHostImplTest, SpellingServiceResults) {
+  std::vector<SpellCheckResult> service_results;
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 0, 6,
+                                             base::ASCIIToUTF16("Hello")));
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 7, 5,
+                                             base::ASCIIToUTF16("World")));
+  TestSpellCheckHostImpl host_impl;
+  host_impl.GetCustomDictionary().AddWord("Hulo");
+  std::vector<SpellCheckResult> results =
+      host_impl.FilterCustomWordResults("Helllo Warld", service_results);
+  ASSERT_EQ(service_results.size(), results.size());
+
+  for (size_t i = 0; i < results.size(); ++i) {
+    EXPECT_EQ(service_results[i].decoration, results[i].decoration);
+    EXPECT_EQ(service_results[i].location, results[i].location);
+    EXPECT_EQ(service_results[i].length, results[i].length);
+    EXPECT_EQ(service_results[i].replacement, results[i].replacement);
+  }
+}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.cc b/chrome/browser/spellchecker/spellcheck_message_filter.cc
deleted file mode 100644
index 7f69a55..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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 "chrome/browser/spellchecker/spellcheck_message_filter.h"
-
-#include <algorithm>
-#include <functional>
-
-#include "base/bind.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "components/prefs/pref_service.h"
-#include "components/spellcheck/browser/spellcheck_host_metrics.h"
-#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/browser/render_process_host.h"
-#include "net/url_request/url_fetcher.h"
-
-using content::BrowserThread;
-
-SpellCheckMessageFilter::SpellCheckMessageFilter(int render_process_id)
-    : BrowserMessageFilter(SpellCheckMsgStart),
-      render_process_id_(render_process_id),
-      client_(new SpellingServiceClient) {
-}
-
-void SpellCheckMessageFilter::OverrideThreadForMessage(
-    const IPC::Message& message, BrowserThread::ID* thread) {
-  // IPC messages arrive on IO thread, but spellcheck data lives on UI thread.
-  // The message filter overrides the thread for these messages because they
-  // access spellcheck data.
-  if (message.type() == SpellCheckHostMsg_RequestDictionary::ID ||
-      message.type() == SpellCheckHostMsg_NotifyChecked::ID)
-    *thread = BrowserThread::UI;
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  if (message.type() == SpellCheckHostMsg_CallSpellingService::ID)
-    *thread = BrowserThread::UI;
-#endif
-}
-
-bool SpellCheckMessageFilter::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilter, message)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestDictionary,
-                        OnSpellCheckerRequestDictionary)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked,
-                        OnNotifyChecked)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
-                        OnCallSpellingService)
-#endif
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-SpellCheckMessageFilter::~SpellCheckMessageFilter() {}
-
-void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() {
-  content::RenderProcessHost* host =
-      content::RenderProcessHost::FromID(render_process_id_);
-  if (!host)
-    return;  // Teardown.
-  // The renderer has requested that we initialize its spellchecker. This should
-  // generally only be called once per session, as after the first call, all
-  // future renderers will be passed the initialization information on startup
-  // (or when the dictionary changes in some way).
-  SpellcheckService* spellcheck_service =
-      SpellcheckServiceFactory::GetForContext(host->GetBrowserContext());
-
-  DCHECK(spellcheck_service);
-  // The spellchecker initialization already started and finished; just send
-  // it to the renderer.
-  spellcheck_service->InitForRenderer(host);
-
-  // TODO(rlp): Ensure that we do not initialize the hunspell dictionary more
-  // than once if we get requests from different renderers.
-}
-
-void SpellCheckMessageFilter::OnNotifyChecked(const base::string16& word,
-                                              bool misspelled) {
-  SpellcheckService* spellcheck = GetSpellcheckService();
-  // Spellcheck service may not be available for a renderer process that is
-  // shutting down.
-  if (!spellcheck)
-    return;
-  if (spellcheck->GetMetrics())
-    spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
-}
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-void SpellCheckMessageFilter::OnCallSpellingService(
-    int route_id,
-    int identifier,
-    const base::string16& text) {
-  DCHECK(!text.empty());
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  CallSpellingService(text, route_id, identifier);
-}
-
-void SpellCheckMessageFilter::OnTextCheckComplete(
-    int route_id,
-    int identifier,
-    bool success,
-    const base::string16& text,
-    const std::vector<SpellCheckResult>& results) {
-  SpellcheckService* spellcheck = GetSpellcheckService();
-  // Spellcheck service may not be available for a renderer process that is
-  // shutting down.
-  if (!spellcheck)
-    return;
-  std::vector<SpellCheckResult> results_copy = results;
-
-  // Erase custom dictionary words from the spellcheck results and record
-  // in-dictionary feedback.
-  std::vector<SpellCheckResult>::iterator write_iter;
-  std::vector<SpellCheckResult>::iterator iter;
-  std::string text_copy = base::UTF16ToUTF8(text);
-  for (iter = write_iter = results_copy.begin();
-       iter != results_copy.end();
-       ++iter) {
-    if (!spellcheck->GetCustomDictionary()->HasWord(
-            text_copy.substr(iter->location, iter->length))) {
-      if (write_iter != iter)
-        *write_iter = *iter;
-      ++write_iter;
-    }
-  }
-  results_copy.erase(write_iter, results_copy.end());
-
-  Send(new SpellCheckMsg_RespondSpellingService(
-      route_id, identifier, success, text, results_copy));
-}
-
-// CallSpellingService always executes the callback OnTextCheckComplete.
-// (Which, in turn, sends a SpellCheckMsg_RespondSpellingService)
-void SpellCheckMessageFilter::CallSpellingService(const base::string16& text,
-                                                  int route_id,
-                                                  int identifier) {
-  content::RenderProcessHost* host =
-      content::RenderProcessHost::FromID(render_process_id_);
-
-  client_->RequestTextCheck(
-      host ? host->GetBrowserContext() : NULL,
-      SpellingServiceClient::SPELLCHECK, text,
-      base::Bind(&SpellCheckMessageFilter::OnTextCheckComplete,
-                 base::Unretained(this), route_id, identifier));
-}
-#endif
-
-SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const {
-  return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
-}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.h b/chrome/browser/spellchecker/spellcheck_message_filter.h
deleted file mode 100644
index 8f62b1e5..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.
-
-#ifndef CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
-#define CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/browser/browser_message_filter.h"
-
-class SpellcheckService;
-struct SpellCheckResult;
-
-// A message filter implementation that receives spell checker requests from
-// SpellCheckProvider.
-class SpellCheckMessageFilter : public content::BrowserMessageFilter {
- public:
-  explicit SpellCheckMessageFilter(int render_process_id);
-
-  // content::BrowserMessageFilter implementation.
-  void OverrideThreadForMessage(const IPC::Message& message,
-                                content::BrowserThread::ID* thread) override;
-  bool OnMessageReceived(const IPC::Message& message) override;
-
- private:
-  friend class TestingSpellCheckMessageFilter;
-
-  ~SpellCheckMessageFilter() override;
-
-  void OnSpellCheckerRequestDictionary();
-  void OnNotifyChecked(const base::string16& word, bool misspelled);
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  void OnCallSpellingService(int route_id,
-                             int identifier,
-                             const base::string16& text);
-
-  // A callback function called when the Spelling service finishes checking
-  // text. Sends the given results to a renderer.
-  void OnTextCheckComplete(
-      int route_id,
-      int identifier,
-      bool success,
-      const base::string16& text,
-      const std::vector<SpellCheckResult>& results);
-
-  // Checks the user profile and sends a JSON-RPC request to the Spelling
-  // service if a user enables the "Ask Google for suggestions" option. When we
-  // receive a response (including an error) from the service, it calls
-  // OnTextCheckComplete. When this function is called before we receive a
-  // response for the previous request, this function cancels the previous
-  // request and sends a new one.
-  void CallSpellingService(const base::string16& text,
-                           int route_id,
-                           int identifier);
-#endif
-
-  // Can be overridden for testing.
-  virtual SpellcheckService* GetSpellcheckService() const;
-
-  int render_process_id_;
-
-  // A JSON-RPC client that calls the Spelling service in the background.
-  std::unique_ptr<SpellingServiceClient> client_;
-};
-
-#endif  // CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
deleted file mode 100644
index 62d445f9..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) 2013 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 <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <tuple>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "ipc/ipc_message.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class TestingSpellCheckMessageFilter : public SpellCheckMessageFilter {
- public:
-  TestingSpellCheckMessageFilter()
-      : SpellCheckMessageFilter(0),
-        spellcheck_(new SpellcheckService(&profile_)) {}
-
-  bool Send(IPC::Message* message) override {
-    sent_messages.push_back(base::WrapUnique(message));
-    return true;
-  }
-
-  SpellcheckService* GetSpellcheckService() const override {
-    return spellcheck_.get();
-  }
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  void OnTextCheckComplete(int route_id,
-                           int identifier,
-                           bool success,
-                           const base::string16& text,
-                           const std::vector<SpellCheckResult>& results) {
-    SpellCheckMessageFilter::OnTextCheckComplete(route_id, identifier, success,
-                                                 text, results);
-  }
-#endif
-
-  std::vector<std::unique_ptr<IPC::Message>> sent_messages;
-
- private:
-  ~TestingSpellCheckMessageFilter() override {}
-
-  content::TestBrowserThreadBundle thread_bundle_;
-  TestingProfile profile_;
-  std::unique_ptr<SpellcheckService> spellcheck_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingSpellCheckMessageFilter);
-};
-
-TEST(SpellCheckMessageFilterTest, TestOverrideThread) {
-  static const uint32_t kSpellcheckMessages[] = {
-    SpellCheckHostMsg_RequestDictionary::ID,
-    SpellCheckHostMsg_NotifyChecked::ID,
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    SpellCheckHostMsg_CallSpellingService::ID,
-#endif
-  };
-  content::BrowserThread::ID thread;
-  IPC::Message message;
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  for (size_t i = 0; i < arraysize(kSpellcheckMessages); ++i) {
-    message.SetHeaderValues(
-        0, kSpellcheckMessages[i], IPC::Message::PRIORITY_NORMAL);
-    thread = content::BrowserThread::IO;
-    filter->OverrideThreadForMessage(message, &thread);
-    EXPECT_EQ(content::BrowserThread::UI, thread);
-  }
-}
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTestCustomDictionary) {
-  static const std::string kCustomWord = "Helllo";
-  static const int kRouteId = 0;
-  static const int kCallbackId = 0;
-  static const base::string16 kText = base::ASCIIToUTF16("Helllo warld.");
-  static const bool kSuccess = true;
-  static const SpellCheckResult::Decoration kDecoration =
-      SpellCheckResult::SPELLING;
-  static const int kLocation = 7;
-  static const int kLength = 5;
-  static const base::string16 kReplacement = base::ASCIIToUTF16("world");
-
-  std::vector<SpellCheckResult> results;
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 0, 6, base::ASCIIToUTF16("Hello")));
-  results.push_back(
-      SpellCheckResult(kDecoration, kLocation, kLength, kReplacement));
-
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  filter->GetSpellcheckService()->GetCustomDictionary()->AddWord(kCustomWord);
-  filter->OnTextCheckComplete(kRouteId, kCallbackId, kSuccess, kText, results);
-  ASSERT_EQ(1U, filter->sent_messages.size());
-
-  SpellCheckMsg_RespondSpellingService::Param params;
-  bool ok = SpellCheckMsg_RespondSpellingService::Read(
-      filter->sent_messages[0].get(), &params);
-  int sent_identifier = std::get<0>(params);
-  bool sent_success = std::get<1>(params);
-  base::string16 sent_text = std::get<2>(params);
-  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
-  EXPECT_TRUE(ok);
-  EXPECT_EQ(kCallbackId, sent_identifier);
-  EXPECT_EQ(kSuccess, sent_success);
-  EXPECT_EQ(kText, sent_text);
-  ASSERT_EQ(1U, sent_results.size());
-  EXPECT_EQ(kDecoration, sent_results[0].decoration);
-  EXPECT_EQ(kLocation, sent_results[0].location);
-  EXPECT_EQ(kLength, sent_results[0].length);
-  EXPECT_EQ(kReplacement, sent_results[0].replacement);
-}
-
-TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTest) {
-  std::vector<SpellCheckResult> results;
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 0, 6, base::ASCIIToUTF16("Hello")));
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 7, 7, base::ASCIIToUTF16("world")));
-
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  filter->OnTextCheckComplete(1, 1, true, base::ASCIIToUTF16("Helllo walrd"),
-                              results);
-  ASSERT_EQ(1U, filter->sent_messages.size());
-
-  SpellCheckMsg_RespondSpellingService::Param params;
-  bool ok = SpellCheckMsg_RespondSpellingService::Read(
-      filter->sent_messages[0].get(), &params);
-  EXPECT_TRUE(ok);
-
-  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
-  EXPECT_EQ(2U, sent_results.size());
-}
-#endif
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index 5abe4b7..2afbf90 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -4,54 +4,40 @@
 
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 
-#include <stddef.h>
-#include <stdint.h>
 #include <string>
-#include <tuple>
 #include <vector>
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/histogram_tester.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_common.h"
+#include "components/spellcheck/common/spellcheck_result.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_utils.h"
-#include "url/gurl.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
 
 using content::BrowserContext;
-
-namespace {
-
-// A corrupted BDICT data used in DeleteCorruptedBDICT. Please do not use this
-// BDICT data for other tests.
-const uint8_t kCorruptedBDICT[] = {
-    0x42, 0x44, 0x69, 0x63, 0x02, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
-    0x3b, 0x00, 0x00, 0x00, 0x65, 0x72, 0xe0, 0xac, 0x27, 0xc7, 0xda, 0x66,
-    0x6d, 0x1e, 0xa6, 0x35, 0xd1, 0xf6, 0xb7, 0x35, 0x32, 0x00, 0x00, 0x00,
-    0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
-    0x0a, 0x0a, 0x41, 0x46, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6,
-    0x49, 0x00, 0x68, 0x02, 0x73, 0x06, 0x74, 0x0b, 0x77, 0x11, 0x79, 0x15,
-};
-
-}  // namespace
+using content::RenderProcessHost;
 
 class SpellcheckServiceBrowserTest : public InProcessBrowserTest,
                                      public spellcheck::mojom::SpellChecker {
@@ -69,13 +55,13 @@
     renderer_.reset();
   }
 
-  BrowserContext* GetContext() {
+  RenderProcessHost* GetRenderer() const { return renderer_.get(); }
+
+  BrowserContext* GetContext() const {
     return static_cast<BrowserContext*>(browser()->profile());
   }
 
-  PrefService* GetPrefs() {
-    return prefs_;
-  }
+  PrefService* GetPrefs() const { return prefs_; }
 
   void InitSpellcheck(bool enable_spellcheck,
                       const std::string& single_dictionary,
@@ -208,6 +194,11 @@
     EXPECT_EQ(2u, words_added.size());
   }
 
+ protected:
+  // Quits the RunLoop on Mojo request flow completion.
+  base::OnceClosure quit_;
+
+ private:
   // Mocked RenderProcessHost.
   std::unique_ptr<content::MockRenderProcessHost> renderer_;
 
@@ -217,9 +208,6 @@
   // Binding to receive the SpellChecker request flow.
   mojo::Binding<spellcheck::mojom::SpellChecker> binding_;
 
-  // Quits the RunLoop on SpellChecker request flow completion.
-  base::OnceClosure quit_;
-
   // Used to verify the SpellChecker request flow.
   bool bound_connection_closed_;
   bool custom_dictionary_changed_called_;
@@ -229,6 +217,64 @@
   DISALLOW_COPY_AND_ASSIGN(SpellcheckServiceBrowserTest);
 };
 
+class SpellcheckServiceHostBrowserTest : public SpellcheckServiceBrowserTest {
+ public:
+  SpellcheckServiceHostBrowserTest() = default;
+
+  void RequestDictionary() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    interface->RequestDictionary();
+  }
+
+  void NotifyChecked() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    const bool misspelt = true;
+    base::UTF8ToUTF16("hallo", 5, &word_);
+    interface->NotifyChecked(word_, misspelt);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void CallSpellingService() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    base::UTF8ToUTF16("hello", 5, &word_);
+    interface->CallSpellingService(
+        word_,
+        base::Bind(&SpellcheckServiceHostBrowserTest::SpellingServiceDone,
+                   base::Unretained(this)));
+
+    base::RunLoop run_loop;
+    quit_ = run_loop.QuitClosure();
+    run_loop.Run();
+
+    EXPECT_TRUE(spelling_service_done_called_);
+  }
+
+ private:
+  void RequestSpellCheckHost(spellcheck::mojom::SpellCheckHostPtr* interface) {
+    SpellCheckHostImpl::Create(GetRenderer()->GetID(),
+                               service_manager::BindSourceInfo(),
+                               mojo::MakeRequest(interface));
+  }
+
+  void SpellingServiceDone(bool success,
+                           const std::vector<::SpellCheckResult>& results) {
+    spelling_service_done_called_ = true;
+    if (quit_)
+      std::move(quit_).Run();
+  }
+
+  bool spelling_service_done_called_ = false;
+  base::string16 word_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpellcheckServiceHostBrowserTest);
+};
+
 // Removing a spellcheck language from accept languages should remove it from
 // spellcheck languages list as well.
 IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
@@ -334,9 +380,49 @@
   EXPECT_TRUE(GetCustomDictionaryChangedState());
 }
 
+// Starting with only a single-language spellcheck setting, the host should
+// initialize the renderer's spellcheck system, and the same if the renderer
+// explicity requests the spellcheck dictionaries.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, RequestDictionary) {
+  InitSpellcheck(true, "en-US", "");
+  EXPECT_TRUE(GetEnableSpellcheckState());
+
+  RequestDictionary();
+  EXPECT_TRUE(GetEnableSpellcheckState());
+}
+
+// When the renderer notifies that it corrected a word, the render process
+// host should record UMA stats about the correction.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, NotifyChecked) {
+  const char kMisspellRatio[] = "SpellCheck.MisspellRatio";
+
+  base::HistogramTester tester;
+  tester.ExpectTotalCount(kMisspellRatio, 0);
+  NotifyChecked();
+  tester.ExpectTotalCount(kMisspellRatio, 1);
+}
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+// When the renderer requests the spelling service for correcting text, the
+// render process host should call the remote spelling service.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, CallSpellingService) {
+  CallSpellingService();
+}
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
 // Tests that we can delete a corrupted BDICT file used by hunspell. We do not
 // run this test on Mac because Mac does not use hunspell by default.
 IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, DeleteCorruptedBDICT) {
+  // Corrupted BDICT data: please do not use this BDICT data for other tests.
+  const uint8_t kCorruptedBDICT[] = {
+      0x42, 0x44, 0x69, 0x63, 0x02, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
+      0x3b, 0x00, 0x00, 0x00, 0x65, 0x72, 0xe0, 0xac, 0x27, 0xc7, 0xda, 0x66,
+      0x6d, 0x1e, 0xa6, 0x35, 0xd1, 0xf6, 0xb7, 0x35, 0x32, 0x00, 0x00, 0x00,
+      0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+      0x0a, 0x0a, 0x41, 0x46, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6,
+      0x49, 0x00, 0x68, 0x02, 0x73, 0x06, 0x74, 0x0b, 0x77, 0x11, 0x79, 0x15,
+  };
+
   // Write the corrupted BDICT data to create a corrupted BDICT file.
   base::FilePath dict_dir;
   ASSERT_TRUE(PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir));