diff options
Diffstat (limited to 'chrome/browser')
7 files changed, 315 insertions, 19 deletions
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.cc b/chrome/browser/spellchecker/spellcheck_message_filter.cc index eb76417..47c64de 100644 --- a/chrome/browser/spellchecker/spellcheck_message_filter.cc +++ b/chrome/browser/spellchecker/spellcheck_message_filter.cc @@ -117,11 +117,12 @@ void SpellCheckMessageFilter::OnCallSpellingService( void SpellCheckMessageFilter::OnTextCheckComplete( int tag, + bool success, const std::vector<SpellCheckResult>& results) { Send(new SpellCheckMsg_RespondSpellingService(route_id_, identifier_, tag, - true, + success, string16(), results)); client_.reset(); diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.h b/chrome/browser/spellchecker/spellcheck_message_filter.h index 5b3926e..527c1c2 100644 --- a/chrome/browser/spellchecker/spellcheck_message_filter.h +++ b/chrome/browser/spellchecker/spellcheck_message_filter.h @@ -41,6 +41,7 @@ class SpellCheckMessageFilter : public content::BrowserMessageFilter { // text. We send the given results to a renderer. void OnTextCheckComplete( int tag, + bool success, const std::vector<SpellCheckResult>& results); // Checks the user profile and sends a JSON-RPC request to the Spelling diff --git a/chrome/browser/spellchecker/spelling_service_client.cc b/chrome/browser/spellchecker/spelling_service_client.cc index 1b406dd..c8429b5 100644 --- a/chrome/browser/spellchecker/spelling_service_client.cc +++ b/chrome/browser/spellchecker/spelling_service_client.cc @@ -25,8 +25,12 @@ // Use the public URL to the Spelling service on Chromium. Unfortunately, this // service is an experimental service and returns an error without a key. -#ifndef SPELLING_SERVICE_KEY -#define SPELLING_SERVICE_KEY +#ifndef SPELLING_SERVICE_KEY_V1 +#define SPELLING_SERVICE_KEY_V1 "" +#endif + +#ifndef SPELLING_SERVICE_KEY_V2 +#define SPELLING_SERVICE_KEY_V2 "" #endif #ifndef SPELLING_SERVICE_URL @@ -51,7 +55,7 @@ bool SpellingServiceClient::RequestTextCheck( return false; std::string locale = profile->GetPrefs()->GetString( prefs::kSpellCheckDictionary); - char language[ULOC_LANG_CAPACITY] = "en"; + char language[ULOC_LANG_CAPACITY] = ULOC_ENGLISH; const char* country = "USA"; if (!locale.empty()) { // Create the parameters needed by Spelling API. Spelling API needs three @@ -83,18 +87,20 @@ bool SpellingServiceClient::RequestTextCheck( "\"text\":\"%s\"," "\"language\":\"%s\"," "\"origin_country\":\"%s\"," - "\"key\":\"" SPELLING_SERVICE_KEY "\"" + "\"key\":\"%s\"" "}" "}"; - std::string request = base::StringPrintf(kSpellingRequest, - type, - encoded_text.c_str(), - language, country); + std::string request = base::StringPrintf( + kSpellingRequest, + type, + encoded_text.c_str(), + language, + country, + type == SUGGEST ? SPELLING_SERVICE_KEY_V1 : SPELLING_SERVICE_KEY_V2); static const char kSpellingServiceURL[] = SPELLING_SERVICE_URL; GURL url = GURL(kSpellingServiceURL); - fetcher_.reset(content::URLFetcher::Create( - url, content::URLFetcher::POST, this)); + fetcher_.reset(CreateURLFetcher(url)); fetcher_->SetRequestContext(context); fetcher_->SetUploadData("application/json", request); fetcher_->SetLoadFlags( @@ -107,15 +113,19 @@ bool SpellingServiceClient::RequestTextCheck( void SpellingServiceClient::OnURLFetchComplete( const content::URLFetcher* source) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); scoped_ptr<content::URLFetcher> clean_up_fetcher(fetcher_.release()); + bool success = false; std::vector<SpellCheckResult> results; if (source->GetResponseCode() / 100 == 2) { std::string data; source->GetResponseAsString(&data); - ParseResponse(data, &results); + success = ParseResponse(data, &results); } - callback_.Run(tag_, results); + callback_.Run(tag_, success, results); +} + +content::URLFetcher* SpellingServiceClient::CreateURLFetcher(const GURL& url) { + return content::URLFetcher::Create(url, content::URLFetcher::POST, this); } bool SpellingServiceClient::ParseResponse( @@ -158,13 +168,13 @@ bool SpellingServiceClient::ParseResponse( if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) return false; - // Retrieve the array of Misspelling objects. When an internal error happens - // in the Spelling service, it returns a JSON representing the internal error. - // (In this case, its HTTP status is 200.) We just return false for this case. + // Retrieve the array of Misspelling objects. When the input text does not + // have misspelled words, it returns an empty JSON. (In this case, its HTTP + // status is 200.) We just return true for this case. ListValue* misspellings = NULL; const char kMisspellings[] = "result.spellingCheckResponse.misspellings"; if (!value->GetList(kMisspellings, &misspellings)) - return false; + return true; for (size_t i = 0; i < misspellings->GetSize(); ++i) { // Retrieve the i-th misspelling region and put it to the given vector. When diff --git a/chrome/browser/spellchecker/spelling_service_client.h b/chrome/browser/spellchecker/spelling_service_client.h index 3a80fc8..279ae98 100644 --- a/chrome/browser/spellchecker/spelling_service_client.h +++ b/chrome/browser/spellchecker/spelling_service_client.h @@ -15,6 +15,7 @@ #include "base/string16.h" #include "content/public/common/url_fetcher_delegate.h" +class GURL; class Profile; class TextCheckClientDelegate; struct SpellCheckResult; @@ -34,6 +35,7 @@ struct SpellCheckResult; // // void OnTextCheckComplete( // int tag, +// bool success, // const std::vector<SpellCheckResult>& results) { // ... // } @@ -62,6 +64,7 @@ class SpellingServiceClient : public content::URLFetcherDelegate { }; typedef base::Callback<void( int /* tag */, + bool /* success */, const std::vector<SpellCheckResult>& /* results */)> TextCheckCompleteCallback; @@ -82,6 +85,11 @@ class SpellingServiceClient : public content::URLFetcherDelegate { const TextCheckCompleteCallback& callback); private: + // Creates a URLFetcher object used for sending a JSON-RPC request. This + // function is overriden by unit tests to prevent them from actually sending + // requests to the Spelling service. + virtual content::URLFetcher* CreateURLFetcher(const GURL& url); + // Parses a JSON-RPC response from the Spelling service. bool ParseResponse(const std::string& data, std::vector<SpellCheckResult>* results); diff --git a/chrome/browser/spellchecker/spelling_service_client_unittest.cc b/chrome/browser/spellchecker/spelling_service_client_unittest.cc new file mode 100644 index 0000000..7357937 --- /dev/null +++ b/chrome/browser/spellchecker/spelling_service_client_unittest.cc @@ -0,0 +1,274 @@ +// 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 <string> +#include <vector> + +#include "base/json/json_reader.h" +#include "base/memory/scoped_ptr.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/spellchecker/spelling_service_client.h" +#include "chrome/common/spellcheck_result.h" +#include "chrome/test/base/testing_profile.h" +#include "content/test/test_url_fetcher_factory.h" +#include "net/base/load_flags.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// A mock URL fetcher used in the TestingSpellingServiceClient class. This class +// verifies JSON-RPC requests when the SpellingServiceClient class sends them to +// the Spelling service. This class also verifies the SpellingServiceClient +// class does not either send cookies to the Spelling service or accept cookies +// from it. +class TestSpellingURLFetcher : public TestURLFetcher { + public: + TestSpellingURLFetcher(int id, + const GURL& url, + content::URLFetcherDelegate* d, + int version, + const std::string& text, + int status, + const std::string& response) + : TestURLFetcher(0, url, d), + version_(base::StringPrintf("v%d", version)), + text_(text) { + set_response_code(status); + SetResponseString(response); + } + virtual ~TestSpellingURLFetcher() { + } + + virtual void SetUploadData(const std::string& upload_content_type, + const std::string& upload_content) OVERRIDE { + // Verify the given content type is JSON. (The Spelling service returns an + // internal server error when this content type is not JSON.) + EXPECT_EQ("application/json", upload_content_type); + + // Parse the JSON to be sent to the service, and verify its parameters. + scoped_ptr<DictionaryValue> value(static_cast<DictionaryValue*>( + base::JSONReader::Read(upload_content, + base::JSON_ALLOW_TRAILING_COMMAS))); + ASSERT_TRUE(!!value.get()); + std::string method; + EXPECT_TRUE(value->GetString("method", &method)); + EXPECT_EQ("spelling.check", method); + std::string version; + EXPECT_TRUE(value->GetString("apiVersion", &version)); + EXPECT_EQ(version_, version); + std::string text; + EXPECT_TRUE(value->GetString("params.text", &text)); + EXPECT_EQ(text_, text); + std::string language; + EXPECT_TRUE(value->GetString("params.language", &language)); + EXPECT_EQ("en", language); + std::string country; + EXPECT_TRUE(value->GetString("params.origin_country", &country)); + EXPECT_EQ("USA", country); + + TestURLFetcher::SetUploadData(upload_content_type, upload_content); + } + + virtual void Start() OVERRIDE { + // Verify that this client does not either send cookies to the Spelling + // service or accept cookies from it. + EXPECT_EQ(net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES, + GetLoadFlags()); + } + + private: + std::string version_; + std::string text_; +}; + +// A class derived from the SpellingServiceClient class used by the +// SpellingServiceClientTest class. This class overrides CreateURLFetcher so +// this test can use TestSpellingURLFetcher. +class TestingSpellingServiceClient : public SpellingServiceClient { + public: + TestingSpellingServiceClient() + : request_type_(0), + response_status_(0), + success_(false), + fetcher_(NULL) { + } + virtual ~TestingSpellingServiceClient() { + } + + void SetHTTPRequest(int type, const std::string& text) { + request_type_ = type; + request_text_ = text; + } + + void SetHTTPResponse(int status, const char* data) { + response_status_ = status; + response_data_.assign(data); + } + + void SetExpectedTextCheckResult(bool success, const char* text) { + success_ = success; + corrected_text_.assign(UTF8ToUTF16(text)); + } + + void CallOnURLFetchComplete() { + ASSERT_TRUE(!!fetcher_); + fetcher_->delegate()->OnURLFetchComplete(fetcher_); + fetcher_ = NULL; + } + + void VerifyResponse(bool success, + const std::vector<SpellCheckResult>& results) { + EXPECT_EQ(success_, success); + string16 text(UTF8ToUTF16(request_text_)); + for (std::vector<SpellCheckResult>::const_iterator it = results.begin(); + it != results.end(); ++it) { + text.replace(it->location, it->length, it->replacement); + } + EXPECT_EQ(corrected_text_, text); + } + + private: + virtual content::URLFetcher* CreateURLFetcher(const GURL& url) { + EXPECT_EQ("https://www.googleapis.com/rpc", url.spec()); + fetcher_ = new TestSpellingURLFetcher(0, url, this, + request_type_, request_text_, + response_status_, response_data_); + return fetcher_; + } + + int request_type_; + std::string request_text_; + int response_status_; + std::string response_data_; + bool success_; + string16 corrected_text_; + TestSpellingURLFetcher* fetcher_; // weak +}; + +// A test class used for testing the SpellingServiceClient class. This class +// implements a callback function used by the SpellingServiceClient class to +// monitor the class calls the callback with expected results. +class SpellingServiceClientTest : public testing::Test { + public: + SpellingServiceClientTest() { + profile_.CreateRequestContext(); + } + virtual ~SpellingServiceClientTest() {} + + void OnTextCheckComplete(int tag, + bool success, + const std::vector<SpellCheckResult>& results) { + client_.VerifyResponse(success, results); + } + + protected: + TestingSpellingServiceClient client_; + TestingProfile profile_; +}; + +} // namespace + +// Verifies that SpellingServiceClient::RequestTextCheck() creates a JSON +// request sent to the Spelling service as we expect. This test also verifies +// that it parses a JSON response from the service and calls the callback +// function. To avoid sending JSON-RPC requests to the service, this test uses a +// custom TestURLFecher class (TestSpellingURLFetcher) which calls +// SpellingServiceClient::OnURLFetchComplete() with the parameters set by this +// test. This test also uses a custom callback function that replaces all +// misspelled words with ones suggested by the service so this test can compare +// the corrected text with the expected results. (If there are not any +// misspelled words, |corrected_text| should be equal to |request_text|.) +TEST_F(SpellingServiceClientTest, RequestTextCheck) { + static const struct { + const char* request_text; + SpellingServiceClient::ServiceType request_type; + int response_status; + const char* response_data; + bool success; + const char* corrected_text; + } kTests[] = { + { + "", + SpellingServiceClient::SUGGEST, + 500, + "", + false, + "", + }, { + "chromebook", + SpellingServiceClient::SUGGEST, + 200, + "{}", + true, + "chromebook", + }, { + "chrombook", + SpellingServiceClient::SUGGEST, + 200, + "{\n" + " \"result\": {\n" + " \"spellingCheckResponse\": {\n" + " \"misspellings\": [{\n" + " \"charStart\": 0,\n" + " \"charLength\": 9,\n" + " \"suggestions\": [{ \"suggestion\": \"chromebook\" }],\n" + " \"canAutoCorrect\": false\n" + " }]\n" + " }\n" + " }\n" + "}", + true, + "chromebook", + }, { + "", + SpellingServiceClient::SPELLCHECK, + 500, + "", + false, + "", + }, { + "I have been to USA.", + SpellingServiceClient::SPELLCHECK, + 200, + "{}", + true, + "I have been to USA.", + }, { + "I have bean to USA.", + SpellingServiceClient::SPELLCHECK, + 200, + "{\n" + " \"result\": {\n" + " \"spellingCheckResponse\": {\n" + " \"misspellings\": [{\n" + " \"charStart\": 7,\n" + " \"charLength\": 4,\n" + " \"suggestions\": [{ \"suggestion\": \"been\" }],\n" + " \"canAutoCorrect\": false\n" + " }]\n" + " }\n" + " }\n" + "}", + true, + "I have been to USA.", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { + client_.SetHTTPRequest(kTests[i].request_type, kTests[i].request_text); + client_.SetHTTPResponse(kTests[i].response_status, kTests[i].response_data); + client_.SetExpectedTextCheckResult(kTests[i].success, + kTests[i].corrected_text); + client_.RequestTextCheck( + &profile_, + 0, + kTests[i].request_type, + ASCIIToUTF16(kTests[i].request_text), + base::Bind(&SpellingServiceClientTest::OnTextCheckComplete, + base::Unretained(this))); + client_.CallOnURLFetchComplete(); + } +} diff --git a/chrome/browser/tab_contents/spelling_menu_observer.cc b/chrome/browser/tab_contents/spelling_menu_observer.cc index 4bd0489..398c03c 100644 --- a/chrome/browser/tab_contents/spelling_menu_observer.cc +++ b/chrome/browser/tab_contents/spelling_menu_observer.cc @@ -246,6 +246,7 @@ void SpellingMenuObserver::ExecuteCommand(int command_id) { void SpellingMenuObserver::OnTextCheckComplete( int tag, + bool success, const std::vector<SpellCheckResult>& results) { animation_timer_.Stop(); @@ -253,7 +254,7 @@ void SpellingMenuObserver::OnTextCheckComplete( // suggested words. If the replaced text is included in the suggestion list // provided by the local spellchecker, we show a "No suggestions from Google" // message. - succeeded_ = true; + succeeded_ = success; if (results.empty()) { succeeded_ = false; } else { diff --git a/chrome/browser/tab_contents/spelling_menu_observer.h b/chrome/browser/tab_contents/spelling_menu_observer.h index 445312c..a5f2339 100644 --- a/chrome/browser/tab_contents/spelling_menu_observer.h +++ b/chrome/browser/tab_contents/spelling_menu_observer.h @@ -53,6 +53,7 @@ class SpellingMenuObserver : public RenderViewContextMenuObserver { // misspelled word. void OnTextCheckComplete( int tag, + bool success, const std::vector<SpellCheckResult>& results); private: |