blob: 80f012d7763565a81761dfa27c761608dc72be24 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
6
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01007#include <algorithm>
8#include <functional>
9
Torne (Richard Coles)58218062012-11-14 11:43:16 +000010#include "base/bind.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000011#include "base/prefs/pref_service.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010012#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000013#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/spellchecker/spellcheck_factory.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "chrome/browser/spellchecker/spellcheck_service.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000017#include "chrome/browser/spellchecker/spelling_service_client.h"
18#include "chrome/common/pref_names.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010019#include "chrome/common/spellcheck_marker.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000020#include "chrome/common/spellcheck_messages.h"
21#include "content/public/browser/render_process_host.h"
22#include "net/url_request/url_fetcher.h"
23
24using content::BrowserThread;
25
26SpellCheckMessageFilter::SpellCheckMessageFilter(int render_process_id)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000027 : render_process_id_(render_process_id),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010028 client_(new SpellingServiceClient) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000029}
30
31void SpellCheckMessageFilter::OverrideThreadForMessage(
32 const IPC::Message& message, BrowserThread::ID* thread) {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +010033 // IPC messages arrive on IO thread, but spellcheck data lives on UI thread.
34 // The message filter overrides the thread for these messages because they
35 // access spellcheck data.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000036 if (message.type() == SpellCheckHostMsg_RequestDictionary::ID ||
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +010037 message.type() == SpellCheckHostMsg_NotifyChecked::ID ||
38 message.type() == SpellCheckHostMsg_RespondDocumentMarkers::ID)
Torne (Richard Coles)58218062012-11-14 11:43:16 +000039 *thread = BrowserThread::UI;
40#if !defined(OS_MACOSX)
41 if (message.type() == SpellCheckHostMsg_CallSpellingService::ID)
42 *thread = BrowserThread::UI;
43#endif
44}
45
46bool SpellCheckMessageFilter::OnMessageReceived(const IPC::Message& message,
47 bool* message_was_ok) {
48 bool handled = true;
49 IPC_BEGIN_MESSAGE_MAP_EX(SpellCheckMessageFilter, message, *message_was_ok)
50 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestDictionary,
51 OnSpellCheckerRequestDictionary)
52 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked,
53 OnNotifyChecked)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010054 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RespondDocumentMarkers,
55 OnRespondDocumentMarkers)
Torne (Richard Coles)58218062012-11-14 11:43:16 +000056#if !defined(OS_MACOSX)
57 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
58 OnCallSpellingService)
59#endif
60 IPC_MESSAGE_UNHANDLED(handled = false)
61 IPC_END_MESSAGE_MAP()
62 return handled;
63}
64
65SpellCheckMessageFilter::~SpellCheckMessageFilter() {}
66
67void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() {
68 content::RenderProcessHost* host =
69 content::RenderProcessHost::FromID(render_process_id_);
70 if (!host)
71 return; // Teardown.
72 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
73 // The renderer has requested that we initialize its spellchecker. This should
74 // generally only be called once per session, as after the first call, all
75 // future renderers will be passed the initialization information on startup
76 // (or when the dictionary changes in some way).
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000077 SpellcheckService* spellcheck_service =
78 SpellcheckServiceFactory::GetForProfile(profile);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000080 DCHECK(spellcheck_service);
81 // The spellchecker initialization already started and finished; just send
82 // it to the renderer.
83 spellcheck_service->InitForRenderer(host);
84
85 // TODO(rlp): Ensure that we do not initialize the hunspell dictionary more
86 // than once if we get requests from different renderers.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000087}
88
89void SpellCheckMessageFilter::OnNotifyChecked(const string16& word,
90 bool misspelled) {
Ben Murdocheb525c52013-07-10 11:40:50 +010091 SpellcheckService* spellcheck = GetSpellcheckService();
92 // Spellcheck service may not be available for a renderer process that is
93 // shutting down.
94 if (!spellcheck)
95 return;
96 if (spellcheck->GetMetrics())
97 spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000098}
99
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100100void SpellCheckMessageFilter::OnRespondDocumentMarkers(
101 const std::vector<uint32>& markers) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100102 SpellcheckService* spellcheck = GetSpellcheckService();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100103 // Spellcheck service may not be available for a renderer process that is
104 // shutting down.
105 if (!spellcheck)
106 return;
107 spellcheck->GetFeedbackSender()->OnReceiveDocumentMarkers(
108 render_process_id_, markers);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100109}
110
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000111#if !defined(OS_MACOSX)
112void SpellCheckMessageFilter::OnCallSpellingService(
113 int route_id,
114 int identifier,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100115 const string16& text,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100116 std::vector<SpellCheckMarker> markers) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000117 DCHECK(!text.empty());
118 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100119 // Erase invalid markers (with offsets out of boundaries of text length).
120 markers.erase(
121 std::remove_if(
122 markers.begin(),
123 markers.end(),
124 std::not1(SpellCheckMarker::IsValidPredicate(text.length()))),
125 markers.end());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100126 CallSpellingService(text, route_id, identifier, markers);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000127}
128
129void SpellCheckMessageFilter::OnTextCheckComplete(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100130 int route_id,
131 int identifier,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100132 const std::vector<SpellCheckMarker>& markers,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000133 bool success,
134 const string16& text,
135 const std::vector<SpellCheckResult>& results) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100136 SpellcheckService* spellcheck = GetSpellcheckService();
137 // Spellcheck service may not be available for a renderer process that is
138 // shutting down.
139 if (!spellcheck)
140 return;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100141 std::vector<SpellCheckResult> results_copy = results;
142 spellcheck->GetFeedbackSender()->OnSpellcheckResults(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100143 render_process_id_, text, markers, &results_copy);
Ben Murdocheb525c52013-07-10 11:40:50 +0100144
145 // Erase custom dictionary words from the spellcheck results and record
146 // in-dictionary feedback.
147 std::vector<SpellCheckResult>::iterator write_iter;
148 std::vector<SpellCheckResult>::iterator iter;
149 std::string text_copy = UTF16ToUTF8(text);
150 for (iter = write_iter = results_copy.begin();
151 iter != results_copy.end();
152 ++iter) {
153 if (spellcheck->GetCustomDictionary()->HasWord(
154 text_copy.substr(iter->location, iter->length))) {
155 spellcheck->GetFeedbackSender()->RecordInDictionary(iter->hash);
156 } else {
157 if (write_iter != iter)
158 *write_iter = *iter;
159 ++write_iter;
160 }
161 }
162 results_copy.erase(write_iter, results_copy.end());
163
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100164 Send(new SpellCheckMsg_RespondSpellingService(
165 route_id, identifier, success, text, results_copy));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000166}
167
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000168// CallSpellingService always executes the callback OnTextCheckComplete.
169// (Which, in turn, sends a SpellCheckMsg_RespondSpellingService)
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100170void SpellCheckMessageFilter::CallSpellingService(
171 const string16& text,
172 int route_id,
173 int identifier,
174 const std::vector<SpellCheckMarker>& markers) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000175 Profile* profile = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000176 content::RenderProcessHost* host =
177 content::RenderProcessHost::FromID(render_process_id_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000178 if (host)
179 profile = Profile::FromBrowserContext(host->GetBrowserContext());
180
181 client_->RequestTextCheck(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100182 profile,
183 SpellingServiceClient::SPELLCHECK,
184 text,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000185 base::Bind(&SpellCheckMessageFilter::OnTextCheckComplete,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100186 base::Unretained(this),
187 route_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100188 identifier,
189 markers));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000190}
191#endif
Ben Murdocheb525c52013-07-10 11:40:50 +0100192
193SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const {
194 return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
195}