Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / spellchecker / spelling_service_client.cc
blob8ef8bcf738ddc350324f9c56deb8aa6677d1c55d
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.
5 #include "chrome/browser/spellchecker/spelling_service_client.h"
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/string_escape.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/common/spellcheck_common.h"
20 #include "chrome/common/spellcheck_result.h"
21 #include "components/user_prefs/user_prefs.h"
22 #include "content/public/browser/browser_context.h"
23 #include "google_apis/google_api_keys.h"
24 #include "net/base/load_flags.h"
25 #include "net/url_request/url_fetcher.h"
26 #include "url/gurl.h"
28 namespace {
30 // The URL for requesting spell checking and sending user feedback.
31 const char kSpellingServiceURL[] = "https://www.googleapis.com/rpc";
33 // The location of spellcheck suggestions in JSON response from spelling
34 // service.
35 const char kMisspellingsPath[] = "result.spellingCheckResponse.misspellings";
37 // The location of error messages in JSON response from spelling service.
38 const char kErrorPath[] = "error";
40 } // namespace
42 SpellingServiceClient::SpellingServiceClient() {
45 SpellingServiceClient::~SpellingServiceClient() {
46 STLDeleteContainerPairPointers(spellcheck_fetchers_.begin(),
47 spellcheck_fetchers_.end());
50 bool SpellingServiceClient::RequestTextCheck(
51 content::BrowserContext* context,
52 ServiceType type,
53 const base::string16& text,
54 const TextCheckCompleteCallback& callback) {
55 DCHECK(type == SUGGEST || type == SPELLCHECK);
56 if (!context || !IsAvailable(context, type)) {
57 callback.Run(false, text, std::vector<SpellCheckResult>());
58 return false;
61 const PrefService* pref = user_prefs::UserPrefs::Get(context);
62 DCHECK(pref);
64 std::string language_code;
65 std::string country_code;
66 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
67 pref->GetString(prefs::kSpellCheckDictionary),
68 &language_code,
69 &country_code);
71 // Format the JSON request to be sent to the Spelling service.
72 std::string encoded_text = base::GetQuotedJSONString(text);
74 static const char kSpellingRequest[] =
75 "{"
76 "\"method\":\"spelling.check\","
77 "\"apiVersion\":\"v%d\","
78 "\"params\":{"
79 "\"text\":%s,"
80 "\"language\":\"%s\","
81 "\"originCountry\":\"%s\","
82 "\"key\":%s"
83 "}"
84 "}";
85 std::string api_key = base::GetQuotedJSONString(google_apis::GetAPIKey());
86 std::string request = base::StringPrintf(
87 kSpellingRequest,
88 type,
89 encoded_text.c_str(),
90 language_code.c_str(),
91 country_code.c_str(),
92 api_key.c_str());
94 GURL url = GURL(kSpellingServiceURL);
95 net::URLFetcher* fetcher = CreateURLFetcher(url);
96 fetcher->SetRequestContext(context->GetRequestContext());
97 fetcher->SetUploadData("application/json", request);
98 fetcher->SetLoadFlags(
99 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES);
100 spellcheck_fetchers_[fetcher] = new TextCheckCallbackData(callback, text);
101 fetcher->Start();
102 return true;
105 bool SpellingServiceClient::IsAvailable(
106 content::BrowserContext* context,
107 ServiceType type) {
108 const PrefService* pref = user_prefs::UserPrefs::Get(context);
109 DCHECK(pref);
110 // If prefs don't allow spellchecking or if the context is off the record,
111 // the spelling service should be unavailable.
112 if (!pref->GetBoolean(prefs::kEnableContinuousSpellcheck) ||
113 !pref->GetBoolean(prefs::kSpellCheckUseSpellingService) ||
114 context->IsOffTheRecord())
115 return false;
117 // If the locale for spelling has not been set, the user has not decided to
118 // use spellcheck so we don't do anything remote (suggest or spelling).
119 std::string locale = pref->GetString(prefs::kSpellCheckDictionary);
120 if (locale.empty())
121 return false;
123 // If we do not have the spelling service enabled, then we are only available
124 // for SUGGEST.
125 const CommandLine* command_line = CommandLine::ForCurrentProcess();
126 if (command_line->HasSwitch(switches::kUseSpellingSuggestions))
127 return type == SUGGEST;
129 // Finally, if all options are available, we only enable only SUGGEST
130 // if SPELLCHECK is not available for our language because SPELLCHECK results
131 // are a superset of SUGGEST results.
132 // TODO(rlp): Only available for English right now. Fix this line to include
133 // all languages SPELLCHECK covers.
134 bool language_available = !locale.compare(0, 2, "en");
135 if (language_available) {
136 return type == SPELLCHECK;
137 } else {
138 // Only SUGGEST is allowed.
139 return type == SUGGEST;
143 bool SpellingServiceClient::ParseResponse(
144 const std::string& data,
145 std::vector<SpellCheckResult>* results) {
146 // When this JSON-RPC call finishes successfully, the Spelling service returns
147 // an JSON object listed below.
148 // * result - an envelope object representing the result from the APIARY
149 // server, which is the JSON-API front-end for the Spelling service. This
150 // object consists of the following variable:
151 // - spellingCheckResponse (SpellingCheckResponse).
152 // * SpellingCheckResponse - an object representing the result from the
153 // Spelling service. This object consists of the following variable:
154 // - misspellings (optional array of Misspelling)
155 // * Misspelling - an object representing a misspelling region and its
156 // suggestions. This object consists of the following variables:
157 // - charStart (number) - the beginning of the misspelled region;
158 // - charLength (number) - the length of the misspelled region;
159 // - suggestions (array of string) - the suggestions for the misspelling
160 // text, and;
161 // - canAutoCorrect (optional boolean) - whether we can use the first
162 // suggestion for auto-correction.
163 // For example, the Spelling service returns the following JSON when we send a
164 // spelling request for "duck goes quisk" as of 16 August, 2011.
165 // {
166 // "result": {
167 // "spellingCheckResponse": {
168 // "misspellings": [{
169 // "charStart": 10,
170 // "charLength": 5,
171 // "suggestions": [{ "suggestion": "quack" }],
172 // "canAutoCorrect": false
173 // }]
174 // }
175 // }
176 // }
177 // If the service is not available, the Spelling service returns JSON with an
178 // error.
179 // {
180 // "error": {
181 // "code": 400,
182 // "message": "Bad Request",
183 // "data": [...]
184 // }
185 // }
186 scoped_ptr<base::DictionaryValue> value(
187 static_cast<base::DictionaryValue*>(
188 base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS)));
189 if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
190 return false;
192 // Check for errors from spelling service.
193 base::DictionaryValue* error = NULL;
194 if (value->GetDictionary(kErrorPath, &error))
195 return false;
197 // Retrieve the array of Misspelling objects. When the input text does not
198 // have misspelled words, it returns an empty JSON. (In this case, its HTTP
199 // status is 200.) We just return true for this case.
200 base::ListValue* misspellings = NULL;
201 if (!value->GetList(kMisspellingsPath, &misspellings))
202 return true;
204 for (size_t i = 0; i < misspellings->GetSize(); ++i) {
205 // Retrieve the i-th misspelling region and put it to the given vector. When
206 // the Spelling service sends two or more suggestions, we read only the
207 // first one because SpellCheckResult can store only one suggestion.
208 base::DictionaryValue* misspelling = NULL;
209 if (!misspellings->GetDictionary(i, &misspelling))
210 return false;
212 int start = 0;
213 int length = 0;
214 base::ListValue* suggestions = NULL;
215 if (!misspelling->GetInteger("charStart", &start) ||
216 !misspelling->GetInteger("charLength", &length) ||
217 !misspelling->GetList("suggestions", &suggestions)) {
218 return false;
221 base::DictionaryValue* suggestion = NULL;
222 base::string16 replacement;
223 if (!suggestions->GetDictionary(0, &suggestion) ||
224 !suggestion->GetString("suggestion", &replacement)) {
225 return false;
227 SpellCheckResult result(
228 SpellCheckResult::SPELLING, start, length, replacement);
229 results->push_back(result);
231 return true;
234 SpellingServiceClient::TextCheckCallbackData::TextCheckCallbackData(
235 TextCheckCompleteCallback callback,
236 base::string16 text)
237 : callback(callback),
238 text(text) {
241 SpellingServiceClient::TextCheckCallbackData::~TextCheckCallbackData() {
244 void SpellingServiceClient::OnURLFetchComplete(
245 const net::URLFetcher* source) {
246 DCHECK(spellcheck_fetchers_[source]);
247 scoped_ptr<const net::URLFetcher> fetcher(source);
248 scoped_ptr<TextCheckCallbackData>
249 callback_data(spellcheck_fetchers_[fetcher.get()]);
250 bool success = false;
251 std::vector<SpellCheckResult> results;
252 if (fetcher->GetResponseCode() / 100 == 2) {
253 std::string data;
254 fetcher->GetResponseAsString(&data);
255 success = ParseResponse(data, &results);
257 callback_data->callback.Run(success, callback_data->text, results);
258 spellcheck_fetchers_.erase(fetcher.get());
261 net::URLFetcher* SpellingServiceClient::CreateURLFetcher(const GURL& url) {
262 return net::URLFetcher::Create(url, net::URLFetcher::POST, this);