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/barrier_closure.h"
     11 #include "base/bind.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/browser_context.h"
     19 #include "content/public/browser/render_process_host.h"
     20 
     21 using content::BrowserThread;
     22 using content::BrowserContext;
     23 
     24 namespace {
     25 
     26 bool CompareLocation(const SpellCheckResult& r1,
     27                      const SpellCheckResult& r2) {
     28   return r1.location < r2.location;
     29 }
     30 
     31 }  // namespace
     32 
     33 class SpellingRequest {
     34  public:
     35   SpellingRequest(SpellingServiceClient* client,
     36                   content::BrowserMessageFilter* destination,
     37                   int render_process_id);
     38 
     39   void RequestCheck(const base::string16& text,
     40                     int route_id,
     41                     int identifier,
     42                     int document_tag,
     43                     const std::vector<SpellCheckMarker>& markers);
     44  private:
     45   // Request server-side checking.
     46   void RequestRemoteCheck(const base::string16& text);
     47 
     48   // Request a check from local spell checker.
     49   void RequestLocalCheck(const base::string16& text, int document_tag);
     50 
     51   // Check if all pending requests are done, send reply to render process if so.
     52   void OnCheckCompleted();
     53 
     54   // Called when server-side checking is complete.
     55   void OnRemoteCheckCompleted(bool success,
     56                               const base::string16& text,
     57                               const std::vector<SpellCheckResult>& results);
     58 
     59   // Called when local checking is complete.
     60   void OnLocalCheckCompleted(const std::vector<SpellCheckResult>& results);
     61 
     62   std::vector<SpellCheckResult> local_results_;
     63   std::vector<SpellCheckResult> remote_results_;
     64 
     65   // Barrier closure for completion of both remote and local check.
     66   base::Closure completion_barrier_;
     67   bool remote_success_;
     68 
     69   SpellingServiceClient* client_;  // Owned by |destination|.
     70   content::BrowserMessageFilter* destination_;  // ref-counted.
     71   int render_process_id_;
     72 
     73   int route_id_;
     74   int identifier_;
     75   int document_tag_;
     76   std::vector<SpellCheckMarker> markers_;
     77 };
     78 
     79 SpellingRequest::SpellingRequest(SpellingServiceClient* client,
     80                                  content::BrowserMessageFilter* destination,
     81                                  int render_process_id)
     82     : remote_success_(false),
     83       client_(client),
     84       destination_(destination),
     85       render_process_id_(render_process_id),
     86       route_id_(-1),
     87       identifier_(-1),
     88       document_tag_(-1) {
     89   destination_->AddRef();
     90 }
     91 
     92 void SpellingRequest::RequestCheck(
     93     const base::string16& text,
     94     int route_id,
     95     int identifier,
     96     int document_tag,
     97     const std::vector<SpellCheckMarker>& markers) {
     98   DCHECK(!text.empty());
     99   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    100 
    101   route_id_ = route_id;
    102   identifier_ = identifier;
    103   document_tag_ = document_tag;
    104   markers_ = markers;
    105 
    106   // Send the remote query out. The barrier owns |this|, ensuring it is deleted
    107   // after completion.
    108   completion_barrier_ =
    109       BarrierClosure(2,
    110                      base::Bind(&SpellingRequest::OnCheckCompleted,
    111                      base::Owned(this)));
    112   RequestRemoteCheck(text);
    113   RequestLocalCheck(text, document_tag_);
    114 }
    115 
    116 void SpellingRequest::RequestRemoteCheck(const base::string16& text) {
    117   BrowserContext* context = NULL;
    118   content::RenderProcessHost* host =
    119       content::RenderProcessHost::FromID(render_process_id_);
    120   if (host)
    121     context = host->GetBrowserContext();
    122 
    123   client_->RequestTextCheck(
    124     context,
    125     SpellingServiceClient::SPELLCHECK,
    126     text,
    127     base::Bind(&SpellingRequest::OnRemoteCheckCompleted,
    128                base::Unretained(this)));
    129 }
    130 
    131 void SpellingRequest::RequestLocalCheck(const base::string16& text,
    132                                         int document_tag) {
    133   spellcheck_mac::RequestTextCheck(
    134       document_tag,
    135       text,
    136       base::Bind(&SpellingRequest::OnLocalCheckCompleted,
    137                  base::Unretained(this)));
    138 }
    139 
    140 void SpellingRequest::OnCheckCompleted() {
    141   // Final completion can happen on any thread - don't DCHECK thread.
    142   const std::vector<SpellCheckResult>* check_results = &local_results_;
    143   if (remote_success_) {
    144     std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
    145     std::sort(local_results_.begin(), local_results_.end(), CompareLocation);
    146     SpellCheckMessageFilterMac::CombineResults(&remote_results_,
    147                                                local_results_);
    148     check_results = &remote_results_;
    149   }
    150 
    151   destination_->Send(
    152       new SpellCheckMsg_RespondTextCheck(
    153           route_id_,
    154           identifier_,
    155           *check_results));
    156   destination_->Release();
    157 
    158   // Object is self-managed - at this point, its life span is over.
    159   // No need to delete, since the OnCheckCompleted callback owns |this|.
    160 }
    161 
    162 void SpellingRequest::OnRemoteCheckCompleted(
    163     bool success,
    164     const base::string16& text,
    165     const std::vector<SpellCheckResult>& results) {
    166   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    167   remote_success_ = success;
    168   remote_results_ = results;
    169 
    170   SpellcheckService* spellcheck_service =
    171       SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
    172   if (spellcheck_service) {
    173     spellcheck_service->GetFeedbackSender()->OnSpellcheckResults(
    174         render_process_id_,
    175         text,
    176         markers_,
    177         &remote_results_);
    178   }
    179 
    180   completion_barrier_.Run();
    181 }
    182 
    183 void SpellingRequest::OnLocalCheckCompleted(
    184     const std::vector<SpellCheckResult>& results) {
    185   // Local checking can happen on any thread - don't DCHECK thread.
    186   local_results_ = results;
    187   completion_barrier_.Run();
    188 }
    189 
    190 
    191 SpellCheckMessageFilterMac::SpellCheckMessageFilterMac(int render_process_id)
    192     : BrowserMessageFilter(SpellCheckMsgStart),
    193       render_process_id_(render_process_id),
    194       client_(new SpellingServiceClient) {
    195 }
    196 
    197 void SpellCheckMessageFilterMac::OverrideThreadForMessage(
    198     const IPC::Message& message, BrowserThread::ID* thread) {
    199   if (message.type() == SpellCheckHostMsg_RequestTextCheck::ID)
    200     *thread = BrowserThread::UI;
    201 }
    202 
    203 bool SpellCheckMessageFilterMac::OnMessageReceived(
    204     const IPC::Message& message) {
    205   bool handled = true;
    206   IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilterMac, message)
    207     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CheckSpelling,
    208                         OnCheckSpelling)
    209     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_FillSuggestionList,
    210                         OnFillSuggestionList)
    211     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_ShowSpellingPanel,
    212                         OnShowSpellingPanel)
    213     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord,
    214                         OnUpdateSpellingPanelWithMisspelledWord)
    215     IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck,
    216                         OnRequestTextCheck)
    217     IPC_MESSAGE_UNHANDLED(handled = false)
    218   IPC_END_MESSAGE_MAP()
    219   return handled;
    220 }
    221 
    222 // static
    223 void SpellCheckMessageFilterMac::CombineResults(
    224     std::vector<SpellCheckResult>* remote_results,
    225     const std::vector<SpellCheckResult>& local_results) {
    226   std::vector<SpellCheckResult>::const_iterator local_iter(
    227       local_results.begin());
    228   std::vector<SpellCheckResult>::iterator remote_iter;
    229 
    230   for (remote_iter = remote_results->begin();
    231        remote_iter != remote_results->end();
    232        ++remote_iter) {
    233     // Discard all local results occurring before remote result.
    234     while (local_iter != local_results.end() &&
    235            local_iter->location < remote_iter->location) {
    236       local_iter++;
    237     }
    238 
    239     // Unless local and remote result coincide, result is GRAMMAR.
    240     remote_iter->decoration = SpellCheckResult::GRAMMAR;
    241     if (local_iter != local_results.end() &&
    242         local_iter->location == remote_iter->location &&
    243         local_iter->length == remote_iter->length) {
    244       remote_iter->decoration = SpellCheckResult::SPELLING;
    245     }
    246   }
    247 }
    248 
    249 SpellCheckMessageFilterMac::~SpellCheckMessageFilterMac() {}
    250 
    251 void SpellCheckMessageFilterMac::OnCheckSpelling(const base::string16& word,
    252                                                  int route_id,
    253                                                  bool* correct) {
    254   *correct = spellcheck_mac::CheckSpelling(word, ToDocumentTag(route_id));
    255 }
    256 
    257 void SpellCheckMessageFilterMac::OnFillSuggestionList(
    258     const base::string16& word,
    259     std::vector<base::string16>* suggestions) {
    260   spellcheck_mac::FillSuggestionList(word, suggestions);
    261 }
    262 
    263 void SpellCheckMessageFilterMac::OnShowSpellingPanel(bool show) {
    264   spellcheck_mac::ShowSpellingPanel(show);
    265 }
    266 
    267 void SpellCheckMessageFilterMac::OnUpdateSpellingPanelWithMisspelledWord(
    268     const base::string16& word) {
    269   spellcheck_mac::UpdateSpellingPanelWithMisspelledWord(word);
    270 }
    271 
    272 void SpellCheckMessageFilterMac::OnRequestTextCheck(
    273     int route_id,
    274     int identifier,
    275     const base::string16& text,
    276     std::vector<SpellCheckMarker> markers) {
    277   DCHECK(!text.empty());
    278   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    279   // Erase invalid markers (with offsets out of boundaries of text length).
    280   markers.erase(
    281       std::remove_if(
    282           markers.begin(),
    283           markers.end(),
    284           std::not1(SpellCheckMarker::IsValidPredicate(text.length()))),
    285       markers.end());
    286   // SpellingRequest self-destructs.
    287   SpellingRequest* request =
    288     new SpellingRequest(client_.get(), this, render_process_id_);
    289   request->RequestCheck(
    290       text, route_id, identifier, ToDocumentTag(route_id), markers);
    291 }
    292 
    293 int SpellCheckMessageFilterMac::ToDocumentTag(int route_id) {
    294   if (!tag_map_.count(route_id))
    295     tag_map_[route_id] = spellcheck_mac::GetDocumentTag();
    296   return tag_map_[route_id];
    297 }
    298 
    299 // TODO(groby): We are currently not notified of retired tags. We need
    300 // to track destruction of RenderViewHosts on the browser process side
    301 // to update our mappings when a document goes away.
    302 void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) {
    303   spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id));
    304   tag_map_.erase(route_id);
    305 }
    306 
    307