spelling_service_client.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
19a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "chrome/common/spellcheck_common.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_result.h"
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/user_prefs/user_prefs.h"
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "content/public/browser/browser_context.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/google_api_keys.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "url/gurl.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The URL for requesting spell checking and sending user feedback.
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kSpellingServiceURL[] = "https://www.googleapis.com/rpc";
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// The location of spellcheck suggestions in JSON response from spelling
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// service.
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)const char kMisspellingsPath[] = "result.spellingCheckResponse.misspellings";
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
37868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// The location of error messages in JSON response from spelling service.
38868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)const char kErrorPath[] = "error";
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellingServiceClient::SpellingServiceClient() {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellingServiceClient::~SpellingServiceClient() {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  STLDeleteContainerPairPointers(spellcheck_fetchers_.begin(),
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 spellcheck_fetchers_.end());
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellingServiceClient::RequestTextCheck(
51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    content::BrowserContext* context,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ServiceType type,
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& text,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const TextCheckCompleteCallback& callback) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(type == SUGGEST || type == SPELLCHECK);
56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!context || !IsAvailable(context, type)) {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback.Run(false, text, std::vector<SpellCheckResult>());
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
61d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const PrefService* pref = user_prefs::UserPrefs::Get(context);
62d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(pref);
63d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string language_code;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string country_code;
66a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
67d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      pref->GetString(prefs::kSpellCheckDictionary),
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &language_code,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &country_code);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Format the JSON request to be sent to the Spelling service.
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string encoded_text = base::GetQuotedJSONString(text);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char kSpellingRequest[] =
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"method\":\"spelling.check\","
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"apiVersion\":\"v%d\","
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"params\":{"
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      "\"text\":%s,"
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"language\":\"%s\","
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"originCountry\":\"%s\","
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "\"key\":%s"
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}"
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string api_key = base::GetQuotedJSONString(google_apis::GetAPIKey());
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string request = base::StringPrintf(
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kSpellingRequest,
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      encoded_text.c_str(),
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      language_code.c_str(),
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      country_code.c_str(),
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      api_key.c_str());
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url = GURL(kSpellingServiceURL);
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::URLFetcher* fetcher = CreateURLFetcher(url);
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  fetcher->SetRequestContext(context->GetRequestContext());
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetUploadData("application/json", request);
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->SetLoadFlags(
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES);
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  spellcheck_fetchers_[fetcher] = new TextCheckCallbackData(callback, text);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  fetcher->Start();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool SpellingServiceClient::IsAvailable(
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    content::BrowserContext* context,
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    ServiceType type) {
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const PrefService* pref = user_prefs::UserPrefs::Get(context);
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(pref);
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // If prefs don't allow spellchecking or if the context is off the record,
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the spelling service should be unavailable.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!pref->GetBoolean(prefs::kEnableContinuousSpellcheck) ||
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !pref->GetBoolean(prefs::kSpellCheckUseSpellingService) ||
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      context->IsOffTheRecord())
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the locale for spelling has not been set, the user has not decided to
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use spellcheck so we don't do anything remote (suggest or spelling).
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string locale = pref->GetString(prefs::kSpellCheckDictionary);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (locale.empty())
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we do not have the spelling service enabled, then we are only available
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // for SUGGEST.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CommandLine* command_line = CommandLine::ForCurrentProcess();
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (command_line->HasSwitch(switches::kUseSpellingSuggestions))
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type == SUGGEST;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finally, if all options are available, we only enable only SUGGEST
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // if SPELLCHECK is not available for our language because SPELLCHECK results
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are a superset of SUGGEST results.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Only available for English right now. Fix this line to include
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // all languages SPELLCHECK covers.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool language_available = !locale.compare(0, 2, "en");
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (language_available) {
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return type == SPELLCHECK;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only SUGGEST is allowed.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type == SUGGEST;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellingServiceClient::ParseResponse(
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& data,
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<SpellCheckResult>* results) {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When this JSON-RPC call finishes successfully, the Spelling service returns
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an JSON object listed below.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * result - an envelope object representing the result from the APIARY
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   server, which is the JSON-API front-end for the Spelling service. This
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   object consists of the following variable:
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - spellingCheckResponse (SpellingCheckResponse).
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * SpellingCheckResponse - an object representing the result from the
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   Spelling service. This object consists of the following variable:
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - misspellings (optional array of Misspelling)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // * Misspelling - an object representing a misspelling region and its
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   suggestions. This object consists of the following variables:
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - charStart (number) - the beginning of the misspelled region;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - charLength (number) - the length of the misspelled region;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - suggestions (array of string) - the suggestions for the misspelling
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     text, and;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   - canAutoCorrect (optional boolean) - whether we can use the first
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     suggestion for auto-correction.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For example, the Spelling service returns the following JSON when we send a
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // spelling request for "duck goes quisk" as of 16 August, 2011.
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   "result": {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     "spellingCheckResponse": {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //       "misspellings": [{
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "charStart": 10,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "charLength": 5,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "suggestions": [{ "suggestion": "quack" }],
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //           "canAutoCorrect": false
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //       }]
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //     }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // }
177868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // If the service is not available, the Spelling service returns JSON with an
178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // error.
179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // {
180868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //   "error": {
181868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //     "code": 400,
182868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //     "message": "Bad Request",
183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //     "data": [...]
184868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  //   }
185868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // }
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> value(
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      static_cast<base::DictionaryValue*>(
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS)));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
192868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Check for errors from spelling service.
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* error = NULL;
194868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (value->GetDictionary(kErrorPath, &error))
195868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return false;
196868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve the array of Misspelling objects. When the input text does not
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have misspelled words, it returns an empty JSON. (In this case, its HTTP
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // status is 200.) We just return true for this case.
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::ListValue* misspellings = NULL;
201868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!value->GetList(kMisspellingsPath, &misspellings))
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < misspellings->GetSize(); ++i) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Retrieve the i-th misspelling region and put it to the given vector. When
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the Spelling service sends two or more suggestions, we read only the
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // first one because SpellCheckResult can store only one suggestion.
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::DictionaryValue* misspelling = NULL;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!misspellings->GetDictionary(i, &misspelling))
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int start = 0;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int length = 0;
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::ListValue* suggestions = NULL;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!misspelling->GetInteger("charStart", &start) ||
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !misspelling->GetInteger("charLength", &length) ||
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !misspelling->GetList("suggestions", &suggestions)) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::DictionaryValue* suggestion = NULL;
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 replacement;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!suggestions->GetDictionary(0, &suggestion) ||
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !suggestion->GetString("suggestion", &replacement)) {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SpellCheckResult result(
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SpellCheckResult::SPELLING, start, length, replacement);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results->push_back(result);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
233868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
234868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)SpellingServiceClient::TextCheckCallbackData::TextCheckCallbackData(
235868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    TextCheckCompleteCallback callback,
236a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 text)
237868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      : callback(callback),
238868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        text(text) {
239868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
240868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
241868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)SpellingServiceClient::TextCheckCallbackData::~TextCheckCallbackData() {
242868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
243868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
244868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void SpellingServiceClient::OnURLFetchComplete(
245868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    const net::URLFetcher* source) {
246868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(spellcheck_fetchers_[source]);
247868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  scoped_ptr<const net::URLFetcher> fetcher(source);
248868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  scoped_ptr<TextCheckCallbackData>
249868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      callback_data(spellcheck_fetchers_[fetcher.get()]);
250868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool success = false;
251868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  std::vector<SpellCheckResult> results;
252868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (fetcher->GetResponseCode() / 100 == 2) {
253868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    std::string data;
254868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    fetcher->GetResponseAsString(&data);
255868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    success = ParseResponse(data, &results);
256868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
257868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  callback_data->callback.Run(success, callback_data->text, results);
258868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  spellcheck_fetchers_.erase(fetcher.get());
259868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
260868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
261868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)net::URLFetcher* SpellingServiceClient::CreateURLFetcher(const GURL& url) {
262868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return net::URLFetcher::Create(url, net::URLFetcher::POST, this);
263868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
264