root/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CompareLocation
  2. document_tag_
  3. RequestCheck
  4. RequestRemoteCheck
  5. RequestLocalCheck
  6. OnCheckCompleted
  7. OnRemoteCheckCompleted
  8. OnLocalCheckCompleted
  9. client_
  10. OverrideThreadForMessage
  11. OnMessageReceived
  12. CombineResults
  13. OnCheckSpelling
  14. OnFillSuggestionList
  15. OnShowSpellingPanel
  16. OnUpdateSpellingPanelWithMisspelledWord
  17. OnRequestTextCheck
  18. ToDocumentTag
  19. RetireDocumentTag

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/spellchecker/spellcheck_message_filter_mac.h"

#include <algorithm>
#include <functional>

#include "base/barrier_closure.h"
#include "base/bind.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/spellchecker/spelling_service_client.h"
#include "chrome/common/spellcheck_messages.h"
#include "chrome/common/spellcheck_result.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"

using content::BrowserThread;
using content::BrowserContext;

namespace {

bool CompareLocation(const SpellCheckResult& r1,
                     const SpellCheckResult& r2) {
  return r1.location < r2.location;
}

}  // namespace

class SpellingRequest {
 public:
  SpellingRequest(SpellingServiceClient* client,
                  content::BrowserMessageFilter* destination,
                  int render_process_id);

  void RequestCheck(const base::string16& text,
                    int route_id,
                    int identifier,
                    int document_tag,
                    const std::vector<SpellCheckMarker>& markers);
 private:
  // Request server-side checking.
  void RequestRemoteCheck(const base::string16& text);

  // Request a check from local spell checker.
  void RequestLocalCheck(const base::string16& text, int document_tag);

  // Check if all pending requests are done, send reply to render process if so.
  void OnCheckCompleted();

  // Called when server-side checking is complete.
  void OnRemoteCheckCompleted(bool success,
                              const base::string16& text,
                              const std::vector<SpellCheckResult>& results);

  // Called when local checking is complete.
  void OnLocalCheckCompleted(const std::vector<SpellCheckResult>& results);

  std::vector<SpellCheckResult> local_results_;
  std::vector<SpellCheckResult> remote_results_;

  // Barrier closure for completion of both remote and local check.
  base::Closure completion_barrier_;
  bool remote_success_;

  SpellingServiceClient* client_;  // Owned by |destination|.
  content::BrowserMessageFilter* destination_;  // ref-counted.
  int render_process_id_;

  int route_id_;
  int identifier_;
  int document_tag_;
  std::vector<SpellCheckMarker> markers_;
};

SpellingRequest::SpellingRequest(SpellingServiceClient* client,
                                 content::BrowserMessageFilter* destination,
                                 int render_process_id)
    : remote_success_(false),
      client_(client),
      destination_(destination),
      render_process_id_(render_process_id),
      route_id_(-1),
      identifier_(-1),
      document_tag_(-1) {
  destination_->AddRef();
}

void SpellingRequest::RequestCheck(
    const base::string16& text,
    int route_id,
    int identifier,
    int document_tag,
    const std::vector<SpellCheckMarker>& markers) {
  DCHECK(!text.empty());
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  route_id_ = route_id;
  identifier_ = identifier;
  document_tag_ = document_tag;
  markers_ = markers;

  // Send the remote query out. The barrier owns |this|, ensuring it is deleted
  // after completion.
  completion_barrier_ =
      BarrierClosure(2,
                     base::Bind(&SpellingRequest::OnCheckCompleted,
                     base::Owned(this)));
  RequestRemoteCheck(text);
  RequestLocalCheck(text, document_tag_);
}

void SpellingRequest::RequestRemoteCheck(const base::string16& text) {
  BrowserContext* context = NULL;
  content::RenderProcessHost* host =
      content::RenderProcessHost::FromID(render_process_id_);
  if (host)
    context = host->GetBrowserContext();

  client_->RequestTextCheck(
    context,
    SpellingServiceClient::SPELLCHECK,
    text,
    base::Bind(&SpellingRequest::OnRemoteCheckCompleted,
               base::Unretained(this)));
}

void SpellingRequest::RequestLocalCheck(const base::string16& text,
                                        int document_tag) {
  spellcheck_mac::RequestTextCheck(
      document_tag,
      text,
      base::Bind(&SpellingRequest::OnLocalCheckCompleted,
                 base::Unretained(this)));
}

void SpellingRequest::OnCheckCompleted() {
  // Final completion can happen on any thread - don't DCHECK thread.
  const std::vector<SpellCheckResult>* check_results = &local_results_;
  if (remote_success_) {
    std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
    std::sort(local_results_.begin(), local_results_.end(), CompareLocation);
    SpellCheckMessageFilterMac::CombineResults(&remote_results_,
                                               local_results_);
    check_results = &remote_results_;
  }

  destination_->Send(
      new SpellCheckMsg_RespondTextCheck(
          route_id_,
          identifier_,
          *check_results));
  destination_->Release();

  // Object is self-managed - at this point, its life span is over.
  // No need to delete, since the OnCheckCompleted callback owns |this|.
}

