Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | #include <functional> |
| 9 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 10 | #include "base/bind.h" |
| 11 | #include "chrome/browser/profiles/profile.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 12 | #include "chrome/browser/spellchecker/spellcheck_factory.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 13 | #include "chrome/browser/spellchecker/spellcheck_platform_mac.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 14 | #include "chrome/browser/spellchecker/spellcheck_service.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 15 | #include "chrome/browser/spellchecker/spelling_service_client.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 16 | #include "chrome/common/spellcheck_messages.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 17 | #include "chrome/common/spellcheck_result.h" |
| 18 | #include "content/public/browser/render_process_host.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 19 | |
| 20 | using content::BrowserThread; |
| 21 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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, |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 40 | int document_tag, |
| 41 | const std::vector<SpellCheckMarker>& markers); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 74 | std::vector<SpellCheckMarker> markers_; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 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) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 103 | markers_ = markers; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 169 | SpellcheckService* spellcheck_service = |
| 170 | SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_); |
| 171 | if (spellcheck_service) { |
| 172 | spellcheck_service->GetFeedbackSender()->OnSpellcheckResults( |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 173 | render_process_id_, |
| 174 | text, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 175 | markers_, |
| 176 | &remote_results_); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 177 | } |
| 178 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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, |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 287 | // SpellingRequest self-destructs. |
| 288 | SpellingRequest* request = |
| 289 | new SpellingRequest(client_.get(), this, render_process_id_); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 290 | request->RequestCheck( |
| 291 | text, route_id, identifier, ToDocumentTag(route_id), markers); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 303 | void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 304 | spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 305 | tag_map_.erase(route_id); |
| 306 | } |
| 307 | |