• 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_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