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