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