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_mac.h"
6
7 #include <algorithm>
8 #include <functional>
9
10 #include "base/barrier_closure.h"
11 #include "base/bind.h"
12 #include "chrome/browser/spellchecker/spellcheck_factory.h"
13 #include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
14 #include "chrome/browser/spellchecker/spellcheck_service.h"
15 #include "chrome/browser/spellchecker/spelling_service_client.h"
16 #include "chrome/common/spellcheck_messages.h"
17 #include "chrome/common/spellcheck_result.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/render_process_host.h"
20
21 using content::BrowserThread;
22 using content::BrowserContext;
23
24 namespace {
25
CompareLocation(const SpellCheckResult & r1,const SpellCheckResult & r2)26 bool CompareLocation(const SpellCheckResult& r1,
27 const SpellCheckResult& r2) {
28 return r1.location < r2.location;
29 }
30
31 } // namespace
32
33 class SpellingRequest {
34 public:
35 SpellingRequest(SpellingServiceClient* client,
36 content::BrowserMessageFilter* destination,
37 int render_process_id);
38
39 void RequestCheck(const base::string16& text,
40 int route_id,
41 int identifier,
42 int document_tag,
43 const std::vector<SpellCheckMarker>& markers);
44 private:
45 // Request server-side checking.
46 void RequestRemoteCheck(const base::string16& text);
47
48 // Request a check from local spell checker.
49 void RequestLocalCheck(const base::string16& text, int document_tag);
50
51 // Check if all pending requests are done, send reply to render process if so.
52 void OnCheckCompleted();
53
54 // Called when server-side checking is complete.
55 void OnRemoteCheckCompleted(bool success,
56 const base::string16& text,
57 const std::vector<SpellCheckResult>& results);
58
59 // Called when local checking is complete.
60 void OnLocalCheckCompleted(const std::vector<SpellCheckResult>& results);
61
62 std::vector<SpellCheckResult> local_results_;
63 std::vector<SpellCheckResult> remote_results_;
64
65 // Barrier closure for completion of both remote and local check.
66 base::Closure completion_barrier_;
67 bool remote_success_;
68
69 SpellingServiceClient* client_; // Owned by |destination|.
70 content::BrowserMessageFilter* destination_; // ref-counted.
71 int render_process_id_;
72
73 int route_id_;
74 int identifier_;
75 int document_tag_;
76 std::vector<SpellCheckMarker> markers_;
77 };
78
SpellingRequest(SpellingServiceClient * client,content::BrowserMessageFilter * destination,int render_process_id)79 SpellingRequest::SpellingRequest(SpellingServiceClient* client,
80 content::BrowserMessageFilter* destination,
81 int render_process_id)
82 : remote_success_(false),
83 client_(client),
84 destination_(destination),
85 render_process_id_(render_process_id),
86 route_id_(-1),
87 identifier_(-1),
88 document_tag_(-1) {
89 destination_->AddRef();
90 }
91
RequestCheck(const base::string16 & text,int route_id,int identifier,int document_tag,const std::vector<SpellCheckMarker> & markers)92 void SpellingRequest::RequestCheck(
93 const base::string16& text,
94 int route_id,
95 int identifier,
96 int document_tag,
97 const std::vector<SpellCheckMarker>& markers) {
98 DCHECK(!text.empty());
99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
100
101 route_id_ = route_id;
102 identifier_ = identifier;
103 document_tag_ = document_tag;
104 markers_ = markers;
105
106 // Send the remote query out. The barrier owns |this|, ensuring it is deleted
107 // after completion.
108 completion_barrier_ =
109 BarrierClosure(2,
110 base::Bind(&SpellingRequest::OnCheckCompleted,
111 base::Owned(this)));
112 RequestRemoteCheck(text);
113 RequestLocalCheck(text, document_tag_);
114 }
115
RequestRemoteCheck(const base::string16 & text)116 void SpellingRequest::RequestRemoteCheck(const base::string16& text) {
117 BrowserContext* context = NULL;
118 content::RenderProcessHost* host =
119 content::RenderProcessHost::FromID(render_process_id_);
120 if (host)
121 context = host->GetBrowserContext();
122
123 client_->RequestTextCheck(
124 context,
125 SpellingServiceClient::SPELLCHECK,
126 text,
127 base::Bind(&SpellingRequest::OnRemoteCheckCompleted,
128 base::Unretained(this)));
129 }
130
RequestLocalCheck(const base::string16 & text,int document_tag)131 void SpellingRequest::RequestLocalCheck(const base::string16& text,
132 int document_tag) {
133 spellcheck_mac::RequestTextCheck(
134 document_tag,
135 text,
136 base::Bind(&SpellingRequest::OnLocalCheckCompleted,
137 base::Unretained(this)));
138 }
139
OnCheckCompleted()140 void SpellingRequest::OnCheckCompleted() {
141 // Final completion can happen on any thread - don't DCHECK thread.
142 const std::vector<SpellCheckResult>* check_results = &local_results_;
143 if (remote_success_) {
144 std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
145 std::sort(local_results_.begin(), local_results_.end(), CompareLocation);
146 SpellCheckMessageFilterMac::CombineResults(&remote_results_,
147 local_results_);
148 check_results = &remote_results_;
149 }
150
151 destination_->Send(
152 new SpellCheckMsg_RespondTextCheck(
153 route_id_,
154 identifier_,
155 *check_results));
156 destination_->Release();
157
158 // Object is self-managed - at this point, its life span is over.
159 // No need to delete, since the OnCheckCompleted callback owns |this|.
160 }
161
OnRemoteCheckCompleted(bool success,const base::string16 & text,const std::vector<SpellCheckResult> & results)162 void SpellingRequest::OnRemoteCheckCompleted(
163 bool success,
164 const base::string16& text,
165 const std::vector<SpellCheckResult>& results) {
166 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
167 remote_success_ = success;
168 remote_results_ = results;
169
170 SpellcheckService* spellcheck_service =
171 SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
172 if (spellcheck_service) {
173 spellcheck_service->GetFeedbackSender()->OnSpellcheckResults(
174 render_process_id_,
175 text,
176 markers_,
177 &remote_results_);
178 }
179
180 completion_barrier_.Run();
181 }
182
OnLocalCheckCompleted(const std::vector<SpellCheckResult> & results)183 void SpellingRequest::OnLocalCheckCompleted(
184 const std::vector<SpellCheckResult>& results) {
185 // Local checking can happen on any thread - don't DCHECK thread.
186 local_results_ = results;
187 completion_barrier_.Run();
188 }
189
190
SpellCheckMessageFilterMac(int render_process_id)191 SpellCheckMessageFilterMac::SpellCheckMessageFilterMac(int render_process_id)
192 : BrowserMessageFilter(SpellCheckMsgStart),
193 render_process_id_(render_process_id),
194 client_(new SpellingServiceClient) {
195 }
196
OverrideThreadForMessage(const IPC::Message & message,BrowserThread::ID * thread)197 void SpellCheckMessageFilterMac::OverrideThreadForMessage(
198 const IPC::Message& message, BrowserThread::ID* thread) {
199 if (message.type() == SpellCheckHostMsg_RequestTextCheck::ID)
200 *thread = BrowserThread::UI;
201 }
202
OnMessageReceived(const IPC::Message & message)203 bool SpellCheckMessageFilterMac::OnMessageReceived(
204 const IPC::Message& message) {
205 bool handled = true;
206 IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilterMac, message)
207 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CheckSpelling,
208 OnCheckSpelling)
209 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_FillSuggestionList,
210 OnFillSuggestionList)
211 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_ShowSpellingPanel,
212 OnShowSpellingPanel)
213 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord,
214 OnUpdateSpellingPanelWithMisspelledWord)
215 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck,
216 OnRequestTextCheck)
217 IPC_MESSAGE_UNHANDLED(handled = false)
218 IPC_END_MESSAGE_MAP()
219 return handled;
220 }
221
222 // static
CombineResults(std::vector<SpellCheckResult> * remote_results,const std::vector<SpellCheckResult> & local_results)223 void SpellCheckMessageFilterMac::CombineResults(
224 std::vector<SpellCheckResult>* remote_results,
225 const std::vector<SpellCheckResult>& local_results) {
226 std::vector<SpellCheckResult>::const_iterator local_iter(
227 local_results.begin());
228 std::vector<SpellCheckResult>::iterator remote_iter;
229
230 for (remote_iter = remote_results->begin();
231 remote_iter != remote_results->end();
232 ++remote_iter) {
233 // Discard all local results occurring before remote result.
234 while (local_iter != local_results.end() &&
235 local_iter->location < remote_iter->location) {
236 local_iter++;
237 }
238
239 // Unless local and remote result coincide, result is GRAMMAR.
240 remote_iter->decoration = SpellCheckResult::GRAMMAR;
241 if (local_iter != local_results.end() &&
242 local_iter->location == remote_iter->location &&
243 local_iter->length == remote_iter->length) {
244 remote_iter->decoration = SpellCheckResult::SPELLING;
245 }
246 }
247 }
248
~SpellCheckMessageFilterMac()249 SpellCheckMessageFilterMac::~SpellCheckMessageFilterMac() {}
250
OnCheckSpelling(const base::string16 & word,int route_id,bool * correct)251 void SpellCheckMessageFilterMac::OnCheckSpelling(const base::string16& word,
252 int route_id,
253 bool* correct) {
254 *correct = spellcheck_mac::CheckSpelling(word, ToDocumentTag(route_id));
255 }
256
OnFillSuggestionList(const base::string16 & word,std::vector<base::string16> * suggestions)257 void SpellCheckMessageFilterMac::OnFillSuggestionList(
258 const base::string16& word,
259 std::vector<base::string16>* suggestions) {
260 spellcheck_mac::FillSuggestionList(word, suggestions);
261 }
262
OnShowSpellingPanel(bool show)263 void SpellCheckMessageFilterMac::OnShowSpellingPanel(bool show) {
264 spellcheck_mac::ShowSpellingPanel(show);
265 }
266
OnUpdateSpellingPanelWithMisspelledWord(const base::string16 & word)267 void SpellCheckMessageFilterMac::OnUpdateSpellingPanelWithMisspelledWord(
268 const base::string16& word) {
269 spellcheck_mac::UpdateSpellingPanelWithMisspelledWord(word);
270 }
271
OnRequestTextCheck(int route_id,int identifier,const base::string16 & text,std::vector<SpellCheckMarker> markers)272 void SpellCheckMessageFilterMac::OnRequestTextCheck(
273 int route_id,
274 int identifier,
275 const base::string16& text,
276 std::vector<SpellCheckMarker> markers) {
277 DCHECK(!text.empty());
278 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
279 // Erase invalid markers (with offsets out of boundaries of text length).
280 markers.erase(
281 std::remove_if(
282 markers.begin(),
283 markers.end(),
284 std::not1(SpellCheckMarker::IsValidPredicate(text.length()))),
285 markers.end());
286 // SpellingRequest self-destructs.
287 SpellingRequest* request =
288 new SpellingRequest(client_.get(), this, render_process_id_);
289 request->RequestCheck(
290 text, route_id, identifier, ToDocumentTag(route_id), markers);
291 }
292
ToDocumentTag(int route_id)293 int SpellCheckMessageFilterMac::ToDocumentTag(int route_id) {
294 if (!tag_map_.count(route_id))
295 tag_map_[route_id] = spellcheck_mac::GetDocumentTag();
296 return tag_map_[route_id];
297 }
298
299 // TODO(groby): We are currently not notified of retired tags. We need
300 // to track destruction of RenderViewHosts on the browser process side
301 // to update our mappings when a document goes away.
RetireDocumentTag(int route_id)302 void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) {
303 spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id));
304 tag_map_.erase(route_id);
305 }
306
307