• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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