summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-21 07:24:04 +0000
committerrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-21 07:24:04 +0000
commitf71e84058d13cbc3198e72433c9933fd0e56d3b5 (patch)
tree37af5e7241c7f6832a6227f7262c05272db821b1
parent8937251d3fd50990d8879fbba0766d744d057c77 (diff)
downloadchromium_src-f71e84058d13cbc3198e72433c9933fd0e56d3b5.zip
chromium_src-f71e84058d13cbc3198e72433c9933fd0e56d3b5.tar.gz
chromium_src-f71e84058d13cbc3198e72433c9933fd0e56d3b5.tar.bz2
Add ability to send feedback for spelling service
This CL adds the ability to send user feedback for spelling suggestions from spelling service. This feedback will be used to improve the spelling suggestions. The feedback will be collected in a follow-up CL. This CL does not actually send out feedback, because no feedback is collected yet. BUG=170514 Review URL: https://chromiumcodereview.appspot.com/14721010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201246 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/spellchecker/feedback.cc143
-rw-r--r--chrome/browser/spellchecker/feedback.h82
-rw-r--r--chrome/browser/spellchecker/feedback_sender.cc306
-rw-r--r--chrome/browser/spellchecker/feedback_sender.h139
-rw-r--r--chrome/browser/spellchecker/feedback_sender_unittest.cc436
-rw-r--r--chrome/browser/spellchecker/feedback_unittest.cc165
-rw-r--r--chrome/browser/spellchecker/misspelling.cc (renamed from chrome/browser/spellchecker/spellcheck_misspelling.cc)31
-rw-r--r--chrome/browser/spellchecker/misspelling.h (renamed from chrome/browser/spellchecker/spellcheck_misspelling.h)24
-rw-r--r--chrome/browser/spellchecker/misspelling_unittest.cc (renamed from chrome/browser/spellchecker/spellcheck_misspelling_unittest.cc)10
-rw-r--r--chrome/browser/spellchecker/spellcheck_service.cc24
-rw-r--r--chrome/browser/spellchecker/spellcheck_service.h6
-rw-r--r--chrome/browser/spellchecker/spelling_service_feedback.cc35
-rw-r--r--chrome/browser/spellchecker/spelling_service_feedback.h37
-rw-r--r--chrome/chrome_browser.gypi10
-rw-r--r--chrome/chrome_tests_unit.gypi4
-rw-r--r--chrome/common/spellcheck_common.h8
-rw-r--r--chrome/common/spellcheck_result.h6
17 files changed, 1352 insertions, 114 deletions
diff --git a/chrome/browser/spellchecker/feedback.cc b/chrome/browser/spellchecker/feedback.cc
new file mode 100644
index 0000000..7e6ce5f
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 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 "chrome/browser/spellchecker/feedback.h"
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+
+namespace spellcheck {
+
+Feedback::Feedback() {
+}
+
+Feedback::~Feedback() {
+}
+
+Misspelling* Feedback::GetMisspelling(uint32 hash) {
+ std::map<uint32, Misspelling>::iterator it = misspellings_.find(hash);
+ if (it == misspellings_.end())
+ return NULL;
+ return &it->second;
+}
+
+void Feedback::FinalizeRemovedMisspellings(
+ int renderer_process_id,
+ const std::vector<uint32>& remaining_markers) {
+ std::map<int, std::vector<uint32> >::iterator i =
+ hashes_.find(renderer_process_id);
+ if (i == hashes_.end() || i->second.empty())
+ return;
+ std::vector<uint32> remaining_copy(remaining_markers);
+ std::sort(remaining_copy.begin(), remaining_copy.end());
+ std::sort(i->second.begin(), i->second.end());
+ std::vector<uint32> removed_markers;
+ std::set_difference(i->second.begin(),
+ i->second.end(),
+ remaining_copy.begin(),
+ remaining_copy.end(),
+ std::back_inserter(removed_markers));
+ for (std::vector<uint32>::const_iterator j = removed_markers.begin();
+ j != removed_markers.end();
+ ++j) {
+ std::map<uint32, Misspelling>::iterator k = misspellings_.find(*j);
+ if (k != misspellings_.end() && !k->second.action.IsFinal())
+ k->second.action.Finalize();
+ }
+}
+
+bool Feedback::RendererHasMisspellings(int renderer_process_id) const {
+ std::map<int, std::vector<uint32> >::const_iterator it =
+ hashes_.find(renderer_process_id);
+ return it != hashes_.end() && !it->second.empty();
+}
+
+std::vector<Misspelling> Feedback::GetMisspellingsInRenderer(
+ int renderer_process_id) const {
+ std::vector<Misspelling> result;
+ std::map<int, std::vector<uint32> >::const_iterator i =
+ hashes_.find(renderer_process_id);
+ if (i == hashes_.end() || i->second.empty())
+ return result;
+ for (std::vector<uint32>::const_iterator j = i->second.begin();
+ j != i->second.end();
+ ++j) {
+ std::map<uint32, Misspelling>::const_iterator k = misspellings_.find(*j);
+ if (k != misspellings_.end())
+ result.push_back(k->second);
+ }
+ return result;
+}
+
+void Feedback::EraseFinalizedMisspellings(int renderer_process_id) {
+ std::map<int, std::vector<uint32> >::iterator i =
+ hashes_.find(renderer_process_id);
+ if (i == hashes_.end() || i->second.empty())
+ return;
+ std::vector<uint32> pending;
+ for (std::vector<uint32>::const_iterator j = i->second.begin();
+ j != i->second.end();
+ ++j) {
+ std::map<uint32, Misspelling>::iterator k = misspellings_.find(*j);
+ if (k != misspellings_.end()) {
+ if (k->second.action.IsFinal())
+ misspellings_.erase(k);
+ else
+ pending.push_back(*j);
+ }
+ }
+ i->second.swap(pending);
+}
+
+bool Feedback::HasMisspelling(uint32 hash) const {
+ return !!misspellings_.count(hash);
+}
+
+void Feedback::AddMisspelling(int renderer_process_id,
+ const Misspelling& misspelling) {
+ misspellings_[misspelling.hash] = misspelling;
+ hashes_[renderer_process_id].push_back(misspelling.hash);
+}
+
+bool Feedback::Empty() const {
+ return misspellings_.empty();
+}
+
+std::vector<int> Feedback::GetRendersWithMisspellings() const {
+ std::vector<int> result;
+ for (std::map<int, std::vector<uint32> >::const_iterator it = hashes_.begin();
+ it != hashes_.end();
+ ++it) {
+ if (!it->second.empty())
+ result.push_back(it->first);
+ }
+ return result;
+}
+
+void Feedback::FinalizeAllMisspellings() {
+ for (std::map<uint32, Misspelling>::iterator it = misspellings_.begin();
+ it != misspellings_.end();
+ ++it) {
+ if (!it->second.action.IsFinal())
+ it->second.action.Finalize();
+ }
+}
+
+std::vector<Misspelling> Feedback::GetAllMisspellings() const {
+ std::vector<Misspelling> result;
+ for (std::map<uint32, Misspelling>::const_iterator it = misspellings_.begin();
+ it != misspellings_.end();
+ ++it) {
+ result.push_back(it->second);
+ }
+ return result;
+}
+
+void Feedback::Clear() {
+ misspellings_.clear();
+ hashes_.clear();
+}
+
+} // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback.h b/chrome/browser/spellchecker/feedback.h
new file mode 100644
index 0000000..0949002
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 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.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
+#define CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
+
+#include <map>
+#include <vector>
+
+#include "chrome/browser/spellchecker/misspelling.h"
+
+namespace spellcheck {
+
+// Stores feedback for the spelling service in |Misspelling| objects. Each
+// |Misspelling| object is identified by a |hash| and corresponds to a document
+// marker with the same |hash| identifier in the renderer.
+class Feedback {
+ public:
+ Feedback();
+ ~Feedback();
+
+ // Returns the misspelling identified by |hash|. Returns NULL if there's no
+ // misspelling identified by |hash|. Retains the ownership of the result.
+ Misspelling* GetMisspelling(uint32 hash);
+
+ // Finalizes the user actions on misspellings that are removed from the
+ // renderer process with ID |renderer_process_id|.
+ void FinalizeRemovedMisspellings(
+ int renderer_process_id,
+ const std::vector<uint32>& remaining_markers);
+
+ // Returns true if the renderer with process ID |renderer_process_id| has
+ // misspellings. Otherwise returns false.
+ bool RendererHasMisspellings(int renderer_process_id) const;
+
+ // Returns a copy of the misspellings in renderer with process ID
+ // |renderer_process_id|.
+ std::vector<Misspelling> GetMisspellingsInRenderer(
+ int renderer_process_id) const;
+
+ // Erases the misspellings with final user actions in the renderer with
+ // process ID |renderer_process_id|.
+ void EraseFinalizedMisspellings(int renderer_process_id);
+
+ // Returns true if there's a misspelling with |hash| identifier. Otherwise
+ // returns false.
+ bool HasMisspelling(uint32 hash) const;
+
+ // Adds the |misspelling| to feedback data. If the |misspelling| has a
+ // duplicate hash, then replaces the existing misspelling with the same hash.
+ void AddMisspelling(int renderer_process_id, const Misspelling& misspelling);
+
+ // Returns true if there're no misspellings. Otherwise returns false.
+ bool Empty() const;
+
+ // Returns a list of process identifiers for renderers that have misspellings.
+ std::vector<int> GetRendersWithMisspellings() const;
+
+ // Finalizes all misspellings.
+ void FinalizeAllMisspellings();
+
+ // Returns a copy of all misspellings.
+ std::vector<Misspelling> GetAllMisspellings() const;
+
+ // Removes all misspellings.
+ void Clear();
+
+ private:
+ // A map of hashes that identify document markers to feedback data to be sent
+ // to spelling service.
+ std::map<uint32, Misspelling> misspellings_;
+
+ // A map of renderer process ID to hashes that identify misspellings.
+ std::map<int, std::vector<uint32> > hashes_;
+
+ DISALLOW_COPY_AND_ASSIGN(Feedback);
+};
+
+} // namespace spellcheck
+
+#endif // CHROME_BROWSER_SPELLCHECKER_FEEDBACK_H_
diff --git a/chrome/browser/spellchecker/feedback_sender.cc b/chrome/browser/spellchecker/feedback_sender.cc
new file mode 100644
index 0000000..c7cbdb8
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2013 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 "chrome/browser/spellchecker/feedback_sender.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/command_line.h"
+#include "base/hash.h"
+#include "base/json/json_writer.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "chrome/browser/spellchecker/word_trimmer.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/spellcheck_common.h"
+#include "chrome/common/spellcheck_marker.h"
+#include "chrome/common/spellcheck_messages.h"
+#include "content/public/browser/render_process_host.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace spellcheck {
+
+namespace {
+
+// The default URL where feedback data is sent.
+const char kFeedbackServiceURL[] = "https://www.googleapis.com/rpc";
+
+// Returns a hash of |session_start|, the current timestamp, and
+// |suggestion_index|.
+uint32 BuildHash(const base::Time& session_start, size_t suggestion_index) {
+ std::stringstream hash_data;
+ hash_data << session_start.ToTimeT()
+ << base::Time::Now().ToTimeT()
+ << suggestion_index;
+ return base::Hash(hash_data.str());
+}
+
+// Returns a pending feedback data structure for the spellcheck |result| and
+// |text|.
+Misspelling BuildFeedback(const SpellCheckResult& result,
+ const string16& text) {
+ size_t start = result.location;
+ string16 context = TrimWords(&start,
+ result.length,
+ text,
+ chrome::spellcheck_common::kContextWordCount);
+ return Misspelling(context,
+ start,
+ result.length,
+ std::vector<string16>(1, result.replacement),
+ result.hash);
+}
+
+// Builds suggestion info from |suggestions|. The caller owns the result.
+base::ListValue* BuildSuggestionInfo(
+ const std::vector<Misspelling>& suggestions,
+ bool is_first_feedback_batch) {
+ base::ListValue* list = new base::ListValue;
+ for (std::vector<Misspelling>::const_iterator it = suggestions.begin();
+ it != suggestions.end();
+ ++it) {
+ base::DictionaryValue* suggestion = it->Serialize();
+ suggestion->SetBoolean("isFirstInSession", is_first_feedback_batch);
+ suggestion->SetBoolean("isAutoCorrection", false);
+ list->Append(suggestion);
+ }
+ return list;
+}
+
+// Builds feedback parameters from |suggestion_info|, |language|, and |country|.
+// Takes ownership of |suggestion_list|. The caller owns the result.
+base::DictionaryValue* BuildParams(base::ListValue* suggestion_info,
+ const std::string& language,
+ const std::string& country) {
+ base::DictionaryValue* params = new base::DictionaryValue;
+ params->Set("suggestionInfo", suggestion_info);
+ params->SetString("key", google_apis::GetAPIKey());
+ params->SetString("language", language);
+ params->SetString("originCountry", country);
+ params->SetString("clientName", "Chrome");
+ return params;
+}
+
+// Builds feedback data from |params|. Takes ownership of |params|. The caller
+// owns the result.
+base::Value* BuildFeedbackValue(base::DictionaryValue* params) {
+ base::DictionaryValue* result = new base::DictionaryValue;
+ result->Set("params", params);
+ result->SetString("method", "spelling.feedback");
+ result->SetString("apiVersion", "v2");
+ return result;
+}
+
+} // namespace
+
+FeedbackSender::FeedbackSender(net::URLRequestContextGetter* request_context,
+ const std::string& language,
+ const std::string& country)
+ : request_context_(request_context),
+ language_(language),
+ country_(country),
+ misspelling_counter_(0),
+ session_start_(base::Time::Now()),
+ feedback_service_url_(kFeedbackServiceURL) {
+ // The command-line switch is for testing and temporary.
+ // TODO(rouslan): Remove the command-line switch when testing is complete by
+ // August 2013.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSpellingServiceFeedbackUrl)) {
+ feedback_service_url_ =
+ GURL(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kSpellingServiceFeedbackUrl));
+ }
+
+ timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(
+ chrome::spellcheck_common::kFeedbackIntervalSeconds),
+ this,
+ &FeedbackSender::RequestDocumentMarkers);
+}
+
+FeedbackSender::~FeedbackSender() {
+}
+
+void FeedbackSender::SelectedSuggestion(uint32 hash, int suggestion_index) {
+ Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ if (!misspelling)
+ return;
+ misspelling->action.type = SpellcheckAction::TYPE_SELECT;
+ misspelling->action.index = suggestion_index;
+ misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::AddedToDictionary(uint32 hash) {
+ Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ if (!misspelling)
+ return;
+ misspelling->action.type = SpellcheckAction::TYPE_ADD_TO_DICT;
+ misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::IgnoredSuggestions(uint32 hash) {
+ Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ if (!misspelling)
+ return;
+ misspelling->action.type = SpellcheckAction::TYPE_PENDING_IGNORE;
+ misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::ManuallyCorrected(uint32 hash,
+ const string16& correction) {
+ Misspelling* misspelling = feedback_.GetMisspelling(hash);
+ if (!misspelling)
+ return;
+ misspelling->action.type = SpellcheckAction::TYPE_MANUALLY_CORRECTED;
+ misspelling->action.value = correction;
+ misspelling->timestamp = base::Time::Now();
+}
+
+void FeedbackSender::OnReceiveDocumentMarkers(
+ int renderer_process_id,
+ const std::vector<uint32>& markers) {
+ if ((base::Time::Now() - session_start_).InHours() >=
+ chrome::spellcheck_common::kSessionHours) {
+ FlushFeedback();
+ return;
+ }
+
+ if (!feedback_.RendererHasMisspellings(renderer_process_id))
+ return;
+
+ feedback_.FinalizeRemovedMisspellings(renderer_process_id, markers);
+ SendFeedback(feedback_.GetMisspellingsInRenderer(renderer_process_id),
+ !renderers_sent_feedback_.count(renderer_process_id));
+ renderers_sent_feedback_.insert(renderer_process_id);
+ feedback_.EraseFinalizedMisspellings(renderer_process_id);
+}
+
+void FeedbackSender::OnSpellcheckResults(
+ std::vector<SpellCheckResult>* results,
+ int renderer_process_id,
+ const string16& text,
+ const std::vector<SpellCheckMarker>& markers) {
+ // Generate a map of marker offsets to marker hashes. This map helps to
+ // efficiently lookup feedback data based on the position of the misspelling
+ // in text
+ typedef std::map<size_t, uint32> MarkerMap;
+ MarkerMap marker_map;
+ for (size_t i = 0; i < markers.size(); ++i)
+ marker_map[markers[i].offset] = markers[i].hash;
+
+ for (std::vector<SpellCheckResult>::iterator result_iter = results->begin();
+ result_iter != results->end();
+ ++result_iter) {
+ MarkerMap::iterator marker_iter = marker_map.find(result_iter->location);
+ if (marker_iter != marker_map.end() &&
+ feedback_.HasMisspelling(marker_iter->second)) {
+ // If the renderer already has a marker for this spellcheck result, then
+ // set the hash of the spellcheck result to be the same as the marker.
+ result_iter->hash = marker_iter->second;
+ } else {
+ // If the renderer does not yet have a marker for this spellcheck result,
+ // then generate a new hash for the spellcheck result.
+ result_iter->hash = BuildHash(session_start_, ++misspelling_counter_);
+ }
+ // Save the feedback data for the spellcheck result.
+ feedback_.AddMisspelling(renderer_process_id,
+ BuildFeedback(*result_iter, text));
+ }
+}
+
+void FeedbackSender::OnLanguageCountryChange(const std::string& language,
+ const std::string& country) {
+ FlushFeedback();
+ language_ = language;
+ country_ = country;
+}
+
+void FeedbackSender::OnURLFetchComplete(const net::URLFetcher* source) {
+ for (ScopedVector<net::URLFetcher>::iterator it = senders_.begin();
+ it != senders_.end();
+ ++it) {
+ if (*it == source) {
+ senders_.erase(it);
+ break;
+ }
+ }
+}
+
+void FeedbackSender::RequestDocumentMarkers() {
+ // Request document markers from all the renderers that are still alive.
+ std::vector<int> alive_renderers;
+ for (content::RenderProcessHost::iterator it(
+ content::RenderProcessHost::AllHostsIterator());
+ !it.IsAtEnd();
+ it.Advance()) {
+ alive_renderers.push_back(it.GetCurrentValue()->GetID());
+ it.GetCurrentValue()->Send(new SpellCheckMsg_RequestDocumentMarkers());
+ }
+
+ // Asynchronously send out the feedback for all the renderers that are no
+ // longer alive.
+ std::vector<int> known_renderers = feedback_.GetRendersWithMisspellings();
+ std::sort(alive_renderers.begin(), alive_renderers.end());
+ std::sort(known_renderers.begin(), known_renderers.end());
+ std::vector<int> dead_renderers;
+ std::set_difference(known_renderers.begin(),
+ known_renderers.end(),
+ alive_renderers.begin(),
+ alive_renderers.end(),
+ std::back_inserter(dead_renderers));
+ for (std::vector<int>::const_iterator it = dead_renderers.begin();
+ it != dead_renderers.end();
+ ++it) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&FeedbackSender::OnReceiveDocumentMarkers,
+ AsWeakPtr(),
+ *it,
+ std::vector<uint32>()));
+ }
+}
+
+void FeedbackSender::FlushFeedback() {
+ if (feedback_.Empty())
+ return;
+ feedback_.FinalizeAllMisspellings();
+ SendFeedback(feedback_.GetAllMisspellings(),
+ renderers_sent_feedback_.empty());
+ feedback_.Clear();
+ renderers_sent_feedback_.clear();
+ session_start_ = base::Time::Now();
+ timer_.Reset();
+}
+
+void FeedbackSender::SendFeedback(const std::vector<Misspelling>& feedback_data,
+ bool is_first_feedback_batch) {
+ scoped_ptr<base::Value> feedback_value(BuildFeedbackValue(
+ BuildParams(BuildSuggestionInfo(feedback_data, is_first_feedback_batch),
+ language_,
+ country_)));
+ std::string feedback;
+ base::JSONWriter::Write(feedback_value.get(), &feedback);
+
+ // The tests use this identifier to mock the URL fetcher.
+ static const int kUrlFetcherId = 0;
+ net::URLFetcher* sender = net::URLFetcher::Create(
+ kUrlFetcherId, feedback_service_url_, net::URLFetcher::POST, this);
+ sender->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES);
+ sender->SetUploadData("application/json", feedback);
+ senders_.push_back(sender);
+
+ // Request context is NULL in testing.
+ if (request_context_.get()) {
+ sender->SetRequestContext(request_context_.get());
+ sender->Start();
+ }
+}
+
+} // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback_sender.h b/chrome/browser/spellchecker/feedback_sender.h
new file mode 100644
index 0000000..082ff20
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2013 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.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
+#define CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer.h"
+#include "chrome/browser/spellchecker/feedback.h"
+#include "chrome/browser/spellchecker/misspelling.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class SpellingServiceFeedbackTest;
+struct SpellCheckMarker;
+struct SpellCheckResult;
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace spellcheck {
+
+// Manages sending feedback to the spelling service.
+class FeedbackSender : public base::SupportsWeakPtr<FeedbackSender>,
+ public net::URLFetcherDelegate {
+ public:
+ FeedbackSender(net::URLRequestContextGetter* request_context,
+ const std::string& language,
+ const std::string& country);
+ virtual ~FeedbackSender();
+
+ // Records that user selected suggestion |suggestion_index| for the
+ // misspelling identified by |hash|.
+ void SelectedSuggestion(uint32 hash, int suggestion_index);
+
+ // Records that user added the misspelling identified by |hash| to the
+ // dictionary.
+ void AddedToDictionary(uint32 hash);
+
+ // Records that user right-clicked on the misspelling identified by |hash|,
+ // but did not select any suggestion.
+ void IgnoredSuggestions(uint32 hash);
+
+ // Records that user did not choose any suggestion but manually corrected the
+ // misspelling identified by |hash| to string |correction|, which is not in
+ // the list of suggestions.
+ void ManuallyCorrected(uint32 hash, const string16& correction);
+
+ // 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
+ // data for the renderer with process ID |renderer_process_id| to the spelling
+ // service. If the current session has expired, then refreshes the session
+ // start timestamp and sends out all of the feedback data.
+ void OnReceiveDocumentMarkers(int renderer_process_id,
+ const std::vector<uint32>& markers);
+
+ // Generates feedback data based on spellcheck results. The new feedback data
+ // is pending. Sets hash identifiers for |results|. Called when spelling
+ // service client receives results from the spelling service.
+ void OnSpellcheckResults(std::vector<SpellCheckResult>* results,
+ int renderer_process_id,
+ const string16& text,
+ const std::vector<SpellCheckMarker>& markers);
+
+ // Receives updated language and country code for feedback. Finalizes and
+ // sends out all of the feedback data.
+ void OnLanguageCountryChange(const std::string& language,
+ const std::string& country);
+
+ private:
+ friend class FeedbackSenderTest;
+
+ // net::URLFetcherDelegate implementation.
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // Requests the document markers from all of the renderers to determine which
+ // feedback can be finalized. Finalizes feedback for renderers that are gone.
+ // Called periodically when |timer_| fires.
+ void RequestDocumentMarkers();
+
+ // Sends out all feedback data. Resets the session-start timestamp to now.
+ // Restarts the timer that requests markers from the renderers.
+ void FlushFeedback();
+
+ // Sends out the |feedback_data|.
+ void SendFeedback(const std::vector<Misspelling>& feedback_data,
+ bool is_first_feedback_batch);
+
+ // Request context for the feedback senders.
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+
+ // The language of text. The string is a BCP 47 language tag.
+ std::string language_;
+
+ // The country of origin. The string is the ISO 3166-1 alpha-3 code.
+ std::string country_;
+
+ // Misspelling counter used to generate unique hash identifier for each
+ // misspelling.
+ size_t misspelling_counter_;
+
+ // Feedback data.
+ Feedback feedback_;
+
+ // A set of renderer process IDs for renderers that have sent out feedback in
+ // this session.
+ std::set<int> renderers_sent_feedback_;
+
+ // When the session started.
+ base::Time session_start_;
+
+ // The URL where the feedback data should be sent.
+ GURL feedback_service_url_;
+
+ // A timer to periodically request a list of document spelling markers from
+ // all of the renderers. The timer runs while an instance of this class is
+ // alive.
+ base::RepeatingTimer<FeedbackSender> timer_;
+
+ // Feedback senders that need to stay alive for the duration of sending data.
+ // If a sender is destroyed before it finishes, then sending feedback will be
+ // canceled.
+ ScopedVector<net::URLFetcher> senders_;
+
+ DISALLOW_COPY_AND_ASSIGN(FeedbackSender);
+};
+
+} // namespace spellcheck
+
+#endif // CHROME_BROWSER_SPELLCHECKER_FEEDBACK_SENDER_H_
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc
new file mode 100644
index 0000000..23eaf5a
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -0,0 +1,436 @@
+// Copyright (c) 2013 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/bind.h"
+#include "base/json/json_reader.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/spellchecker/feedback_sender.h"
+#include "chrome/common/spellcheck_common.h"
+#include "chrome/common/spellcheck_marker.h"
+#include "chrome/common/spellcheck_result.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace spellcheck {
+
+namespace {
+
+static const int kMisspellingLength = 6;
+static const int kMisspellingStart = 0;
+static const int kRendererProcessId = 0;
+static const int kUrlFetcherId = 0;
+static const std::string kCountry = "USA";
+static const std::string kLanguage = "en";
+static const string16 kText = ASCIIToUTF16("Helllo world.");
+
+SpellCheckResult BuildSpellCheckResult() {
+ return SpellCheckResult(SpellCheckResult::SPELLING,
+ kMisspellingStart,
+ kMisspellingLength,
+ ASCIIToUTF16("Hello"));
+}
+
+} // namespace
+
+class FeedbackSenderTest : public testing::Test {
+ public:
+ FeedbackSenderTest()
+ : ui_thread_(content::BrowserThread::UI, &loop_),
+ feedback_(NULL, kLanguage, kCountry) {}
+ virtual ~FeedbackSenderTest() {}
+
+ private:
+ TestingProfile profile_;
+ base::MessageLoop loop_;
+ content::TestBrowserThread ui_thread_;
+
+ protected:
+ uint32 AddPendingFeedback() {
+ std::vector<SpellCheckResult> results(1, BuildSpellCheckResult());
+ feedback_.OnSpellcheckResults(
+ &results, kRendererProcessId, kText, std::vector<SpellCheckMarker>());
+ return results[0].hash;
+ }
+
+ void ExpireSession() {
+ feedback_.session_start_ =
+ base::Time::Now() -
+ base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours);
+ }
+
+ net::TestURLFetcher* GetFetcher() {
+ return fetchers_.GetFetcherByID(kUrlFetcherId);
+ }
+
+ net::TestURLFetcherFactory fetchers_;
+ spellcheck::FeedbackSender feedback_;
+};
+
+// Do not send data if there's no feedback.
+TEST_F(FeedbackSenderTest, NoFeedback) {
+ EXPECT_EQ(NULL, GetFetcher());
+ feedback_.OnReceiveDocumentMarkers(kRendererProcessId,
+ std::vector<uint32>());
+ EXPECT_EQ(NULL, GetFetcher());
+}
+
+// Do not send data if not aware of which markers are still in the document.
+TEST_F(FeedbackSenderTest, NoDocumentMarkersReceived) {
+ EXPECT_EQ(NULL, GetFetcher());
+ uint32 hash = AddPendingFeedback();
+ EXPECT_EQ(NULL, GetFetcher());
+ static const int kSuggestionIndex = 1;
+ feedback_.SelectedSuggestion(hash, kSuggestionIndex);
+ EXPECT_EQ(NULL, GetFetcher());
+}
+
+// Send PENDING feedback message if the marker is still in the document, and the
+// user has not performed any action on it.
+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();
+}
+
+// Send NO_ACTION feedback message if the marker has been removed from the
+// document.
+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();
+}
+
+// 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);
+ 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();
+}
+
+// Send ADD_TO_DICT feedback message if the user has added the misspelled word
+// to the custom dictionary.
+TEST_F(FeedbackSenderTest, AddToDictFeedback) {
+ uint32 hash = AddPendingFeedback();
+ 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();
+}
+
+// Send PENDING feedback message if the user saw the spelling suggestion, but
+// decided to not select it, and the marker is still in the document.
+TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerInDocument) {
+ uint32 hash = AddPendingFeedback();
+ 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();
+}
+
+// Send IGNORE feedback message if the user saw the spelling suggestion, but
+// decided to not select it, and the marker is no longer in the document.
+TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerNotInDocument) {
+ uint32 hash = AddPendingFeedback();
+ 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();
+}
+
+// Send MANUALLY_CORRECTED feedback message if the user manually corrected the
+// misspelled word.
+TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) {
+ uint32 hash = AddPendingFeedback();
+ static const std::string kManualCorrection = "Howdy";
+ 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();
+}
+
+// Send feedback messages in batch.
+TEST_F(FeedbackSenderTest, BatchFeedback) {
+ std::vector<SpellCheckResult> results;
+ results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
+ kMisspellingStart,
+ kMisspellingLength,
+ ASCIIToUTF16("Hello")));
+ static const int kSecondMisspellingStart = 7;
+ static const int kSecondMisspellingLength = 5;
+ results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
+ kSecondMisspellingStart,
+ kSecondMisspellingLength,
+ ASCIIToUTF16("world")));
+ feedback_.OnSpellcheckResults(
+ &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();
+}
+
+// Send a series of PENDING feedback messages and one final NO_ACTION feedback
+// message with the same hash identifier for a single misspelling.
+TEST_F(FeedbackSenderTest, SameHashFeedback) {
+ uint32 hash = AddPendingFeedback();
+ 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();
+ 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);
+
+ 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);
+
+ 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);
+
+ feedback_.OnReceiveDocumentMarkers(kRendererProcessId, std::vector<uint32>());
+ EXPECT_EQ(NULL, GetFetcher());
+}
+
+// When a session expires:
+// 1) Pending feedback is finalized and sent to the server in the last message
+// batch in the session.
+// 2) No feedback is sent until a spellcheck request happens.
+// 3) Existing markers get new hash identifiers.
+TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
+ std::vector<SpellCheckResult> results(1, SpellCheckResult(
+ SpellCheckResult::SPELLING,
+ kMisspellingStart,
+ kMisspellingLength,
+ ASCIIToUTF16("Hello")));
+ feedback_.OnSpellcheckResults(
+ &results, kRendererProcessId, kText, std::vector<SpellCheckMarker>());
+ uint32 original_hash = results[0].hash;
+ 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();
+ 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);
+
+ 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);
+
+ // 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());
+
+ // The first spellcheck request after session expiration creates different
+ // document marker hash identifiers.
+ std::vector<SpellCheckMarker> original_markers(
+ 1, SpellCheckMarker(results[0].hash, results[0].location));
+ results[0] = SpellCheckResult(SpellCheckResult::SPELLING,
+ kMisspellingStart,
+ kMisspellingLength,
+ ASCIIToUTF16("Hello"));
+ feedback_.OnSpellcheckResults(
+ &results, kRendererProcessId, kText, original_markers);
+ uint32 updated_hash = results[0].hash;
+ EXPECT_NE(updated_hash, original_hash);
+ remaining_markers[0] = updated_hash;
+
+ // 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();
+ 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();
+}
+
+// First message in session has an indicator.
+TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
+ // Session 1, message 1
+ 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();
+
+ // 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();
+
+ ExpireSession();
+
+ // Session 1, message 3 (last)
+ AddPendingFeedback();
+ feedback_.OnReceiveDocumentMarkers(kRendererProcessId, std::vector<uint32>());
+ fetcher = GetFetcher();
+ EXPECT_NE(std::string::npos,
+ fetcher->upload_data().find("\"isFirstInSession\":false"))
+ << fetcher->upload_data();
+
+ // 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();
+
+ // 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();
+}
+
+// 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();
+
+ AddPendingFeedback();
+ feedback_.OnLanguageCountryChange("en", "US");
+ fetcher = GetFetcher();
+ EXPECT_NE(std::string::npos,
+ fetcher->upload_data().find("\"language\":\"pt\""))
+ << fetcher->upload_data();
+}
+
+// The field names and types should correspond to the API.
+TEST_F(FeedbackSenderTest, FeedbackAPI) {
+ AddPendingFeedback();
+ feedback_.OnReceiveDocumentMarkers(kRendererProcessId,
+ std::vector<uint32>());
+ std::string actual_data = GetFetcher()->upload_data();
+ scoped_ptr<base::DictionaryValue> actual(static_cast<base::DictionaryValue*>(
+ base::JSONReader::Read(actual_data)));
+ actual->SetString("params.key", "TestDummyKey");
+ base::ListValue* suggestions = NULL;
+ actual->GetList("params.suggestionInfo", &suggestions);
+ base::DictionaryValue* suggestion = NULL;
+ suggestions->GetDictionary(0, &suggestion);
+ suggestion->SetString("suggestionId", "42");
+ suggestion->SetString("timestamp", "9001");
+ static const std::string expected_data =
+ "{\"apiVersion\":\"v2\","
+ "\"method\":\"spelling.feedback\","
+ "\"params\":"
+ "{\"clientName\":\"Chrome\","
+ "\"originCountry\":\"USA\","
+ "\"key\":\"TestDummyKey\","
+ "\"language\":\"en\","
+ "\"suggestionInfo\":[{"
+ "\"isAutoCorrection\":false,"
+ "\"isFirstInSession\":true,"
+ "\"misspelledLength\":6,"
+ "\"misspelledStart\":0,"
+ "\"originalText\":\"Helllo world\","
+ "\"suggestionId\":\"42\","
+ "\"suggestions\":[\"Hello\"],"
+ "\"timestamp\":\"9001\","
+ "\"userActions\":[{\"actionType\":\"NO_ACTION\"}]}]}}";
+ scoped_ptr<base::Value> expected(base::JSONReader::Read(expected_data));
+ EXPECT_TRUE(expected->Equals(actual.get()))
+ << "Expected data: " << expected_data
+ << "\nActual data: " << actual_data;
+}
+
+} // namespace spellcheck
diff --git a/chrome/browser/spellchecker/feedback_unittest.cc b/chrome/browser/spellchecker/feedback_unittest.cc
new file mode 100644
index 0000000..2a8cfa2
--- /dev/null
+++ b/chrome/browser/spellchecker/feedback_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2013 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 "chrome/browser/spellchecker/feedback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace spellcheck {
+
+namespace {
+
+// Identifier for a renderer process.
+static const int kRendererProcessId = 7;
+
+// Hash identifier for a misspelling.
+static const uint32 kMisspellingHash = 42;
+
+} // namespace
+
+class FeedbackTest : public testing::Test {
+ public:
+ FeedbackTest() {}
+ virtual ~FeedbackTest() {}
+
+ protected:
+ void AddMisspelling(int renderer_process_id, uint32 hash) {
+ Misspelling misspelling;
+ misspelling.hash = hash;
+ feedback_.AddMisspelling(renderer_process_id, misspelling);
+ }
+
+ spellcheck::Feedback feedback_;
+};
+
+// Should be able to retrieve misspelling after it's added.
+TEST_F(FeedbackTest, RetreiveMisspelling) {
+ EXPECT_EQ(NULL, feedback_.GetMisspelling(kMisspellingHash));
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ Misspelling* result = feedback_.GetMisspelling(kMisspellingHash);
+ EXPECT_NE(static_cast<Misspelling*>(NULL), result);
+ EXPECT_EQ(kMisspellingHash, result->hash);
+}
+
+// Removed misspellings should be finalized.
+TEST_F(FeedbackTest, FinalizeRemovedMisspellings) {
+ static const int kRemovedMisspellingHash = 1;
+ static const int kRemainingMisspellingHash = 2;
+ AddMisspelling(kRendererProcessId, kRemovedMisspellingHash);
+ AddMisspelling(kRendererProcessId, kRemainingMisspellingHash);
+ std::vector<uint32> remaining_markers(1, kRemainingMisspellingHash);
+ feedback_.FinalizeRemovedMisspellings(kRendererProcessId, remaining_markers);
+ Misspelling* removed_misspelling =
+ feedback_.GetMisspelling(kRemovedMisspellingHash);
+ EXPECT_NE(static_cast<Misspelling*>(NULL), removed_misspelling);
+ EXPECT_TRUE(removed_misspelling->action.IsFinal());
+ Misspelling* remaining_misspelling =
+ feedback_.GetMisspelling(kRemainingMisspellingHash);
+ EXPECT_NE(static_cast<Misspelling*>(NULL), remaining_misspelling);
+ EXPECT_FALSE(remaining_misspelling->action.IsFinal());
+}
+
+// Misspellings should be associated with a renderer.
+TEST_F(FeedbackTest, RendererHasMisspellings) {
+ EXPECT_FALSE(feedback_.RendererHasMisspellings(kRendererProcessId));
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ EXPECT_TRUE(feedback_.RendererHasMisspellings(kRendererProcessId));
+}
+
+// Should be able to retrieve misspellings in renderer.
+TEST_F(FeedbackTest, GetMisspellingsInRenderer) {
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ const std::vector<Misspelling>& renderer_with_misspellings =
+ feedback_.GetMisspellingsInRenderer(kRendererProcessId);
+ EXPECT_EQ(static_cast<size_t>(1), renderer_with_misspellings.size());
+ EXPECT_EQ(kMisspellingHash, renderer_with_misspellings[0].hash);
+ const std::vector<Misspelling>& renderer_without_misspellings =
+ feedback_.GetMisspellingsInRenderer(kRendererProcessId + 1);
+ EXPECT_EQ(static_cast<size_t>(0), renderer_without_misspellings.size());
+}
+
+// Finalized misspellings should be erased.
+TEST_F(FeedbackTest, EraseFinalizedMisspellings) {
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ feedback_.FinalizeRemovedMisspellings(kRendererProcessId,
+ std::vector<uint32>());
+ EXPECT_TRUE(feedback_.RendererHasMisspellings(kRendererProcessId));
+ feedback_.EraseFinalizedMisspellings(kRendererProcessId);
+ EXPECT_FALSE(feedback_.RendererHasMisspellings(kRendererProcessId));
+}
+
+// Should be able to check for misspelling existence.
+TEST_F(FeedbackTest, HasMisspelling) {
+ EXPECT_FALSE(feedback_.HasMisspelling(kMisspellingHash));
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ EXPECT_TRUE(feedback_.HasMisspelling(kMisspellingHash));
+}
+
+// Should be able to check for feedback data presence.
+TEST_F(FeedbackTest, EmptyFeedback) {
+ EXPECT_TRUE(feedback_.Empty());
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ EXPECT_FALSE(feedback_.Empty());
+}
+
+// Should be able to retrieve a list of all renderers with misspellings.
+TEST_F(FeedbackTest, GetRendersWithMisspellings) {
+ EXPECT_TRUE(feedback_.GetRendersWithMisspellings().empty());
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+ std::vector<int> result = feedback_.GetRendersWithMisspellings();
+ EXPECT_EQ(static_cast<size_t>(2), result.size());
+ EXPECT_NE(result[0], result[1]);
+ EXPECT_TRUE(result[0] == kRendererProcessId ||
+ result[0] == kRendererProcessId + 1);
+ EXPECT_TRUE(result[1] == kRendererProcessId ||
+ result[1] == kRendererProcessId + 1);
+}
+
+// Should be able to finalize all misspellings.
+TEST_F(FeedbackTest, FinalizeAllMisspellings) {
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+ {
+ std::vector<Misspelling> pending = feedback_.GetAllMisspellings();
+ for (std::vector<Misspelling>::const_iterator it = pending.begin();
+ it != pending.end();
+ ++it) {
+ EXPECT_FALSE(it->action.IsFinal());
+ }
+ }
+ feedback_.FinalizeAllMisspellings();
+ {
+ std::vector<Misspelling> final = feedback_.GetAllMisspellings();
+ for (std::vector<Misspelling>::const_iterator it = final.begin();
+ it != final.end();
+ ++it) {
+ EXPECT_TRUE(it->action.IsFinal());
+ }
+ }
+}
+
+// Should be able to retrieve a copy of all misspellings.
+TEST_F(FeedbackTest, GetAllMisspellings) {
+ EXPECT_TRUE(feedback_.GetAllMisspellings().empty());
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+ const std::vector<Misspelling>& result = feedback_.GetAllMisspellings();
+ EXPECT_EQ(static_cast<size_t>(2), result.size());
+ EXPECT_NE(result[0].hash, result[1].hash);
+ EXPECT_TRUE(result[0].hash == kMisspellingHash ||
+ result[0].hash == kMisspellingHash + 1);
+ EXPECT_TRUE(result[1].hash == kMisspellingHash ||
+ result[1].hash == kMisspellingHash + 1);
+}
+
+// Should be able to clear all misspellings.
+TEST_F(FeedbackTest, ClearFeedback) {
+ AddMisspelling(kRendererProcessId, kMisspellingHash);
+ AddMisspelling(kRendererProcessId + 1, kMisspellingHash + 1);
+ EXPECT_FALSE(feedback_.Empty());
+ feedback_.Clear();
+ EXPECT_TRUE(feedback_.Empty());
+}
+
+} // namespace spellcheck
diff --git a/chrome/browser/spellchecker/spellcheck_misspelling.cc b/chrome/browser/spellchecker/misspelling.cc
index 1ca006a..4afbaa7 100644
--- a/chrome/browser/spellchecker/spellcheck_misspelling.cc
+++ b/chrome/browser/spellchecker/misspelling.cc
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/spellchecker/spellcheck_misspelling.h"
+#include "chrome/browser/spellchecker/misspelling.h"
+#include "base/strings/string_number_conversions.h"
#include "base/values.h"
namespace {
@@ -25,31 +26,35 @@ base::Value* BuildUserActionValue(const SpellcheckAction& action) {
} // namespace
-SpellcheckMisspelling::SpellcheckMisspelling()
- : location(0), length(0), timestamp(base::Time::Now()) {
+Misspelling::Misspelling()
+ : location(0), length(0), hash(0), timestamp(base::Time::Now()) {
}
-SpellcheckMisspelling::SpellcheckMisspelling(
- const string16& context,
- size_t location,
- size_t length,
- const std::vector<string16>& suggestions)
+Misspelling::Misspelling(const string16& context,
+ size_t location,
+ size_t length,
+ const std::vector<string16>& suggestions,
+ uint32 hash)
: context(context),
location(location),
length(length),
suggestions(suggestions),
+ hash(hash),
timestamp(base::Time::Now()) {
}
-SpellcheckMisspelling::~SpellcheckMisspelling() {
+Misspelling::~Misspelling() {
}
-base::DictionaryValue* SpellcheckMisspelling::Serialize() const {
+base::DictionaryValue* Misspelling::Serialize() const {
base::DictionaryValue* result = new base::DictionaryValue;
- result->SetString("originalText", context);
- result->SetInteger("misspelledStart", location);
+ result->SetString(
+ "timestamp",
+ base::Int64ToString(static_cast<long>(timestamp.ToJsTime())));
result->SetInteger("misspelledLength", length);
- result->SetDouble("timestamp", timestamp.ToJsTime());
+ result->SetInteger("misspelledStart", location);
+ result->SetString("originalText", context);
+ result->SetString("suggestionId", base::UintToString(hash));
result->Set("suggestions", BuildSuggestionsValue(suggestions));
result->Set("userActions", BuildUserActionValue(action));
return result;
diff --git a/chrome/browser/spellchecker/spellcheck_misspelling.h b/chrome/browser/spellchecker/misspelling.h
index 8497252..9a9d0fe 100644
--- a/chrome/browser/spellchecker/spellcheck_misspelling.h
+++ b/chrome/browser/spellchecker/misspelling.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MISSPELLING_H_
-#define CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MISSPELLING_H_
+#ifndef CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
+#define CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
#include <vector>
@@ -11,14 +11,15 @@
#include "chrome/browser/spellchecker/spellcheck_action.h"
// Spellcheck misspelling.
-class SpellcheckMisspelling {
+class Misspelling {
public:
- SpellcheckMisspelling();
- SpellcheckMisspelling(const string16& context,
- size_t location,
- size_t length,
- const std::vector<string16>& suggestions);
- ~SpellcheckMisspelling();
+ Misspelling();
+ Misspelling(const string16& context,
+ size_t location,
+ size_t length,
+ const std::vector<string16>& suggestions,
+ uint32 hash);
+ ~Misspelling();
// Serializes the data in this object into a dictionary value. The caller owns
// the result.
@@ -37,6 +38,9 @@ class SpellcheckMisspelling {
// Spelling suggestions.
std::vector<string16> suggestions;
+ // The hash that identifies the misspelling.
+ uint32 hash;
+
// User action.
SpellcheckAction action;
@@ -44,4 +48,4 @@ class SpellcheckMisspelling {
base::Time timestamp;
};
-#endif // CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MISSPELLING_H_
+#endif // CHROME_BROWSER_SPELLCHECKER_MISSPELLING_H_
diff --git a/chrome/browser/spellchecker/spellcheck_misspelling_unittest.cc b/chrome/browser/spellchecker/misspelling_unittest.cc
index 91bad77..175f7ad 100644
--- a/chrome/browser/spellchecker/spellcheck_misspelling_unittest.cc
+++ b/chrome/browser/spellchecker/misspelling_unittest.cc
@@ -5,22 +5,24 @@
#include "base/json/json_reader.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
-#include "chrome/browser/spellchecker/spellcheck_misspelling.h"
+#include "chrome/browser/spellchecker/misspelling.h"
#include "testing/gtest/include/gtest/gtest.h"
-TEST(SpellcheckMisspellingTest, SerializeTest) {
- SpellcheckMisspelling misspelling;
+TEST(MisspellingTest, SerializeTest) {
+ Misspelling misspelling;
misspelling.context = ASCIIToUTF16("How doe sit know");
misspelling.location = 4;
misspelling.length = 7;
misspelling.timestamp = base::Time::FromJsTime(42);
+ misspelling.hash = 9001;
misspelling.suggestions.push_back(ASCIIToUTF16("does it"));
scoped_ptr<base::Value> expected(base::JSONReader::Read(
"{\"originalText\": \"How doe sit know\","
"\"misspelledStart\": 4,"
"\"misspelledLength\": 7,"
- "\"timestamp\": 42.0,"
+ "\"timestamp\": \"42\","
+ "\"suggestionId\":\"9001\","
"\"suggestions\": [\"does it\"],"
"\"userActions\": [{\"actionType\": \"PENDING\"}]}"));
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index e847224..987f2ce 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -40,6 +40,15 @@ SpellcheckService::SpellcheckService(Profile* profile)
PrefService* prefs = profile_->GetPrefs();
pref_change_registrar_.Init(prefs);
+ std::string language_code;
+ std::string country_code;
+ chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
+ prefs->GetString(prefs::kSpellCheckDictionary),
+ &language_code,
+ &country_code);
+ feedback_sender_.reset(new spellcheck::FeedbackSender(
+ profile->GetRequestContext(), language_code, country_code));
+
pref_change_registrar_.Add(
prefs::kEnableAutoSpellCorrect,
base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
@@ -193,8 +202,8 @@ SpellcheckHunspellDictionary* SpellcheckService::GetHunspellDictionary() {
return hunspell_dictionary_.get();
}
-SpellingServiceFeedback* SpellcheckService::GetFeedbackSender() {
- return &feedback_sender_;
+spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
+ return feedback_sender_.get();
}
bool SpellcheckService::LoadExternalDictionary(std::string language,
@@ -283,12 +292,17 @@ void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
void SpellcheckService::OnSpellCheckDictionaryChanged() {
if (hunspell_dictionary_.get())
hunspell_dictionary_->RemoveObserver(this);
+ std::string dictionary =
+ profile_->GetPrefs()->GetString(prefs::kSpellCheckDictionary);
hunspell_dictionary_.reset(new SpellcheckHunspellDictionary(
- profile_->GetPrefs()->GetString(prefs::kSpellCheckDictionary),
- profile_->GetRequestContext(),
- this));
+ dictionary, profile_->GetRequestContext(), this));
hunspell_dictionary_->AddObserver(this);
hunspell_dictionary_->Load();
+ std::string language_code;
+ std::string country_code;
+ chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
+ dictionary, &language_code, &country_code);
+ feedback_sender_->OnLanguageCountryChange(language_code, country_code);
}
void SpellcheckService::OnUseSpellingServiceChanged() {
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index b278961..2689708 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -10,9 +10,9 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_change_registrar.h"
+#include "chrome/browser/spellchecker/feedback_sender.h"
#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
-#include "chrome/browser/spellchecker/spelling_service_feedback.h"
#include "chrome/common/spellcheck_common.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
#include "content/public/browser/notification_observer.h"
@@ -94,7 +94,7 @@ class SpellcheckService : public ProfileKeyedService,
SpellcheckHunspellDictionary* GetHunspellDictionary();
// Returns the instance of the spelling service feedback sender.
- SpellingServiceFeedback* GetFeedbackSender();
+ spellcheck::FeedbackSender* GetFeedbackSender();
// Load a dictionary from a given path. Format specifies how the dictionary
// is stored. Return value is true if successful.
@@ -158,7 +158,7 @@ class SpellcheckService : public ProfileKeyedService,
scoped_ptr<SpellcheckHunspellDictionary> hunspell_dictionary_;
- SpellingServiceFeedback feedback_sender_;
+ scoped_ptr<spellcheck::FeedbackSender> feedback_sender_;
base::WeakPtrFactory<SpellcheckService> weak_ptr_factory_;
diff --git a/chrome/browser/spellchecker/spelling_service_feedback.cc b/chrome/browser/spellchecker/spelling_service_feedback.cc
deleted file mode 100644
index 102d868..0000000
--- a/chrome/browser/spellchecker/spelling_service_feedback.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 "chrome/browser/spellchecker/spelling_service_feedback.h"
-
-#include "chrome/common/spellcheck_common.h"
-#include "chrome/common/spellcheck_messages.h"
-#include "content/public/browser/render_process_host.h"
-
-SpellingServiceFeedback::SpellingServiceFeedback() {
- timer_.Start(
- FROM_HERE,
- base::TimeDelta::FromSeconds(
- chrome::spellcheck_common::kFeedbackIntervalSeconds),
- this,
- &SpellingServiceFeedback::RequestDocumentMarkers);
-}
-
-SpellingServiceFeedback::~SpellingServiceFeedback() {
-}
-
-void SpellingServiceFeedback::OnReceiveDocumentMarkers(
- int render_process_id,
- const std::vector<uint32>& markers) const {
-}
-
-void SpellingServiceFeedback::RequestDocumentMarkers() {
- for (content::RenderProcessHost::iterator i(
- content::RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd();
- i.Advance()) {
- i.GetCurrentValue()->Send(new SpellCheckMsg_RequestDocumentMarkers());
- }
-}
diff --git a/chrome/browser/spellchecker/spelling_service_feedback.h b/chrome/browser/spellchecker/spelling_service_feedback.h
deleted file mode 100644
index 949a9d5..0000000
--- a/chrome/browser/spellchecker/spelling_service_feedback.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-#ifndef CHROME_BROWSER_SPELLCHECKER_SPELLING_SERVICE_FEEDBACK_H_
-#define CHROME_BROWSER_SPELLCHECKER_SPELLING_SERVICE_FEEDBACK_H_
-
-#include <vector>
-
-#include "base/timer.h"
-
-// Manages sending feedback to the spelling service.
-class SpellingServiceFeedback {
- public:
- SpellingServiceFeedback();
- ~SpellingServiceFeedback();
-
- // Receives document markers for renderer with process ID |render_process_id|.
- // Called when the renderer responds to RequestDocumentMarkers() call.
- void OnReceiveDocumentMarkers(
- int render_process_id,
- const std::vector<uint32>& markers) const;
-
- private:
- // Requests the document markers from all of the renderers. Called
- // periodically when |timer_| fires.
- void RequestDocumentMarkers();
-
- // A timer to periodically request a list of document spelling markers from
- // all of the renderers. The timer runs while an instance of this class is
- // alive.
- base::RepeatingTimer<SpellingServiceFeedback> timer_;
-
- DISALLOW_COPY_AND_ASSIGN(SpellingServiceFeedback);
-};
-
-#endif // CHROME_BROWSER_SPELLCHECKER_SPELLING_SERVICE_FEEDBACK_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 1d4bb6f..2b8dfd0 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1860,6 +1860,12 @@
'browser/speech/tts_message_filter.cc',
'browser/speech/tts_message_filter.h',
'browser/speech/tts_win.cc',
+ 'browser/spellchecker/feedback.cc',
+ 'browser/spellchecker/feedback.h',
+ 'browser/spellchecker/feedback_sender.cc',
+ 'browser/spellchecker/feedback_sender.h',
+ 'browser/spellchecker/misspelling.cc',
+ 'browser/spellchecker/misspelling.h',
'browser/spellchecker/spellcheck_action.cc',
'browser/spellchecker/spellcheck_action.h',
'browser/spellchecker/spellcheck_custom_dictionary.cc',
@@ -1875,16 +1881,12 @@
'browser/spellchecker/spellcheck_message_filter.h',
'browser/spellchecker/spellcheck_message_filter_mac.cc',
'browser/spellchecker/spellcheck_message_filter_mac.h',
- 'browser/spellchecker/spellcheck_misspelling.cc',
- 'browser/spellchecker/spellcheck_misspelling.h',
'browser/spellchecker/spellcheck_platform_mac.h',
'browser/spellchecker/spellcheck_platform_mac.mm',
'browser/spellchecker/spellcheck_service.cc',
'browser/spellchecker/spellcheck_service.h',
'browser/spellchecker/spelling_service_client.cc',
'browser/spellchecker/spelling_service_client.h',
- 'browser/spellchecker/spelling_service_feedback.cc',
- 'browser/spellchecker/spelling_service_feedback.h',
'browser/spellchecker/word_trimmer.cc',
'browser/spellchecker/word_trimmer.h',
'browser/ssl/ssl_add_certificate.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 968f384..eb01f92 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1110,12 +1110,14 @@
'browser/speech/extension_api/extension_manifests_tts_unittest.cc',
'browser/speech/speech_recognition_bubble_controller_unittest.cc',
'browser/speech/tts_controller_unittest.cc',
+ 'browser/spellchecker/feedback_sender_unittest.cc',
+ 'browser/spellchecker/feedback_unittest.cc',
+ 'browser/spellchecker/misspelling_unittest.cc',
'browser/spellchecker/spellcheck_action_unittest.cc',
'browser/spellchecker/spellcheck_custom_dictionary_unittest.cc',
'browser/spellchecker/spellcheck_host_metrics_unittest.cc',
'browser/spellchecker/spellcheck_message_filter_mac_unittest.cc',
'browser/spellchecker/spellcheck_message_filter_unittest.cc',
- 'browser/spellchecker/spellcheck_misspelling_unittest.cc',
'browser/spellchecker/spellcheck_platform_mac_unittest.cc',
'browser/spellchecker/spellcheck_service_unittest.cc',
'browser/spellchecker/spelling_service_client_unittest.cc',
diff --git a/chrome/common/spellcheck_common.h b/chrome/common/spellcheck_common.h
index 1bed570..a79aa1c 100644
--- a/chrome/common/spellcheck_common.h
+++ b/chrome/common/spellcheck_common.h
@@ -15,6 +15,14 @@ class FilePath;
namespace chrome {
namespace spellcheck_common {
+// The number of hours that a session of feedback for spelling service lasts.
+// After this number of hours passes, all feedback.
+static const int kSessionHours = 6;
+
+// The number of context words to keep on either side of a misspelling for
+// spelling service feedback.
+static const int kContextWordCount = 2;
+
// The number of seconds between sending feedback to spelling service.
static const int kFeedbackIntervalSeconds = 1800; // 30 minutes
diff --git a/chrome/common/spellcheck_result.h b/chrome/common/spellcheck_result.h
index 29b1118..262b06d 100644
--- a/chrome/common/spellcheck_result.h
+++ b/chrome/common/spellcheck_result.h
@@ -24,14 +24,16 @@ struct SpellCheckResult {
Type t = SPELLING,
int loc = 0,
int len = 0,
- const string16& rep = string16())
- : type(t), location(loc), length(len), replacement(rep) {
+ const string16& rep = string16(),
+ uint32 h = 0)
+ : type(t), location(loc), length(len), replacement(rep), hash(h) {
}
Type type;
int location;
int length;
string16 replacement;
+ uint32 hash;
};
#endif // CHROME_COMMON_SPELLCHECK_RESULT_H_