spelling_service_client.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/spellchecker/spelling_service_client.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_reader.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/string_escape.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/stl_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stringprintf.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_result.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/google_api_keys.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/icu/public/common/unicode/uloc.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The URL for requesting spell checking and sending user feedback.
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kSpellingServiceURL[] = "https://www.googleapis.com/rpc";
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Gets the ISO codes for the language and country of this |locale|. The
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |locale| is an ISO locale ID that may not include a country ID, e.g., "fr" or
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// "de". This method converts the UI locale to a full locale ID and converts the
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// full locale ID to an ISO language code and an ISO3 country code.
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GetISOLanguageCountryCodeFromLocale(
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& locale,
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string* language_code,
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string* country_code) {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(language_code);
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(country_code);
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  char language[ULOC_LANG_CAPACITY] = ULOC_ENGLISH;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const char* country = "USA";
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!locale.empty()) {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    UErrorCode error = U_ZERO_ERROR;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    char id[ULOC_LANG_CAPACITY + ULOC_SCRIPT_CAPACITY + ULOC_COUNTRY_CAPACITY];
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    uloc_addLikelySubtags(locale.c_str(), id, arraysize(id), &error);
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    error = U_ZERO_ERROR;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    uloc_getLanguage(id, language, arraysize(language), &error);
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    country = uloc_getISO3Country(id);
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *language_code = std::string(language);
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *country_code = std::string(country);
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellingServiceClient::SpellingServiceClient() {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellingServiceClient::~SpellingServiceClient() {
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  STLDeleteContainerPairPointers(spellcheck_fetchers_.begin(),
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 spellcheck_fetchers_.end());
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellingServiceClient::RequestTextCheck(
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ServiceType type,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& text,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const TextCheckCompleteCallback& callback) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(type == SUGGEST || type == SPELLCHECK);
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!profile || !IsAvailable(profile, type)) {
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback.Run(false, text, std::vector<SpellCheckResult>());
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string language_code;
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string country_code;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetISOLanguageCountryCodeFromLocale(
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      profile->GetPrefs()->GetString(prefs::kSpellCheckDictionary),
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &language_code,
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &country_code);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Format the JSON request to be sent to the Spelling service.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string encoded_text;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::JsonDoubleQuote(text, false, &encoded_text);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kSpellingRequest[] =
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"method\":\"spelling.check\","
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"apiVersion\":\"v%d\","
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"params\":{"
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"text\":\"%s\","
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"language\":\"%s\","
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"originCountry\":\"%s\","
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "\"key\":%s"
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}"
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string api_key = base::GetDoubleQuotedJson(google_apis::GetAPIKey());
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string request = base::StringPrintf(
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kSpellingRequest,
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      encoded_text.c_str(),
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      language_code.c_str(),
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      country_code.c_str(),
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      api_key.c_str());
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url = GURL(kSpellingServiceURL);
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::URLFetcher* fetcher = CreateURLFetcher(url);
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetRequestContext(profile->GetRequestContext());
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetUploadData("application/json", request);
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetLoadFlags(
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  spellcheck_fetchers_[fetcher] = new TextCheckCallbackData(callback, text);
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->Start();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellingServiceClient::IsAvailable(Profile* profile, ServiceType type) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefService* pref = profile->GetPrefs();
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If prefs don't allow spellchecking or if the profile is off the record,
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the spelling service should be unavailable.
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!pref->GetBoolean(prefs::kEnableContinuousSpellcheck) ||
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !pref->GetBoolean(prefs::kSpellCheckUseSpellingService) ||
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      profile->IsOffTheRecord())
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the locale for spelling has not been set, the user has not decided to
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use spellcheck so we don't do anything remote (suggest or spelling).
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string locale = pref->GetString(prefs::kSpellCheckDictionary);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (locale.empty())
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we do not have the spelling service enabled, then we are only available
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // for SUGGEST.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CommandLine* command_line = CommandLine::ForCurrentProcess();
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (command_line->HasSwitch(switches::kUseSpellingSuggestions))
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type == SUGGEST;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finally, if all options are available, we only enable only SUGGEST
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // if SPELLCHECK is not available for our language because SPELLCHECK results
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are a superset of SUGGEST results.
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Only available for English right now. Fix this line to include
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // all languages SPELLCHECK covers.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool language_available = !locale.compare(0, 2, "en");
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (language_available) {
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return type == SPELLCHECK;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only SUGGEST is allowed.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type == SUGGEST;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellingServiceClient::TextCheckCallbackData::TextCheckCallbackData(
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TextCheckCompleteCallback callback,
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16 text)
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      : callback(callback),
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        text(text) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellingServiceClient::TextCheckCallbackData::~TextCheckCallbackData() {
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellingServiceClient::OnURLFetchComplete(
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(spellcheck_fetchers_[source]);
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<const net::URLFetcher> fetcher(source);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<TextCheckCallbackData>
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      callback_data(spellcheck_fetchers_[fetcher.get()]);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool success = false;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SpellCheckResult> results;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (fetcher->GetResponseCode() / 100 == 2) {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string data;
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->GetResponseAsString(&data);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    success = ParseResponse(data, &results);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  callback_data->callback.Run(success, callback_data->text, results);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  spellcheck_fetchers_.erase(fetcher.get());
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)net::URLFetcher* SpellingServiceClient::CreateURLFetcher(const GURL& url) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return net::URLFetcher::Create(url, net::URLFetcher::POST, this);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellingServiceClient::ParseResponse(
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& data,
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<SpellCheckResult>* results) {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When this JSON-RPC call finishes successfully, the Spelling service returns
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an JSON object listed below.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * result - an envelope object representing the result from the APIARY
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   server, which is the JSON-API front-end for the Spelling service. This
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   object consists of the following variable:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - spellingCheckResponse (SpellingCheckResponse).
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * SpellingCheckResponse - an object representing the result from the
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   Spelling service. This object consists of the following variable:
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - misspellings (optional array of Misspelling)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * Misspelling - an object representing a misspelling region and its
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   suggestions. This object consists of the following variables:
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - charStart (number) - the beginning of the misspelled region;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - charLength (number) - the length of the misspelled region;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - suggestions (array of string) - the suggestions for the misspelling
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     text, and;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - canAutoCorrect (optional boolean) - whether we can use the first
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     suggestion for auto-correction.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For example, the Spelling service returns the following JSON when we send a
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // spelling request for "duck goes quisk" as of 16 August, 2011.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   "result": {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     "spellingCheckResponse": {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //       "misspellings": [{
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "charStart": 10,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "charLength": 5,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "suggestions": [{ "suggestion": "quack" }],
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "canAutoCorrect": false
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //       }]
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     }
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   }
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // }
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<DictionaryValue> value(
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<DictionaryValue*>(
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS)));
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve the array of Misspelling objects. When the input text does not
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have misspelled words, it returns an empty JSON. (In this case, its HTTP
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // status is 200.) We just return true for this case.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ListValue* misspellings = NULL;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char kMisspellings[] = "result.spellingCheckResponse.misspellings";
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value->GetList(kMisspellings, &misspellings))
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < misspellings->GetSize(); ++i) {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Retrieve the i-th misspelling region and put it to the given vector. When
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the Spelling service sends two or more suggestions, we read only the
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // first one because SpellCheckResult can store only one suggestion.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* misspelling = NULL;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!misspellings->GetDictionary(i, &misspelling))
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int start = 0;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int length = 0;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ListValue* suggestions = NULL;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!misspelling->GetInteger("charStart", &start) ||
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !misspelling->GetInteger("charLength", &length) ||
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !misspelling->GetList("suggestions", &suggestions)) {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue* suggestion = NULL;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 replacement;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!suggestions->GetDictionary(0, &suggestion) ||
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !suggestion->GetString("suggestion", &replacement)) {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SpellCheckResult result(
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SpellCheckResult::SPELLING, start, length, replacement);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results->push_back(result);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
261