void SpellingRequest::OnRemoteCheckCompleted(
    bool success,
    const base::string16& text,
    const std::vector<SpellCheckResult>& results) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  remote_success_ = success;
  remote_results_ = results;

  SpellcheckService* spellcheck_service =
      SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
  if (spellcheck_service) {
    spellcheck_service->GetFeedbackSender()->OnSpellcheckResults(
        render_process_id_,
        text,
        markers_,
        &remote_results_);
  }

  completion_barrier_.Run();
}

void SpellingRequest::OnLocalCheckCompleted(
    const std::vector<SpellCheckResult>& results) {
  // Local checking can happen on any thread - don't DCHECK thread.
  local_results_ = results;
  completion_barrier_.Run();
}


SpellCheckMessageFilterMac::SpellCheckMessageFilterMac(int render_process_id)
    : BrowserMessageFilter(SpellCheckMsgStart),
      render_process_id_(render_process_id),
      client_(new SpellingServiceClient) {
}

void SpellCheckMessageFilterMac::OverrideThreadForMessage(
    const IPC::Message& message, BrowserThread::ID* thread) {
  if (message.type() == SpellCheckHostMsg_RequestTextCheck::ID)
    *thread = BrowserThread::UI;
}

bool SpellCheckMessageFilterMac::OnMessageReceived(const IPC::Message& message,
                                                   bool* message_was_ok) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(SpellCheckMessageFilterMac, message, *message_was_ok)
    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CheckSpelling,
                        OnCheckSpelling)
    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_FillSuggestionList,
                        OnFillSuggestionList)
    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_ShowSpellingPanel,
                        OnShowSpellingPanel)
    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord,
                        OnUpdateSpellingPanelWithMisspelledWord)
    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck,
                        OnRequestTextCheck)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

// static
void SpellCheckMessageFilterMac::CombineResults(
    std::vector<SpellCheckResult>* remote_results,
    const std::vector<SpellCheckResult>& local_results) {
  std::vector<SpellCheckResult>::const_iterator local_iter(
      local_results.begin());
  std::vector<SpellCheckResult>::iterator remote_iter;

  for (remote_iter = remote_results->begin();
       remote_iter != remote_results->end();
       ++remote_iter) {
    // Discard all local results occurring before remote result.
    while (local_iter != local_results.end() &&
           local_iter->location < remote_iter->location) {
      local_iter++;
    }

    // Unless local and remote result coincide, result is GRAMMAR.
    remote_iter->decoration = SpellCheckResult::GRAMMAR;
    if (local_iter != local_results.end() &&
        local_iter->location == remote_iter->location &&
        local_iter->length == remote_iter->length) {
      remote_iter->decoration = SpellCheckResult::SPELLING;
    }
  }
}

SpellCheckMessageFilterMac::~SpellCheckMessageFilterMac() {}

void SpellCheckMessageFilterMac::OnCheckSpelling(const base::string16& word,
                                                 int route_id,
                                                 bool* correct) {
  *correct = spellcheck_mac::CheckSpelling(word, ToDocumentTag(route_id));
}

void SpellCheckMessageFilterMac::OnFillSuggestionList(
    const base::string16& word,
    std::vector<base::string16>* suggestions) {
  spellcheck_mac::FillSuggestionList(word, suggestions);
}

void SpellCheckMessageFilterMac::OnShowSpellingPanel(bool show) {
  spellcheck_mac::ShowSpellingPanel(show);
}

void SpellCheckMessageFilterMac::OnUpdateSpellingPanelWithMisspelledWord(
    const base::string16& word) {
  spellcheck_mac::UpdateSpellingPanelWithMisspelledWord(word);
}

void SpellCheckMessageFilterMac::OnRequestTextCheck(
    int route_id,
    int identifier,
    const base::string16& text,
    std::vector<SpellCheckMarker> markers) {
  DCHECK(!text.empty());
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  // Erase invalid markers (with offsets out of boundaries of text length).
  markers.erase(
      std::remove_if(
          markers.begin(),
          markers.end(),
          std::not1(SpellCheckMarker::IsValidPredicate(text.length()))),
      markers.end());
  // SpellingRequest self-destructs.
  SpellingRequest* request =
    new SpellingRequest(client_.get(), this, render_process_id_);
  request->RequestCheck(
      text, route_id, identifier, ToDocumentTag(route_id), markers);
}

int SpellCheckMessageFilterMac::ToDocumentTag(int route_id) {
  if (!tag_map_.count(route_id))
    tag_map_[route_id] = spellcheck_mac::GetDocumentTag();
  return tag_map_[route_id];
}

// TODO(groby): We are currently not notified of retired tags. We need
// to track destruction of RenderViewHosts on the browser process side
// to update our mappings when a document goes away.
void SpellCheckMessageFilterMac::RetireDocumentTag(int route_id) {
  spellcheck_mac::CloseDocumentWithTag(ToDocumentTag(route_id));
  tag_map_.erase(route_id);
}


/* [<][>][^][v][top][bottom][index][help] */