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/bind.h" 11 #include "chrome/browser/profiles/profile.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/render_process_host.h" 19 20 using content::BrowserThread; 21 22 namespace { 23 24 bool CompareLocation(const SpellCheckResult& r1, 25 const SpellCheckResult& r2) { 26 return r1.location < r2.location; 27 } 28 29 } // namespace 30 31 class 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, 40 int document_tag, 41 const std::vector<SpellCheckMarker>& markers); 42 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_; 74 std::vector<SpellCheckMarker> markers_; 75 }; 76 77 SpellingRequest::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 91 void SpellingRequest::RequestCheck( 92 const string16& text, 93 int route_id, 94 int identifier, 95 int document_tag, 96 const std::vector<SpellCheckMarker>& markers) { 97 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; 103 markers_ = markers; 104 105 // Send the remote query out. 106 RequestRemoteCheck(text); 107 RequestLocalCheck(text, document_tag_); 108 } 109 110 void 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 125 void 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 134 void 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 160 void 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 169 SpellcheckService* spellcheck_service = 170 SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_); 171 if (spellcheck_service) { 172 spellcheck_service->GetFeedbackSender()->OnSpellcheckResults( 173 render_process_id_, 174 text, 175 markers_, 176 &remote_results_); 177 } 178 179 OnCheckCompleted(); 180 } 181 182 void 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 193 SpellCheckMessageFilterMac::SpellCheckMessageFilterMac(int render_process_id) 194 : render_process_id_(render_process_id), 195 client_(new SpellingServiceClient) { 196 } 197 198 void SpellCheckMessageFilterMac::OverrideThreadForMessage( 199 const IPC::Message& message, BrowserThread::ID* thread) { 200 if (message.type() == SpellCheckHostMsg_RequestTextCheck::ID) 201 *thread = BrowserThread::UI; 202 } 203 204 bool 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) 212 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 223 // static 224 void 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 250 SpellCheckMessageFilterMac::~SpellCheckMessageFilterMac() {} 251 252 void SpellCheckMessageFilterMac::OnCheckSpelling(const string16& word, 253 int route_id, 254 bool* correct) { 255 *correct = spellcheck_mac::CheckSpelling(word, ToDocumentTag(route_id)); 256 } 257 258 void SpellCheckMessageFilterMac::OnFillSuggestionList( 259 const string16& word, 260 std::vector<string16>* suggestions) { 261 spellcheck_mac::FillSuggestionList(word, suggestions); 262 } 263 264 void SpellCheckMessageFilterMac::OnShowSpellingPanel(bool show) { 265 spellcheck_mac::ShowSpellingPanel(show); 266 } 267 268 void SpellCheckMessageFilterMac::OnUpdateSpellingPanelWithMisspelledWord( 269 const string16& word) { 270 spellcheck_mac::UpdateSpellingPanelWithMisspelledWord(word); 271 } 272 273 void SpellCheckMessageFilterMac::OnRequestTextCheck( 274 int route_id, 275 int identifier, 276 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()); 287 // SpellingRequest self-destructs. 288 SpellingRequest* request = 289 new SpellingRequest(client_.get(), this, render_process_id_); 290 request->RequestCheck( 291 text, route_id, identifier, ToDocumentTag(route_id), markers); 292 } 293 294 int 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 300 // 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. 303 void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) { 304 spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id)); 305 tag_map_.erase(route_id); 306 } 307 308