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.h" 6 7 #include <algorithm> 8 #include <functional> 9 10 #include "base/bind.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/spellchecker/spellcheck_factory.h" 15 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h" 16 #include "chrome/browser/spellchecker/spellcheck_service.h" 17 #include "chrome/browser/spellchecker/spelling_service_client.h" 18 #include "chrome/common/pref_names.h" 19 #include "chrome/common/spellcheck_marker.h" 20 #include "chrome/common/spellcheck_messages.h" 21 #include "content/public/browser/render_process_host.h" 22 #include "net/url_request/url_fetcher.h" 23 24 using content::BrowserThread; 25 26 SpellCheckMessageFilter::SpellCheckMessageFilter(int render_process_id) 27 : render_process_id_(render_process_id), 28 client_(new SpellingServiceClient) { 29 } 30 31 void SpellCheckMessageFilter::OverrideThreadForMessage( 32 const IPC::Message& message, BrowserThread::ID* thread) { 33 // IPC messages arrive on IO thread, but spellcheck data lives on UI thread. 34 // The message filter overrides the thread for these messages because they 35 // access spellcheck data. 36 if (message.type() == SpellCheckHostMsg_RequestDictionary::ID || 37 message.type() == SpellCheckHostMsg_NotifyChecked::ID || 38 message.type() == SpellCheckHostMsg_RespondDocumentMarkers::ID) 39 *thread = BrowserThread::UI; 40 #if !defined(OS_MACOSX) 41 if (message.type() == SpellCheckHostMsg_CallSpellingService::ID) 42 *thread = BrowserThread::UI; 43 #endif 44 } 45 46 bool SpellCheckMessageFilter::OnMessageReceived(const IPC::Message& message, 47 bool* message_was_ok) { 48 bool handled = true; 49 IPC_BEGIN_MESSAGE_MAP_EX(SpellCheckMessageFilter, message, *message_was_ok) 50 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestDictionary, 51 OnSpellCheckerRequestDictionary) 52 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked, 53 OnNotifyChecked) 54 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RespondDocumentMarkers, 55 OnRespondDocumentMarkers) 56 #if !defined(OS_MACOSX) 57 IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService, 58 OnCallSpellingService) 59 #endif 60 IPC_MESSAGE_UNHANDLED(handled = false) 61 IPC_END_MESSAGE_MAP() 62 return handled; 63 } 64 65 SpellCheckMessageFilter::~SpellCheckMessageFilter() {} 66 67 void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() { 68 content::RenderProcessHost* host = 69 content::RenderProcessHost::FromID(render_process_id_); 70 if (!host) 71 return; // Teardown. 72 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); 73 // The renderer has requested that we initialize its spellchecker. This should 74 // generally only be called once per session, as after the first call, all 75 // future renderers will be passed the initialization information on startup 76 // (or when the dictionary changes in some way). 77 SpellcheckService* spellcheck_service = 78 SpellcheckServiceFactory::GetForProfile(profile); 79 80 DCHECK(spellcheck_service); 81 // The spellchecker initialization already started and finished; just send 82 // it to the renderer. 83 spellcheck_service->InitForRenderer(host); 84 85 // TODO(rlp): Ensure that we do not initialize the hunspell dictionary more 86 // than once if we get requests from different renderers. 87 } 88 89 void SpellCheckMessageFilter::OnNotifyChecked(const string16& word, 90 bool misspelled) { 91 SpellcheckService* spellcheck = GetSpellcheckService(); 92 // Spellcheck service may not be available for a renderer process that is 93 // shutting down. 94 if (!spellcheck) 95 return; 96 if (spellcheck->GetMetrics()) 97 spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled); 98 } 99 100 void SpellCheckMessageFilter::OnRespondDocumentMarkers( 101 const std::vector<uint32>& markers) { 102 SpellcheckService* spellcheck = GetSpellcheckService(); 103 // Spellcheck service may not be available for a renderer process that is 104 // shutting down. 105 if (!spellcheck) 106 return; 107 spellcheck->GetFeedbackSender()->OnReceiveDocumentMarkers( 108 render_process_id_, markers); 109 } 110 111 #if !defined(OS_MACOSX) 112 void SpellCheckMessageFilter::OnCallSpellingService( 113 int route_id, 114 int identifier, 115 const string16& text, 116 std::vector<SpellCheckMarker> markers) { 117 DCHECK(!text.empty()); 118 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 119 // Erase invalid markers (with offsets out of boundaries of text length). 120 markers.erase( 121 std::remove_if( 122 markers.begin(), 123 markers.end(), 124 std::not1(SpellCheckMarker::IsValidPredicate(text.length()))), 125 markers.end()); 126 CallSpellingService(text, route_id, identifier, markers); 127 } 128 129 void SpellCheckMessageFilter::OnTextCheckComplete( 130 int route_id, 131 int identifier, 132 const std::vector<SpellCheckMarker>& markers, 133 bool success, 134 const string16& text, 135 const std::vector<SpellCheckResult>& results) { 136 SpellcheckService* spellcheck = GetSpellcheckService(); 137 // Spellcheck service may not be available for a renderer process that is 138 // shutting down. 139 if (!spellcheck) 140 return; 141 std::vector<SpellCheckResult> results_copy = results; 142 spellcheck->GetFeedbackSender()->OnSpellcheckResults( 143 render_process_id_, text, markers, &results_copy); 144 145 // Erase custom dictionary words from the spellcheck results and record 146 // in-dictionary feedback. 147 std::vector<SpellCheckResult>::iterator write_iter; 148 std::vector<SpellCheckResult>::iterator iter; 149 std::string text_copy = UTF16ToUTF8(text); 150 for (iter = write_iter = results_copy.begin(); 151 iter != results_copy.end(); 152 ++iter) { 153 if (spellcheck->GetCustomDictionary()->HasWord( 154 text_copy.substr(iter->location, iter->length))) { 155 spellcheck->GetFeedbackSender()->RecordInDictionary(iter->hash); 156 } else { 157 if (write_iter != iter) 158 *write_iter = *iter; 159 ++write_iter; 160 } 161 } 162 results_copy.erase(write_iter, results_copy.end()); 163 164 Send(new SpellCheckMsg_RespondSpellingService( 165 route_id, identifier, success, text, results_copy)); 166 } 167 168 // CallSpellingService always executes the callback OnTextCheckComplete. 169 // (Which, in turn, sends a SpellCheckMsg_RespondSpellingService) 170 void SpellCheckMessageFilter::CallSpellingService( 171 const string16& text, 172 int route_id, 173 int identifier, 174 const std::vector<SpellCheckMarker>& markers) { 175 Profile* profile = NULL; 176 content::RenderProcessHost* host = 177 content::RenderProcessHost::FromID(render_process_id_); 178 if (host) 179 profile = Profile::FromBrowserContext(host->GetBrowserContext()); 180 181 client_->RequestTextCheck( 182 profile, 183 SpellingServiceClient::SPELLCHECK, 184 text, 185 base::Bind(&SpellCheckMessageFilter::OnTextCheckComplete, 186 base::Unretained(this), 187 route_id, 188 identifier, 189 markers)); 190 } 191 #endif 192 193 SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const { 194 return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_); 195 } 196