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_mac.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 
     10 #include "base/bind.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/spellchecker/spellcheck_factory.h"
     13 #include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
     14 #include "chrome/browser/spellchecker/spellcheck_service.h"
     15 #include "chrome/browser/spellchecker/spelling_service_client.h"
     16 #include "chrome/common/spellcheck_messages.h"
     17 #include "chrome/common/spellcheck_result.h"
     18 #include "content/public/browser/render_process_host.h"
     19 
     20 using content::BrowserThread;
     21 
     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,
     40                     int document_tag,
     41                     const std::vector<SpellCheckMarker>& markers);
     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_;
     74   std::vector<SpellCheckMarker> markers_;
     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 
     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) {
     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;
    103   markers_ = markers;
    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 
    169   SpellcheckService* spellcheck_service =
    170       SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
    171   if (spellcheck_service) {
    172     spellcheck_service->GetFeedbackSender()->OnSpellcheckResults(
    173         render_process_id_,
    174         text,
    175         markers_,
    176         &remote_results_);
    177   }
    178 
    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 }
    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)
    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 
    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 
    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 
    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,
    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());
    287   // SpellingRequest self-destructs.
    288   SpellingRequest* request =
    289     new SpellingRequest(client_.get(), this, render_process_id_);
    290   request->RequestCheck(
    291       text, route_id, identifier, ToDocumentTag(route_id), markers);
    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 
    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.
    303 void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) {
    304   spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id));
    305   tag_map_.erase(route_id);
    306 }
    307 
    308