Home | History | Annotate | Download | only in spellchecker
      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