summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-25 03:32:10 +0000
committerrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-25 03:32:10 +0000
commite0c45d7f8ab8e057702a31239266367b467e5913 (patch)
tree5ef7579ac1a983552f219f692138721b41f75fbe
parent86f1a7369e976faba6581eaca7d8a38d7c0aa6c6 (diff)
downloadchromium_src-e0c45d7f8ab8e057702a31239266367b467e5913.zip
chromium_src-e0c45d7f8ab8e057702a31239266367b467e5913.tar.gz
chromium_src-e0c45d7f8ab8e057702a31239266367b467e5913.tar.bz2
Send IN_DICTIONARY feedback when user has misspelling in custom dictionary
When a user types a word that is misspelled and is in the custom dictionary, then the spelling service client will generate a spellcheck result for the misspelling anyway. The feedback sender sends an IN_DICTIONARY message to the spelling service to indicate that the user cannot see this spellcheck result and, therefore, cannot provide any other feedback on the spellcheck suggestion quality. BUG=170514 Review URL: https://chromiumcodereview.appspot.com/15897005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208380 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/spellchecker/feedback_sender.cc17
-rw-r--r--chrome/browser/spellchecker/feedback_sender.h5
-rw-r--r--chrome/browser/spellchecker/feedback_sender_unittest.cc275
-rw-r--r--chrome/browser/spellchecker/spellcheck_custom_dictionary.cc2
-rw-r--r--chrome/browser/spellchecker/spellcheck_custom_dictionary.h2
-rw-r--r--chrome/browser/spellchecker/spellcheck_message_filter.cc54
-rw-r--r--chrome/browser/spellchecker/spellcheck_message_filter.h6
-rw-r--r--chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc123
8 files changed, 294 insertions, 190 deletions
diff --git a/chrome/browser/spellchecker/feedback_sender.cc b/chrome/browser/spellchecker/feedback_sender.cc
index 3b72c69..c250cf4 100644
--- a/chrome/browser/spellchecker/feedback_sender.cc
+++ b/chrome/browser/spellchecker/feedback_sender.cc
@@ -155,6 +155,8 @@ FeedbackSender::~FeedbackSender() {
void FeedbackSender::SelectedSuggestion(uint32 hash, int suggestion_index) {
Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+ // when the session expires every |kSessionHours| hours.
if (!misspelling)
return;
misspelling->action.type = SpellcheckAction::TYPE_SELECT;
@@ -164,6 +166,8 @@ void FeedbackSender::SelectedSuggestion(uint32 hash, int suggestion_index) {
void FeedbackSender::AddedToDictionary(uint32 hash) {
Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+ // when the session expires every |kSessionHours| hours.
if (!misspelling)
return;
misspelling->action.type = SpellcheckAction::TYPE_ADD_TO_DICT;
@@ -181,8 +185,19 @@ void FeedbackSender::AddedToDictionary(uint32 hash) {
}
}
+void FeedbackSender::RecordInDictionary(uint32 hash) {
+ Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+ // when the session expires every |kSessionHours| hours.
+ if (!misspelling)
+ return;
+ misspelling->action.type = SpellcheckAction::TYPE_IN_DICTIONARY;
+}
+
void FeedbackSender::IgnoredSuggestions(uint32 hash) {
Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+ // when the session expires every |kSessionHours| hours.
if (!misspelling)
return;
misspelling->action.type = SpellcheckAction::TYPE_PENDING_IGNORE;
@@ -192,6 +207,8 @@ void FeedbackSender::IgnoredSuggestions(uint32 hash) {
void FeedbackSender::ManuallyCorrected(uint32 hash,
const string16& correction) {
Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ // GetMisspelling() returns null for flushed feedback. Feedback is flushed
+ // when the session expires every |kSessionHours| hours.
if (!misspelling)
return;
misspelling->action.type = SpellcheckAction::TYPE_MANUALLY_CORRECTED;
diff --git a/chrome/browser/spellchecker/feedback_sender.h b/chrome/browser/spellchecker/feedback_sender.h
index dbcaba2..5a89f7b 100644
--- a/chrome/browser/spellchecker/feedback_sender.h
+++ b/chrome/browser/spellchecker/feedback_sender.h
@@ -18,7 +18,6 @@
#include "net/url_request/url_fetcher_delegate.h"
class SpellCheckMarker;
-class SpellingServiceFeedbackTest;
struct SpellCheckResult;
namespace net {
@@ -58,6 +57,10 @@ class FeedbackSender : public base::SupportsWeakPtr<FeedbackSender>,
// the list of suggestions.
void ManuallyCorrected(uint32 hash, const string16& correction);
+ // Records that user has the misspelling in the custom dictionary. The user
+ // will never see the spellcheck suggestions for the misspelling.
+ void RecordInDictionary(uint32 hash);
+
// Receives document markers for renderer with process ID |render_process_id|
// when the renderer responds to a RequestDocumentMarkers() call. Finalizes
// feedback for the markers that are gone from the renderer. Sends feedback
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc
index 262b009..5504005 100644
--- a/chrome/browser/spellchecker/feedback_sender_unittest.cc
+++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -43,6 +43,18 @@ SpellCheckResult BuildSpellCheckResult() {
ASCIIToUTF16("Hello"));
}
+// Returns the number of times that |needle| appears in |haystack| without
+// overlaps. For example, CountOccurences("bananana", "nana") returns 1.
+int CountOccurences(const std::string& haystack, const std::string& needle) {
+ int number_of_occurrences = 0;
+ for (size_t pos = haystack.find(needle);
+ pos != std::string::npos;
+ pos = haystack.find(needle, pos + needle.length())) {
+ ++number_of_occurrences;
+ }
+ return number_of_occurrences;
+}
+
} // namespace
class FeedbackSenderTest : public testing::Test {
@@ -70,6 +82,7 @@ class FeedbackSenderTest : public testing::Test {
content::TestBrowserThread ui_thread_;
scoped_ptr<base::FieldTrialList> field_trial_list_;
scoped_refptr<base::FieldTrial> field_trial_;
+ net::TestURLFetcherFactory fetchers_;
protected:
uint32 AddPendingFeedback() {
@@ -85,30 +98,55 @@ class FeedbackSenderTest : public testing::Test {
base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours);
}
- net::TestURLFetcher* GetFetcher() {
- return fetchers_.GetFetcherByID(kUrlFetcherId);
+ bool UploadDataContains(const std::string& data) const {
+ const net::TestURLFetcher* fetcher =
+ fetchers_.GetFetcherByID(kUrlFetcherId);
+ return fetcher && CountOccurences(fetcher->upload_data(), data) > 0;
+ }
+
+ bool UploadDataContains(const std::string& data,
+ int number_of_occurrences) const {
+ const net::TestURLFetcher* fetcher =
+ fetchers_.GetFetcherByID(kUrlFetcherId);
+ return fetcher && CountOccurences(fetcher->upload_data(), data) ==
+ number_of_occurrences;
+ }
+
+ // Returns true if the feedback sender would be uploading data now. Otherwise
+ // returns false. The test does not open network connections.
+ bool IsUploadingData() const {
+ return !!fetchers_.GetFetcherByID(kUrlFetcherId);
+ }
+
+ void ClearUploadData() {
+ fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ }
+
+ std::string GetUploadData() const {
+ const net::TestURLFetcher* fetcher =
+ fetchers_.GetFetcherByID(kUrlFetcherId);
+ return fetcher ? fetcher->upload_data() : std::string();
}
- net::TestURLFetcherFactory fetchers_;
scoped_ptr<spellcheck::FeedbackSender> feedback_;
};
// Do not send data if there's no feedback.
TEST_F(FeedbackSenderTest, NoFeedback) {
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
}
// Do not send data if not aware of which markers are still in the document.
TEST_F(FeedbackSenderTest, NoDocumentMarkersReceived) {
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
uint32 hash = AddPendingFeedback();
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
static const int kSuggestionIndex = 1;
feedback_->SelectedSuggestion(hash, kSuggestionIndex);
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
}
// Send PENDING feedback message if the marker is still in the document, and the
@@ -117,10 +155,7 @@ TEST_F(FeedbackSenderTest, PendingFeedback) {
uint32 hash = AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>(1, hash));
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
}
// Send NO_ACTION feedback message if the marker has been removed from the
@@ -129,26 +164,18 @@ TEST_F(FeedbackSenderTest, NoActionFeedback) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"NO_ACTION\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
}
// Send SELECT feedback message if the user has selected a spelling suggestion.
TEST_F(FeedbackSenderTest, SelectFeedback) {
uint32 hash = AddPendingFeedback();
- static const int kSuggestion = 0;
- feedback_->SelectedSuggestion(hash, kSuggestion);
+ static const int kSuggestionIndex = 0;
+ feedback_->SelectedSuggestion(hash, kSuggestionIndex);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"SELECT\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionTargetIndex\":" + kSuggestion))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"SELECT\""));
+ EXPECT_TRUE(UploadDataContains("\"actionTargetIndex\":" + kSuggestionIndex));
}
// Send ADD_TO_DICT feedback message if the user has added the misspelled word
@@ -158,10 +185,17 @@ TEST_F(FeedbackSenderTest, AddToDictFeedback) {
feedback_->AddedToDictionary(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"ADD_TO_DICT\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"ADD_TO_DICT\""));
+}
+
+// Send IN_DICTIONARY feedback message if the user has the misspelled word in
+// the custom dictionary.
+TEST_F(FeedbackSenderTest, InDictionaryFeedback) {
+ uint32 hash = AddPendingFeedback();
+ feedback_->RecordInDictionary(hash);
+ feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
+ std::vector<uint32>());
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"IN_DICTIONARY\""));
}
// Send PENDING feedback message if the user saw the spelling suggestion, but
@@ -171,10 +205,7 @@ TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerInDocument) {
feedback_->IgnoredSuggestions(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>(1, hash));
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
}
// Send IGNORE feedback message if the user saw the spelling suggestion, but
@@ -184,10 +215,7 @@ TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerNotInDocument) {
feedback_->IgnoredSuggestions(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"IGNORE\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\""));
}
// Send MANUALLY_CORRECTED feedback message if the user manually corrected the
@@ -198,15 +226,9 @@ TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) {
feedback_->ManuallyCorrected(hash, ASCIIToUTF16(kManualCorrection));
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(
- std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"MANUALLY_CORRECTED\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionTargetValue\":\"" +
- kManualCorrection + "\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\""));
+ EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" +
+ kManualCorrection + "\""));
}
// Send feedback messages in batch.
@@ -226,11 +248,7 @@ TEST_F(FeedbackSenderTest, BatchFeedback) {
&results, kRendererProcessId, kText, std::vector<SpellCheckMarker>());
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- size_t pos = fetcher->upload_data().find("\"actionType\":\"NO_ACTION\"");
- EXPECT_NE(std::string::npos, pos) << fetcher->upload_data();
- pos = fetcher->upload_data().find("\"actionType\":\"NO_ACTION\"", pos + 1);
- EXPECT_NE(std::string::npos, pos) << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2));
}
// Send a series of PENDING feedback messages and one final NO_ACTION feedback
@@ -240,37 +258,25 @@ TEST_F(FeedbackSenderTest, SameHashFeedback) {
std::vector<uint32> remaining_markers(1, hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
std::string hash_string = base::StringPrintf("\"suggestionId\":\"%u\"", hash);
- EXPECT_NE(std::string::npos, fetcher->upload_data().find(hash_string))
- << fetcher->upload_data();
- fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ EXPECT_TRUE(UploadDataContains(hash_string));
+ ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos, fetcher->upload_data().find(hash_string))
- << fetcher->upload_data();
- fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+ EXPECT_TRUE(UploadDataContains(hash_string));
+ ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"NO_ACTION\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos, fetcher->upload_data().find(hash_string))
- << fetcher->upload_data();
- fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+ EXPECT_TRUE(UploadDataContains(hash_string));
+ ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
}
// When a session expires:
@@ -291,40 +297,26 @@ TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
std::vector<uint32> remaining_markers(1, original_hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_EQ(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"NO_ACTION\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
+ EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
std::string original_hash_string =
base::StringPrintf("\"suggestionId\":\"%u\"", original_hash);
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find(original_hash_string))
- << fetcher->upload_data();
- fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ EXPECT_TRUE(UploadDataContains(original_hash_string));
+ ClearUploadData();
ExpireSession();
// Last message batch in the current session has only finalized messages.
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"NO_ACTION\""))
- << fetcher->upload_data();
- EXPECT_EQ(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find(original_hash_string))
- << fetcher->upload_data();
- fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+ EXPECT_FALSE(UploadDataContains("\"actionType\":\"PENDING\""));
+ EXPECT_TRUE(UploadDataContains(original_hash_string));
+ ClearUploadData();
// The next session starts on the next spellchecker request. Until then,
// there's no more feedback sent.
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- EXPECT_EQ(NULL, GetFetcher());
+ EXPECT_FALSE(IsUploadingData());
// The first spellcheck request after session expiration creates different
// document marker hash identifiers.
@@ -343,20 +335,12 @@ TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
// The first feedback message batch in session |i + 1| has the new document
// marker hash identifiers.
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
- fetcher = GetFetcher();
- EXPECT_EQ(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"NO_ACTION\""))
- << fetcher->upload_data();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"actionType\":\"PENDING\""))
- << fetcher->upload_data();
- EXPECT_EQ(std::string::npos,
- fetcher->upload_data().find(original_hash_string))
- << fetcher->upload_data();
+ EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
+ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
+ EXPECT_FALSE(UploadDataContains(original_hash_string));
std::string updated_hash_string =
base::StringPrintf("\"suggestionId\":\"%u\"", updated_hash);
- EXPECT_NE(std::string::npos, fetcher->upload_data().find(updated_hash_string))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains(updated_hash_string));
}
// First message in session has an indicator.
@@ -365,19 +349,13 @@ TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"isFirstInSession\":true"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
// Session 1, message 2
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"isFirstInSession\":false"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
ExpireSession();
@@ -385,45 +363,29 @@ TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"isFirstInSession\":false"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
// Session 2, message 1
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"isFirstInSession\":true"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
// Session 2, message 2
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"isFirstInSession\":false"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
}
// Flush all feedback when the spellcheck language and country change.
TEST_F(FeedbackSenderTest, OnLanguageCountryChange) {
AddPendingFeedback();
feedback_->OnLanguageCountryChange("pt", "BR");
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"language\":\"en\""))
- << fetcher->upload_data();
-
+ EXPECT_TRUE(UploadDataContains("\"language\":\"en\""));
AddPendingFeedback();
feedback_->OnLanguageCountryChange("en", "US");
- fetcher = GetFetcher();
- EXPECT_NE(std::string::npos,
- fetcher->upload_data().find("\"language\":\"pt\""))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("\"language\":\"pt\""));
}
// The field names and types should correspond to the API.
@@ -431,7 +393,7 @@ TEST_F(FeedbackSenderTest, FeedbackAPI) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- std::string actual_data = GetFetcher()->upload_data();
+ std::string actual_data = GetUploadData();
scoped_ptr<base::DictionaryValue> actual(
static_cast<base::DictionaryValue*>(base::JSONReader::Read(actual_data)));
actual->SetString("params.key", "TestDummyKey");
@@ -481,22 +443,6 @@ TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) {
EXPECT_EQ(hash, results[0].hash);
}
-namespace {
-
-// Returns the number of times that |needle| appears in |haystack| without
-// overlaps. For example, CountOccurences("bananana", "nana") returns 1.
-int CountOccurences(const std::string& haystack, const std::string& needle) {
- int number_of_occurences = 0;
- size_t pos = haystack.find(needle);
- while (pos != std::string::npos) {
- ++number_of_occurences;
- pos = haystack.find(needle, pos + needle.length());
- }
- return number_of_occurences;
-}
-
-} // namespace
-
// Adding a word to dictionary should trigger ADD_TO_DICT feedback for every
// occurrence of that word.
TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) {
@@ -525,20 +471,14 @@ TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) {
remaining_markers.push_back(results[i].hash);
feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
remaining_markers);
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_EQ(2, CountOccurences(fetcher->upload_data(), "PENDING"))
- << fetcher->upload_data();
- EXPECT_EQ(0, CountOccurences(fetcher->upload_data(), "ADD_TO_DICT"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("PENDING", 2));
+ EXPECT_FALSE(UploadDataContains("ADD_TO_DICT"));
feedback_->AddedToDictionary(results[0].hash);
feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
remaining_markers);
- fetcher = GetFetcher();
- EXPECT_EQ(0, CountOccurences(fetcher->upload_data(), "PENDING"))
- << fetcher->upload_data();
- EXPECT_EQ(2, CountOccurences(fetcher->upload_data(), "ADD_TO_DICT"))
- << fetcher->upload_data();
+ EXPECT_FALSE(UploadDataContains("PENDING"));
+ EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
}
// ADD_TO_DICT feedback for multiple occurrences of a word should trigger only
@@ -551,11 +491,8 @@ TEST_F(FeedbackSenderTest, AddToDictOnlyPending) {
feedback_->AddedToDictionary(add_to_dict_hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
- net::TestURLFetcher* fetcher = GetFetcher();
- EXPECT_EQ(1, CountOccurences(fetcher->upload_data(), "SELECT"))
- << fetcher->upload_data();
- EXPECT_EQ(2, CountOccurences(fetcher->upload_data(), "ADD_TO_DICT"))
- << fetcher->upload_data();
+ EXPECT_TRUE(UploadDataContains("SELECT", 1));
+ EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
}
} // namespace spellcheck
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
index 0cd0c7c..c3d2484 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
@@ -261,7 +261,7 @@ bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) {
return result == VALID_CHANGE;
}
-bool SpellcheckCustomDictionary::HasWord(const std::string& word) {
+bool SpellcheckCustomDictionary::HasWord(const std::string& word) const {
return !!words_.count(word);
}
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
index e54b6fa..848022a 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
@@ -91,7 +91,7 @@ class SpellcheckCustomDictionary : public SpellcheckDictionary,
bool RemoveWord(const std::string& word);
// Returns true if the dictionary contains |word|. Otherwise returns false.
- bool HasWord(const std::string& word);
+ bool HasWord(const std::string& word) const;
// Adds |observer| to be notified of dictionary events and changes.
void AddObserver(Observer* observer);
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.cc b/chrome/browser/spellchecker/spellcheck_message_filter.cc
index 091a66e..e58be59 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/prefs/pref_service.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
@@ -87,23 +88,18 @@ void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() {
void SpellCheckMessageFilter::OnNotifyChecked(const string16& word,
bool misspelled) {
- content::RenderProcessHost* host =
- content::RenderProcessHost::FromID(render_process_id_);
- if (!host)
- return; // Teardown.
- // Delegates to SpellCheckHost which tracks the stats of our spellchecker.
- Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
- SpellcheckService* spellcheck_service =
- SpellcheckServiceFactory::GetForProfile(profile);
- DCHECK(spellcheck_service);
- if (spellcheck_service->GetMetrics())
- spellcheck_service->GetMetrics()->RecordCheckedWordStats(word, misspelled);
+ SpellcheckService* spellcheck = GetSpellcheckService();
+ // Spellcheck service may not be available for a renderer process that is
+ // shutting down.
+ if (!spellcheck)
+ return;
+ if (spellcheck->GetMetrics())
+ spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
}
void SpellCheckMessageFilter::OnRespondDocumentMarkers(
const std::vector<uint32>& markers) {
- SpellcheckService* spellcheck =
- SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+ SpellcheckService* spellcheck = GetSpellcheckService();
// Spellcheck service may not be available for a renderer process that is
// shutting down.
if (!spellcheck)
@@ -137,12 +133,34 @@ void SpellCheckMessageFilter::OnTextCheckComplete(
bool success,
const string16& text,
const std::vector<SpellCheckResult>& results) {
- SpellcheckService* spellcheck =
- SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
- DCHECK(spellcheck);
+ SpellcheckService* spellcheck = GetSpellcheckService();
+ // Spellcheck service may not be available for a renderer process that is
+ // shutting down.
+ if (!spellcheck)
+ return;
std::vector<SpellCheckResult> results_copy = results;
spellcheck->GetFeedbackSender()->OnSpellcheckResults(
&results_copy, render_process_id_, text, markers);
+
+ // Erase custom dictionary words from the spellcheck results and record
+ // in-dictionary feedback.
+ std::vector<SpellCheckResult>::iterator write_iter;
+ std::vector<SpellCheckResult>::iterator iter;
+ std::string text_copy = UTF16ToUTF8(text);
+ for (iter = write_iter = results_copy.begin();
+ iter != results_copy.end();
+ ++iter) {
+ if (spellcheck->GetCustomDictionary()->HasWord(
+ text_copy.substr(iter->location, iter->length))) {
+ spellcheck->GetFeedbackSender()->RecordInDictionary(iter->hash);
+ } else {
+ if (write_iter != iter)
+ *write_iter = *iter;
+ ++write_iter;
+ }
+ }
+ results_copy.erase(write_iter, results_copy.end());
+
Send(new SpellCheckMsg_RespondSpellingService(
route_id, identifier, success, text, results_copy));
}
@@ -171,3 +189,7 @@ void SpellCheckMessageFilter::CallSpellingService(
markers));
}
#endif
+
+SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const {
+ return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.h b/chrome/browser/spellchecker/spellcheck_message_filter.h
index 77c8306..885aab1 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter.h
+++ b/chrome/browser/spellchecker/spellcheck_message_filter.h
@@ -11,6 +11,7 @@
#include "content/public/browser/browser_message_filter.h"
class SpellCheckMarker;
+class SpellcheckService;
struct SpellCheckResult;
// A message filter implementation that receives spell checker requests from
@@ -27,6 +28,8 @@ class SpellCheckMessageFilter : public content::BrowserMessageFilter {
bool* message_was_ok) OVERRIDE;
private:
+ friend class TestingSpellCheckMessageFilter;
+
virtual ~SpellCheckMessageFilter();
void OnSpellCheckerRequestDictionary();
@@ -61,6 +64,9 @@ class SpellCheckMessageFilter : public content::BrowserMessageFilter {
const std::vector<SpellCheckMarker>& markers);
#endif
+ // Can be overridden for testing.
+ virtual SpellcheckService* GetSpellcheckService() const;
+
int render_process_id_;
// A JSON-RPC client that calls the Spelling service in the background.
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
index 3407dbd..d0fb26f 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
@@ -2,12 +2,58 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/common/spellcheck_marker.h"
#include "chrome/common/spellcheck_messages.h"
-#include "content/public/browser/browser_thread.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
#include "ipc/ipc_message.h"
#include "testing/gtest/include/gtest/gtest.h"
+class TestingSpellCheckMessageFilter : public SpellCheckMessageFilter {
+ public:
+ TestingSpellCheckMessageFilter()
+ : SpellCheckMessageFilter(0),
+ ui_thread_(content::BrowserThread::UI, &message_loop_),
+ spellcheck_(new SpellcheckService(&profile_)) {}
+
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ sent_messages.push_back(message);
+ return true;
+ }
+
+ virtual SpellcheckService* GetSpellcheckService() const OVERRIDE {
+ return spellcheck_.get();
+ }
+
+#if !defined(OS_MACOSX)
+ void OnTextCheckComplete(int route_id,
+ int identifier,
+ const std::vector<SpellCheckMarker>& markers,
+ bool success,
+ const string16& text,
+ const std::vector<SpellCheckResult>& results) {
+ SpellCheckMessageFilter::OnTextCheckComplete(
+ route_id, identifier, markers, success, text, results);
+ }
+#endif
+
+ ScopedVector<IPC::Message> sent_messages;
+
+ private:
+ virtual ~TestingSpellCheckMessageFilter() {}
+
+ base::MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ TestingProfile profile_;
+ scoped_ptr<SpellcheckService> spellcheck_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingSpellCheckMessageFilter);
+};
+
TEST(SpellCheckMessageFilterTest, TestOverrideThread) {
static const uint32 kSpellcheckMessages[] = {
SpellCheckHostMsg_RequestDictionary::ID,
@@ -17,9 +63,10 @@ TEST(SpellCheckMessageFilterTest, TestOverrideThread) {
SpellCheckHostMsg_CallSpellingService::ID,
#endif
};
- scoped_refptr<SpellCheckMessageFilter> filter(new SpellCheckMessageFilter(0));
content::BrowserThread::ID thread;
IPC::Message message;
+ scoped_refptr<TestingSpellCheckMessageFilter> filter(
+ new TestingSpellCheckMessageFilter);
for (size_t i = 0; i < arraysize(kSpellcheckMessages); ++i) {
message.SetHeaderValues(
0, kSpellcheckMessages[i], IPC::Message::PRIORITY_NORMAL);
@@ -28,3 +75,75 @@ TEST(SpellCheckMessageFilterTest, TestOverrideThread) {
EXPECT_EQ(content::BrowserThread::UI, thread);
}
}
+
+#if !defined(OS_MACOSX)
+TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTestCustomDictionary) {
+ static const std::string kCustomWord = "Helllo";
+ static const int kRouteId = 0;
+ static const int kCallbackId = 0;
+ static const std::vector<SpellCheckMarker> kMarkers;
+ static const string16 kText = ASCIIToUTF16("Helllo warld.");
+ static const bool kSuccess = true;
+ static const SpellCheckResult::Type kType = SpellCheckResult::SPELLING;
+ static const int kLocation = 7;
+ static const int kLength = 5;
+ static const string16 kReplacement = ASCIIToUTF16("world");
+
+ std::vector<SpellCheckResult> results;
+ results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 0, 6, ASCIIToUTF16("Hello")));
+ results.push_back(SpellCheckResult(kType, kLocation, kLength, kReplacement));
+
+ scoped_refptr<TestingSpellCheckMessageFilter> filter(
+ new TestingSpellCheckMessageFilter);
+ filter->GetSpellcheckService()->GetCustomDictionary()->AddWord(kCustomWord);
+ filter->OnTextCheckComplete(
+ kRouteId, kCallbackId, kMarkers, kSuccess, kText, results);
+ EXPECT_EQ(static_cast<size_t>(1), filter->sent_messages.size());
+
+ int sent_identifier = -1;
+ bool sent_success = false;
+ string16 sent_text;
+ std::vector<SpellCheckResult> sent_results;
+ bool ok = SpellCheckMsg_RespondSpellingService::Read(filter->sent_messages[0],
+ &sent_identifier,
+ &sent_success,
+ &sent_text,
+ &sent_results);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(kCallbackId, sent_identifier);
+ EXPECT_EQ(kSuccess, sent_success);
+ EXPECT_EQ(kText, sent_text);
+ EXPECT_EQ(static_cast<size_t>(1), sent_results.size());
+ EXPECT_EQ(kType, sent_results[0].type);
+ EXPECT_EQ(kLocation, sent_results[0].location);
+ EXPECT_EQ(kLength, sent_results[0].length);
+ EXPECT_EQ(kReplacement, sent_results[0].replacement);
+}
+
+TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTest) {
+ std::vector<SpellCheckResult> results;
+ results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 0, 6, ASCIIToUTF16("Hello")));
+ results.push_back(SpellCheckResult(
+ SpellCheckResult::SPELLING, 7, 7, ASCIIToUTF16("world")));
+
+ scoped_refptr<TestingSpellCheckMessageFilter> filter(
+ new TestingSpellCheckMessageFilter);
+ filter->OnTextCheckComplete(1, 1, std::vector<SpellCheckMarker>(),
+ true, ASCIIToUTF16("Helllo walrd"), results);
+ EXPECT_EQ(static_cast<size_t>(1), filter->sent_messages.size());
+
+ int sent_identifier = -1;
+ bool sent_success = false;
+ string16 sent_text;
+ std::vector<SpellCheckResult> sent_results;
+ bool ok = SpellCheckMsg_RespondSpellingService::Read(filter->sent_messages[0],
+ &sent_identifier,
+ &sent_success,
+ &sent_text,
+ &sent_results);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(static_cast<size_t>(2), sent_results.size());
+}
+#endif