1 // 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
7 #include <algorithm>
8 #include <functional>
9
10 #include "base/bind.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/spellchecker/spellcheck_factory.h"
14 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
15 #include "chrome/browser/spellchecker/spellcheck_service.h"
16 #include "chrome/browser/spellchecker/spelling_service_client.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/common/spellcheck_marker.h"
19 #include "chrome/common/spellcheck_messages.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "net/url_request/url_fetcher.h"
22
23 using content::BrowserThread;
24
SpellCheckMessageFilter(int render_process_id)25 SpellCheckMessageFilter::SpellCheckMessageFilter(int render_process_id)
26 : BrowserMessageFilter(SpellCheckMsgStart),
27 render_process_id_(render_process_id),
28 client_(new SpellingServiceClient) {
29 }
30
OverrideThreadForMessage(const IPC::Message & message,BrowserThread::ID * thread)31 void SpellCheckMessageFilter::OverrideThreadForMessage(
32 const IPC::Message& message, BrowserThread::ID* thread) {
33 // 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.
36 if (message.type() == SpellCheckHostMsg_RequestDictionary::ID ||
37 message.type() == SpellCheckHostMsg_NotifyChecked::ID ||
38 message.type() == SpellCheckHostMsg_RespondDocumentMarkers::ID)
39 *thread = BrowserThread::UI;
40 #if !defined(OS_MACOSX)
41 if (message.type() == SpellCheckHostMsg_CallSpellingService::ID)
42 *thread = BrowserThread::UI;
43 #endif
44 }
45
OnMessageReceived(const IPC::Message & message)46 bool SpellCheckMessageFilter::OnMessageReceived(const IPC::Message& message) {
47 bool handled = true;
48 IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilter, message)
49 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestDictionary,
50 OnSpellCheckerRequestDictionary)
51 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked,
52 OnNotifyChecked)
53 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RespondDocumentMarkers,
54 OnRespondDocumentMarkers)
55 #if !defined(OS_MACOSX)
56 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
57 OnCallSpellingService)
58 #endif
59 IPC_MESSAGE_UNHANDLED(handled = false)
60 IPC_END_MESSAGE_MAP()
61 return handled;
62 }
63
~SpellCheckMessageFilter()64 SpellCheckMessageFilter::~SpellCheckMessageFilter() {}
65
OnSpellCheckerRequestDictionary()66 void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() {
67 content::RenderProcessHost* host =
68 content::RenderProcessHost::FromID(render_process_id_);
69 if (!host)
70 return; // Teardown.
71 // The renderer has requested that we initialize its spellchecker. This should
72 // generally only be called once per session, as after the first call, all
73 // future renderers will be passed the initialization information on startup
74 // (or when the dictionary changes in some way).
75 SpellcheckService* spellcheck_service =
76 SpellcheckServiceFactory::GetForContext(host->GetBrowserContext());
77
78 DCHECK(spellcheck_service);
79 // The spellchecker initialization already started and finished; just send
80 // it to the renderer.
81 spellcheck_service->InitForRenderer(host);
82
83 // TODO(rlp): Ensure that we do not initialize the hunspell dictionary more
84 // than once if we get requests from different renderers.
85 }
86
OnNotifyChecked(const base::string16 & word,bool misspelled)87 void SpellCheckMessageFilter::OnNotifyChecked(const base::string16& word,
88 bool misspelled) {
89 SpellcheckService* spellcheck = GetSpellcheckService();
90 // Spellcheck service may not be available for a renderer process that is
91 // shutting down.
92 if (!spellcheck)
93 return;
94 if (spellcheck->GetMetrics())
95 spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
96 }
97
OnRespondDocumentMarkers(const std::vector<uint32> & markers)98 void SpellCheckMessageFilter::OnRespondDocumentMarkers(
99 const std::vector<uint32>& markers) {
100 SpellcheckService* spellcheck = GetSpellcheckService();
101 // Spellcheck service may not be available for a renderer process that is
102 // shutting down.
103 if (!spellcheck)
104 return;
105 spellcheck->GetFeedbackSender()->OnReceiveDocumentMarkers(
106 render_process_id_, markers);
107 }
108
109 #if !defined(OS_MACOSX)
OnCallSpellingService(int route_id,int identifier,const base::string16 & text,std::vector<SpellCheckMarker> markers)110 void SpellCheckMessageFilter::OnCallSpellingService(
111 int route_id,
112 int identifier,
113 const base::string16& text,
114 std::vector<SpellCheckMarker> markers) {
115 DCHECK(!text.empty());
116 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
117 // Erase invalid markers (with offsets out of boundaries of text length).
118 markers.erase(
119 std::remove_if(
120 markers.begin(),
121 markers.end(),
122 std::not1(SpellCheckMarker::IsValidPredicate(text.length()))),
123 markers.end());
124 CallSpellingService(text, route_id, identifier, markers);
125 }
126
OnTextCheckComplete(int route_id,int identifier,const std::vector<SpellCheckMarker> & markers,bool success,const base::string16 & text,const std::vector<SpellCheckResult> & results)127 void SpellCheckMessageFilter::OnTextCheckComplete(
128 int route_id,
129 int identifier,
130 const std::vector<SpellCheckMarker>& markers,
131 bool success,
132 const base::string16& text,
133 const std::vector<SpellCheckResult>& results) {
134 SpellcheckService* spellcheck = GetSpellcheckService();
135 // Spellcheck service may not be available for a renderer process that is
136 // shutting down.
137 if (!spellcheck)
138 return;
139 std::vector<SpellCheckResult> results_copy = results;
140 spellcheck->GetFeedbackSender()->OnSpellcheckResults(
141 render_process_id_, text, markers, &results_copy);
142
143 // Erase custom dictionary words from the spellcheck results and record
144 // in-dictionary feedback.
145 std::vector<SpellCheckResult>::iterator write_iter;
146 std::vector<SpellCheckResult>::iterator iter;
147 std::string text_copy = base::UTF16ToUTF8(text);
148 for (iter = write_iter = results_copy.begin();
149 iter != results_copy.end();
150 ++iter) {
151 if (spellcheck->GetCustomDictionary()->HasWord(
152 text_copy.substr(iter->location, iter->length))) {
153 spellcheck->GetFeedbackSender()->RecordInDictionary(iter->hash);
154 } else {
155 if (write_iter != iter)
156 *write_iter = *iter;
157 ++write_iter;
158 }
159 }
160 results_copy.erase(write_iter, results_copy.end());
161
162 Send(new SpellCheckMsg_RespondSpellingService(
163 route_id, identifier, success, text, results_copy));
164 }
165
166 // CallSpellingService always executes the callback OnTextCheckComplete.
167 // (Which, in turn, sends a SpellCheckMsg_RespondSpellingService)
CallSpellingService(const base::string16 & text,int route_id,int identifier,const std::vector<SpellCheckMarker> & markers)168 void SpellCheckMessageFilter::CallSpellingService(
169 const base::string16& text,
170 int route_id,
171 int identifier,
172 const std::vector<SpellCheckMarker>& markers) {
173 content::RenderProcessHost* host =
174 content::RenderProcessHost::FromID(render_process_id_);
175
176 client_->RequestTextCheck(
177 host ? host->GetBrowserContext() : NULL,
178 SpellingServiceClient::SPELLCHECK,
179 text,
180 base::Bind(&SpellCheckMessageFilter::OnTextCheckComplete,
181 base::Unretained(this),
182 route_id,
183 identifier,
184 markers));
185 }
186 #endif
187
GetSpellcheckService() const188 SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const {
189 return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
190 }
191