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/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