summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-05 09:26:08 +0000
committerhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-05 09:26:08 +0000
commitc8a7266a5d9b9c9ff798def26d7f3bedd4b46713 (patch)
tree180ed72a3d13604088ce8e8be6b4673d9acd5a39
parent5758a947293b9429378f1782f808567ccde38ea5 (diff)
downloadchromium_src-c8a7266a5d9b9c9ff798def26d7f3bedd4b46713.zip
chromium_src-c8a7266a5d9b9c9ff798def26d7f3bedd4b46713.tar.gz
chromium_src-c8a7266a5d9b9c9ff798def26d7f3bedd4b46713.tar.bz2
Integrate the Spelling service to Chrome.
This change integrates the Spelling service (a.k.a. "Did you mean") to a context-menu of Chrome so we can see the spell-check result retrieved from the Spelling service. This change sends a JSON-RPC request to the service in the background and update a context-menu item while showing to prevent junkiness. BUG=29191,93746 TEST=right-click a misspelled word and see it is updated. Review URL: http://codereview.chromium.org/7713033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99633 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/chrome_command_ids.h1
-rw-r--r--chrome/app/generated_resources.grd16
-rw-r--r--chrome/browser/profiles/profile.cc7
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu.cc63
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu.h87
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_gtk.cc68
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_gtk.h5
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_mac.h5
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_mac.mm43
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_observer.cc19
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu_observer.h102
-rw-r--r--chrome/browser/tab_contents/spelling_menu_observer.cc276
-rw-r--r--chrome/browser/tab_contents/spelling_menu_observer.h106
-rw-r--r--chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc17
-rw-r--r--chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h16
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/common/pref_names.cc3
-rw-r--r--chrome/common/pref_names.h1
18 files changed, 832 insertions, 7 deletions
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index b3cad45..ee27d73 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -265,6 +265,7 @@
#define IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS 50153
#define IDC_CONTENT_CONTEXT_LOOK_UP_IN_DICTIONARY 50154
#define IDC_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS 50155
+#define IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION 50156
// Frame items.
#define IDC_CONTENT_CONTEXT_RELOADFRAME 50160
#define IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE 50161
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3fdbaef..98c3099 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -540,6 +540,12 @@ are declared in build/common.gypi.
<message name="IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS" desc="The name of the No Spelling Suggestions display in the content area context menu">
&amp;No spelling suggestions
</message>
+ <message name="IDS_CONTENT_CONTEXT_SPELLING_CHECKING" desc="The place-holder message shown while the Spelling service is checking text">
+ Loading spelling suggestion
+ </message>
+ <message name="IDS_CONTENT_CONTEXT_SPELLING_CORRECT" desc="The message shown when the selection text is correct.">
+ No suggestions found
+ </message>
<message name="IDS_CONTENT_CONTEXT_SPELLCHECK_MENU" desc="The name of the Spellcheck Options submenu in the content area context menu">
&amp;Spell-checker options
</message>
@@ -734,6 +740,12 @@ are declared in build/common.gypi.
<message name="IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS" desc="In Title Case: The name of the No Spelling Suggestions display in the content area context menu">
&amp;No Spelling Suggestions
</message>
+ <message name="IDS_CONTENT_CONTEXT_SPELLING_CHECKING" desc="The place-holder message shown while the Spelling service is checking text">
+ Loading spelling suggestion
+ </message>
+ <message name="IDS_CONTENT_CONTEXT_SPELLING_CORRECT" desc="The message shown when the selection text is correct.">
+ No suggestions found
+ </message>
<message name="IDS_CONTENT_CONTEXT_SPELLCHECK_MENU" desc="In Title Case: The name of the Spellcheck Options submenu in the content area context menu">
&amp;Spell-checker Options
</message>
@@ -9001,7 +9013,9 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_OPTIONS_SUGGEST_PREF" desc="The documentation string of the 'Use Suggest' preference">
Use a prediction service to help complete searches and URLs typed in the address bar
</message>
-
+ <message name="IDS_OPTIONS_SPELLING_PREF" desc="The documentation string of the 'Use Spelling' preference">
+ Use a web service to help resolve spelling errors
+ </message>
<message name="IDS_OPTIONS_ENABLE_LOGGING" desc="The label of the checkbox to enable/disable crash and user metrics logging">
Automatically send usage statistics and crash reports to Google
</message>
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 997c966..22d1e85 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -129,6 +129,13 @@ void Profile::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterLocalizedStringPref(prefs::kSpellCheckDictionary,
IDS_SPELLCHECK_DICTIONARY,
PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterBooleanPref(prefs::kSpellCheckUseSpellingService,
+#if defined(GOOGLE_CHROME_BUILD)
+ true,
+#else
+ false,
+#endif
+ PrefService::UNSYNCABLE_PREF);
prefs->RegisterBooleanPref(prefs::kEnableSpellCheck,
true,
PrefService::SYNCABLE_PREF);
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index 77ba463..3f24801 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -37,6 +37,7 @@
#include "chrome/browser/spellchecker/spellcheck_host.h"
#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
#include "chrome/browser/spellchecker/spellchecker_platform_engine.h"
+#include "chrome/browser/tab_contents/spelling_menu_observer.h"
#include "chrome/browser/translate/translate_manager.h"
#include "chrome/browser/translate/translate_prefs.h"
#include "chrome/browser/translate/translate_tab_helper.h"
@@ -552,6 +553,27 @@ void RenderViewContextMenu::InitMenu() {
#endif
}
+ if (params_.is_editable) {
+ // Add a menu item that shows suggestions from the Spelling service.
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kExperimentalSpellcheckerFeatures)) {
+ PrefService* pref = profile_->GetPrefs();
+ bool use_spelling_service =
+ pref && pref->GetBoolean(prefs::kSpellCheckUseSpellingService);
+ if (use_spelling_service) {
+ if (!spelling_menu_observer_.get())
+ spelling_menu_observer_.reset(new SpellingMenuObserver(this));
+
+ if (spelling_menu_observer_.get())
+ observers_.AddObserver(spelling_menu_observer_.get());
+ }
+ }
+ }
+
+ // Ask our observers to add their menu items.
+ FOR_EACH_OBSERVER(RenderViewContextMenuObserver, observers_,
+ InitMenu(params_));
+
if (params_.is_editable)
AppendEditableItems();
else if (has_selection)
@@ -571,6 +593,27 @@ void RenderViewContextMenu::LookUpInDictionary() {
NOTREACHED();
}
+void RenderViewContextMenu::AddMenuItem(int command_id,
+ const string16& title) {
+ menu_model_.AddItem(command_id, title);
+}
+
+void RenderViewContextMenu::UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& label) {
+ // This function needs platform-specific implementation.
+ NOTIMPLEMENTED();
+}
+
+
+RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const {
+ return source_tab_contents_->render_view_host();
+}
+
+Profile* RenderViewContextMenu::GetProfile() const {
+ return profile_;
+}
+
bool RenderViewContextMenu::AppendCustomItems() {
size_t total_items = 0;
AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
@@ -794,7 +837,7 @@ void RenderViewContextMenu::AppendEditableItems() {
}
// If word is misspelled, give option for "Add to dictionary"
- if (!params_.misspelled_word.empty()) {
+ if (!spelling_menu_observer_.get() && !params_.misspelled_word.empty()) {
if (params_.dictionary_suggestions.empty()) {
menu_model_.AddItem(IDC_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS,
l10n_util::GetStringUTF16(
@@ -961,6 +1004,15 @@ ExtensionMenuItem* RenderViewContextMenu::GetExtensionMenuItem(int id) const {
// Menu delegate functions -----------------------------------------------------
bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
+ // If this command is is added by one of our observers, we dispatch it to the
+ // observer.
+ ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
+ RenderViewContextMenuObserver* observer;
+ while ((observer = it.GetNext()) != NULL) {
+ if (observer->IsCommandIdSupported(id))
+ return observer->IsCommandIdEnabled(id);
+ }
+
if (id == IDC_PRINT &&
(source_tab_contents_->content_restrictions() &
CONTENT_RESTRICTION_PRINT)) {
@@ -1329,6 +1381,15 @@ bool RenderViewContextMenu::IsCommandIdChecked(int id) const {
}
void RenderViewContextMenu::ExecuteCommand(int id) {
+ // If this command is is added by one of our observers, we dispatch it to the
+ // observer.
+ ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_);
+ RenderViewContextMenuObserver* observer;
+ while ((observer = it.GetNext()) != NULL) {
+ if (observer->IsCommandIdSupported(id))
+ return observer->ExecuteCommand(id);
+ }
+
// Check to see if one of the spell check language ids have been clicked.
if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
id < IDC_SPELLCHECK_LANGUAGES_LAST) {
diff --git a/chrome/browser/tab_contents/render_view_context_menu.h b/chrome/browser/tab_contents/render_view_context_menu.h
index 65bb3dd..513ab0a 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.h
+++ b/chrome/browser/tab_contents/render_view_context_menu.h
@@ -12,9 +12,11 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
#include "base/string16.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "chrome/browser/extensions/extension_menu_manager.h"
+#include "chrome/browser/tab_contents/render_view_context_menu_observer.h"
#include "content/common/page_transition_types.h"
#include "ui/base/models/simple_menu_model.h"
#include "webkit/glue/context_menu.h"
@@ -22,7 +24,9 @@
class ExtensionMenuItem;
class Profile;
+class RenderViewHost;
class TabContents;
+class SpellingMenuObserver;
namespace gfx {
class Point;
@@ -32,7 +36,74 @@ namespace WebKit {
struct WebMediaPlayerAction;
}
-class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate {
+// An interface that controls a RenderViewContextMenu instance from observers.
+// This interface is designed mainly for controlling the instance while showing
+// so we can add a context-menu item that takes long time to create its text,
+// such as retrieving the item text from a server. The simplest usage is:
+// 1. Adding an item with temporary text;
+// 2. Posting a background task that creates the item text, and;
+// 3. Calling UpdateMenuItem() in the callback function.
+// The following snippet describes the simple usage that updates a context-menu
+// item with this interface.
+//
+// class MyTask : public URLFetcher::Delegate {
+// public:
+// MyTask(RenderViewContextMenuProxy* proxy, int id)
+// : proxy_(proxy),
+// id_(id) {
+// }
+// virtual ~MyTask() {
+// }
+// virtual void OnURLFetchComplete(const URLFetcher* source,
+// const GURL& url,
+// const net::URLRequestStatus& status,
+// int response,
+// const net::ResponseCookies& cookies,
+// const std::string& data) {
+// bool enabled = response == 200;
+// const char* text = enabled ? "OK" : "ERROR";
+// proxy_->UpdateMenuItem(id_, enabled, ASCIIToUTF16(text));
+// }
+// void Start(const GURL* url, net::URLRequestContextGetter* context) {
+// fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this));
+// fetcher_->set_request_context(context);
+// fetcher_->Start();
+// }
+//
+// private:
+// URLFetcher fetcher_;
+// RenderViewContextMenuProxy* proxy_;
+// int id_;
+// };
+//
+// void RenderViewContextMenu::AppendEditableItems() {
+// // Add a menu item with temporary text shown while we create the final
+// // text.
+// menu_model_.AddItemWithStringId(IDC_MY_ITEM, IDC_MY_TEXT);
+//
+// // Start a task that creates the final text.
+// my_task_ = new MyTask(this, IDC_MY_ITEM);
+// my_task_->Start(...);
+// }
+//
+class RenderViewContextMenuProxy {
+ public:
+ // Add a menu item to a context menu.
+ virtual void AddMenuItem(int command_id, const string16& title) = 0;
+
+ // Update the status and text of the specified context-menu item.
+ virtual void UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) = 0;
+
+ // Retrieve the RenderViewHost (or Profile) instance associated with a context
+ // menu, respectively.
+ virtual RenderViewHost* GetRenderViewHost() const = 0;
+ virtual Profile* GetProfile() const = 0;
+};
+
+class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate,
+ public RenderViewContextMenuProxy {
public:
static const size_t kMaxExtensionItemTitleLength;
static const size_t kMaxSelectionTextLength;
@@ -55,6 +126,14 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate {
virtual void MenuWillShow(ui::SimpleMenuModel* source) OVERRIDE;
virtual void MenuClosed(ui::SimpleMenuModel* source) OVERRIDE;
+ // RenderViewContextMenuDelegate implementation.
+ virtual void AddMenuItem(int command_id, const string16& title) OVERRIDE;
+ virtual void UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) OVERRIDE;
+ virtual RenderViewHost* GetRenderViewHost() const;
+ virtual Profile* GetProfile() const;
+
protected:
void InitMenu();
@@ -170,6 +249,12 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate {
ScopedVector<ui::SimpleMenuModel> extension_menu_models_;
scoped_refptr<ProtocolHandlerRegistry> protocol_handler_registry_;
+ // An observer that handles a spelling-menu items.
+ scoped_ptr<SpellingMenuObserver> spelling_menu_observer_;
+
+ // Our observers.
+ mutable ObserverList<RenderViewContextMenuObserver> observers_;
+
DISALLOW_COPY_AND_ASSIGN(RenderViewContextMenu);
};
diff --git a/chrome/browser/tab_contents/render_view_context_menu_gtk.cc b/chrome/browser/tab_contents/render_view_context_menu_gtk.cc
index 569bf53..24a4f99 100644
--- a/chrome/browser/tab_contents/render_view_context_menu_gtk.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu_gtk.cc
@@ -7,6 +7,7 @@
#include <gtk/gtk.h>
#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "content/browser/renderer_host/render_widget_host_view_gtk.h"
@@ -16,6 +17,60 @@
#include "ui/gfx/gtk_util.h"
#include "webkit/glue/context_menu.h"
+namespace {
+
+// A callback function for gtk_container_foreach(). This callback just checks
+// the menu ID and set the given user data if it is same as the specified ID.
+struct GtkWidgetAtParam {
+ int index;
+ GtkWidget* widget;
+};
+
+void GtkWidgetAt(GtkWidget* widget, gpointer user_data) {
+ GtkWidgetAtParam* param = reinterpret_cast<GtkWidgetAtParam*>(user_data);
+
+ gpointer data = g_object_get_data(G_OBJECT(widget), "menu-id");
+ if (data && (GPOINTER_TO_INT(data) - 1) == param->index &&
+ GTK_IS_MENU_ITEM(widget)) {
+ param->widget = widget;
+ }
+}
+
+// Retrieves a GtkWidget which has the specified command_id. This function
+// traverses the given |model| in the depth-first order. When this function
+// finds an item whose command_id is the same as the given |command_id|, it
+// returns the GtkWidget associated with the item. This function emulates
+// views::MenuItemViews::GetMenuItemByID() for GTK.
+GtkWidget* GetMenuItemByID(ui::MenuModel* model,
+ GtkWidget* menu,
+ int command_id) {
+ if (!menu)
+ return NULL;
+
+ for (int i = 0; i < model->GetItemCount(); ++i) {
+ if (model->GetCommandIdAt(i) == command_id) {
+ GtkWidgetAtParam param;
+ param.index = i;
+ param.widget = NULL;
+ gtk_container_foreach(GTK_CONTAINER(menu), &GtkWidgetAt, &param);
+ return param.widget;
+ }
+
+ ui::MenuModel* submenu = model->GetSubmenuModelAt(i);
+ if (submenu) {
+ GtkWidget* subitem = GetMenuItemByID(
+ submenu,
+ gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)),
+ command_id);
+ if (subitem)
+ return subitem;
+ }
+ }
+ return NULL;
+}
+
+} // namespace
+
RenderViewContextMenuGtk::RenderViewContextMenuGtk(
TabContents* web_contents,
const ContextMenuParams& params,
@@ -70,3 +125,16 @@ bool RenderViewContextMenuGtk::AlwaysShowIconForCmd(int command_id) const {
return command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST;
}
+
+void RenderViewContextMenuGtk::UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) {
+ GtkWidget* item = GetMenuItemByID(&menu_model_, menu_gtk_->widget(),
+ command_id);
+ if (!item || !GTK_IS_MENU_ITEM(item))
+ return;
+
+ // Enable (or disable) the menu item and updates its text.
+ gtk_widget_set_sensitive(item, enabled);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item), UTF16ToUTF8(title).c_str());
+}
diff --git a/chrome/browser/tab_contents/render_view_context_menu_gtk.h b/chrome/browser/tab_contents/render_view_context_menu_gtk.h
index 11cc91c..12862f3 100644
--- a/chrome/browser/tab_contents/render_view_context_menu_gtk.h
+++ b/chrome/browser/tab_contents/render_view_context_menu_gtk.h
@@ -28,6 +28,11 @@ class RenderViewContextMenuGtk : public RenderViewContextMenu,
// Menu::Delegate implementation ---------------------------------------------
virtual bool AlwaysShowIconForCmd(int command_id) const;
+ // RenderViewContextMenuDelegate implementation ------------------------------
+ virtual void UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) OVERRIDE;
+
protected:
// RenderViewContextMenu implementation --------------------------------------
virtual void PlatformInit();
diff --git a/chrome/browser/tab_contents/render_view_context_menu_mac.h b/chrome/browser/tab_contents/render_view_context_menu_mac.h
index 5c52a5b..bc6ced6 100644
--- a/chrome/browser/tab_contents/render_view_context_menu_mac.h
+++ b/chrome/browser/tab_contents/render_view_context_menu_mac.h
@@ -25,6 +25,11 @@ class RenderViewContextMenuMac : public RenderViewContextMenu {
virtual ~RenderViewContextMenuMac();
virtual void ExecuteCommand(int id);
+ // RenderViewContextMenuDelegate implementation.
+ virtual void UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) OVERRIDE;
+
protected:
// RenderViewContextMenu implementation-
virtual void PlatformInit();
diff --git a/chrome/browser/tab_contents/render_view_context_menu_mac.mm b/chrome/browser/tab_contents/render_view_context_menu_mac.mm
index f7c52be..6efeec3 100644
--- a/chrome/browser/tab_contents/render_view_context_menu_mac.mm
+++ b/chrome/browser/tab_contents/render_view_context_menu_mac.mm
@@ -14,6 +14,35 @@
#import "content/common/chrome_application_mac.h"
#include "grit/generated_resources.h"
+namespace {
+
+// Retrieves an NSMenuItem which has the specified command_id. This function
+// traverses the given |model| in the depth-first order. When this function
+// finds an item whose command_id is the same as the given |command_id|, it
+// returns the NSMenuItem associated with the item. This function emulates
+// views::MenuItemViews::GetMenuItemByID() for Mac.
+NSMenuItem* GetMenuItemByID(ui::MenuModel* model,
+ NSMenu* menu,
+ int command_id) {
+ for (int i = 0; i < model->GetItemCount(); ++i) {
+ NSMenuItem* item = [menu itemAtIndex:i];
+ if (model->GetCommandIdAt(i) == command_id)
+ return item;
+
+ ui::MenuModel* submenu = model->GetSubmenuModelAt(i);
+ if (submenu && [item hasSubmenu]) {
+ NSMenuItem* subitem = GetMenuItemByID(submenu,
+ [item submenu],
+ command_id);
+ if (subitem)
+ return subitem;
+ }
+ }
+ return nil;
+}
+
+} // namespace
+
// Obj-C bridge class that is the target of all items in the context menu.
// Relies on the tag being set to the command id.
@@ -111,3 +140,17 @@ void RenderViewContextMenuMac::LookUpInDictionary() {
if (ok)
NSPerformService(@"Look Up in Dictionary", pboard);
}
+
+void RenderViewContextMenuMac::UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) {
+ NSMenuItem* item = GetMenuItemByID(&menu_model_, [menuController_ menu],
+ command_id);
+ if (!item)
+ return;
+
+ // Update the returned NSMenuItem directly so we can update it immediately.
+ [item setEnabled:enabled];
+ [item setTitle:SysUTF16ToNSString(title)];
+ [[item menu] itemChanged:item];
+}
diff --git a/chrome/browser/tab_contents/render_view_context_menu_observer.cc b/chrome/browser/tab_contents/render_view_context_menu_observer.cc
new file mode 100644
index 0000000..b6311c6
--- /dev/null
+++ b/chrome/browser/tab_contents/render_view_context_menu_observer.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 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/tab_contents/render_view_context_menu_observer.h"
+
+void RenderViewContextMenuObserver::InitMenu(const ContextMenuParams& params) {
+}
+
+bool RenderViewContextMenuObserver::IsCommandIdSupported(int command_id) {
+ return false;
+}
+
+bool RenderViewContextMenuObserver::IsCommandIdEnabled(int command_id) {
+ return false;
+}
+
+void RenderViewContextMenuObserver::ExecuteCommand(int command_id) {
+}
diff --git a/chrome/browser/tab_contents/render_view_context_menu_observer.h b/chrome/browser/tab_contents/render_view_context_menu_observer.h
new file mode 100644
index 0000000..34823e8
--- /dev/null
+++ b/chrome/browser/tab_contents/render_view_context_menu_observer.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 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_TAB_CONTENTS_RENDER_VIEW_CONTEXT_MENU_OBSERVER_H_
+#define CHROME_BROWSER_TAB_CONTENTS_RENDER_VIEW_CONTEXT_MENU_OBSERVER_H_
+#pragma once
+
+struct ContextMenuParams;
+
+// The interface used for implementing context-menu items. The following
+// instruction describe how to implement a context-menu item with this
+// interface.
+//
+// 1. Add command IDs for the context-menu items to 'chrome_command_ids.h'.
+//
+// #define IDC_MY_COMMAND 99999
+//
+// 2. Add strings for the context-menu items to 'generated_sources.grd'.
+//
+// <message name="IDS_MY_COMMAND" desc="...">
+// My command
+// </message>
+//
+// 3. Create a class that implements this interface. (It is a good idea to use
+// the RenderViewContextMenuDelegate interface to avoid accessing the
+// RenderViewContextMenu class directly.)
+//
+// class MyMenuObserver : public RenderViewContextMenuObserver {
+// public:
+// MyMenuObserver(RenderViewContextMenuDelegate* d);
+// ~MyMenuObserver();
+//
+// virtual void InitMenu(const ContextMenuParams& params) OVERRIDE;
+// virtual bool IsCommandIdSupported(int command_id) OVERRIDE;
+// virtual bool IsCommandIdEnabled(int command_id) OVERRIDE;
+// virtual void ExecuteCommand(int command_id) OVERRIDE;
+//
+// private:
+// RenderViewContextMenuDelgate* delegate_;
+// }
+//
+// void MyMenuObserver::InitMenu(const ContextMenuParams& params) {
+// delegate_->AddMenuItem(IDC_MY_COMMAND,...);
+// }
+//
+// bool MyMenuObserver::IsCommandIdSupported(int command_id) {
+// return command_id == IDC_MY_COMMAND;
+// }
+//
+// bool MyMenuObserver::IsCommandIdEnabled(int command_id) {
+// DCHECK(command_id == IDC_MY_COMMAND);
+// return true;
+// }
+//
+// void MyMenuObserver::ExecuteCommand(int command_id) {
+// DCHECK(command_id == IDC_MY_COMMAND);
+// }
+//
+// 4. Add this observer class to the RenderViewContextMenu class. (It is good
+// to use scoped_ptr<> so Chrome can create its instances only when it needs.)
+//
+// class RenderViewContextMenu {
+// ...
+// private:
+// scoped_ptr<MyMenuObserver> my_menu_observer_;
+// };
+//
+// 5. Create its instance in InitMenu() and add it to the observer list of the
+// RenderViewContextMenu class.
+//
+// void RenderViewContextMenu::InitMenu() {
+// ...
+// my_menu_observer_.reset(new MyMenuObserver(this));
+// observers_.AddObserver(my_menu_observer_.get());
+// }
+//
+//
+class RenderViewContextMenuObserver {
+ public:
+ virtual ~RenderViewContextMenuObserver() {}
+
+ // Called when the RenderViewContextMenu class initializes a context menu. We
+ // usually call RenderViewContextMenuDelegate::AddMenuItem() to add menu items
+ // in this function.
+ virtual void InitMenu(const ContextMenuParams& params);
+
+ // Called when the RenderViewContextMenu class asks whether an observer
+ // listens for the specified command ID. If this function returns true, the
+ // RenderViewContextMenu class calls IsCommandIdEnabled() or ExecuteCommand().
+ virtual bool IsCommandIdSupported(int command_id);
+
+ // Called when the RenderViewContextMenu class sets the initial status of the
+ // specified context-menu item. If we need to enable or disable a context-menu
+ // item while showing, use RenderViewContextMenuDelegate::UpdateMenuItem().
+ virtual bool IsCommandIdEnabled(int command_id);
+
+ // Called when a user selects the specified context-menu item.
+ virtual void ExecuteCommand(int command_id);
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_RENDER_VIEW_CONTEXT_MENU_OBSERVER_H_
diff --git a/chrome/browser/tab_contents/spelling_menu_observer.cc b/chrome/browser/tab_contents/spelling_menu_observer.cc
new file mode 100644
index 0000000..0a77f77
--- /dev/null
+++ b/chrome/browser/tab_contents/spelling_menu_observer.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2011 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/tab_contents/spelling_menu_observer.h"
+
+#include <string>
+
+#include "base/json/json_reader.h"
+#include "base/json/string_escape.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab_contents/render_view_context_menu.h"
+#include "chrome/common/pref_names.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "unicode/uloc.h"
+#include "webkit/glue/context_menu.h"
+
+#if defined(GOOGLE_CHROME_BUILD)
+#include "chrome/browser/spellchecker/internal/spellcheck_internal.h"
+#else
+// Use a dummy URL and a key on Chromium to avoid build breaks until the
+// Spelling API is released. These dummy parameters just cause a timeout and
+// show 'no suggestions found'.
+#define SPELLING_SERVICE_KEY
+#define SPELLING_SERVICE_URL "http://127.0.0.1/rpc"
+#endif
+
+SpellingMenuObserver::SpellingMenuObserver(RenderViewContextMenuProxy* proxy)
+ : proxy_(proxy),
+ loading_frame_(0),
+ succeeded_(false) {
+}
+
+SpellingMenuObserver::~SpellingMenuObserver() {
+}
+
+void SpellingMenuObserver::InitMenu(const ContextMenuParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Exit if we are not in an editable element because we add a menu item only
+ // for editable elements.
+ if (!params.is_editable)
+ return;
+
+ Profile* profile = proxy_->GetProfile();
+ if (!profile || !profile->GetRequestContext())
+ return;
+
+ // Retrieve the misspelled word to be sent to the Spelling service.
+ string16 text = params.misspelled_word;
+ if (text.empty())
+ return;
+
+ // Initialize variables used in OnURLFetchComplete(). We copy the input text
+ // to the result text so we can replace its misspelled regions with
+ // suggestions.
+ loading_frame_ = 0;
+ succeeded_ = false;
+ result_ = text;
+
+ // Add a placeholder item. This item will be updated when we receive a
+ // response from the Spelling service. (We do not have to disable this item
+ // now since Chrome will call IsCommandIdEnabled() and disable it.)
+ loading_message_ =
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLING_CHECKING);
+ proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION,
+ loading_message_);
+
+ // Invoke a JSON-RPC call to the Spelling service in the background so we can
+ // update the placeholder item when we receive its response. It also starts
+ // the animation timer so we can show animation until we receive it.
+ const PrefService* pref = profile->GetPrefs();
+ std::string language =
+ pref ? pref->GetString(prefs::kSpellCheckDictionary) : "en-US";
+ Invoke(text, language, profile->GetRequestContext());
+}
+
+bool SpellingMenuObserver::IsCommandIdSupported(int command_id) {
+ return command_id == IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION;
+}
+
+bool SpellingMenuObserver::IsCommandIdEnabled(int command_id) {
+ return command_id == IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION && succeeded_;
+}
+
+void SpellingMenuObserver::ExecuteCommand(int command_id) {
+ if (IsCommandIdEnabled(command_id))
+ proxy_->GetRenderViewHost()->Replace(result_);
+}
+
+bool SpellingMenuObserver::Invoke(const string16& text,
+ const std::string& locale,
+ net::URLRequestContextGetter* context) {
+ // Create the parameters needed by Spelling API. Spelling API needs three
+ // parameters: ISO language code, ISO3 country code, and text to be checked by
+ // the service. On the other hand, Chrome uses an ISO locale ID and it may
+ // not include a country ID, e.g. "fr", "de", etc. To create the input
+ // parameters, we convert the UI locale to a full locale ID, and convert the
+ // full locale ID to an ISO language code and and ISO3 country code. Also, we
+ // convert the given text to a JSON string, i.e. quote all its non-ASCII
+ // characters.
+ UErrorCode error = U_ZERO_ERROR;
+ char id[ULOC_LANG_CAPACITY + ULOC_SCRIPT_CAPACITY + ULOC_COUNTRY_CAPACITY];
+ uloc_addLikelySubtags(locale.c_str(), id, arraysize(id), &error);
+
+ error = U_ZERO_ERROR;
+ char language[ULOC_LANG_CAPACITY];
+ uloc_getLanguage(id, language, arraysize(language), &error);
+
+ const char* country = uloc_getISO3Country(id);
+
+ std::string encoded_text;
+ base::JsonDoubleQuote(text, false, &encoded_text);
+
+ // Format the JSON request to be sent to the Spelling service.
+ static const char kSpellingRequest[] =
+ "{"
+ "\"method\":\"spelling.check\","
+ "\"apiVersion\":\"v1\","
+ "\"params\":{"
+ "\"text\":\"%s\","
+ "\"language\":\"%s\","
+ "\"origin_country\":\"%s\","
+ "\"key\":\"" SPELLING_SERVICE_KEY "\""
+ "}"
+ "}";
+ std::string request = base::StringPrintf(kSpellingRequest,
+ encoded_text.c_str(),
+ language, country);
+
+ static const char kSpellingServiceURL[] = SPELLING_SERVICE_URL;
+ GURL url = GURL(kSpellingServiceURL);
+ fetcher_.reset(new URLFetcher(url, URLFetcher::POST, this));
+ fetcher_->set_request_context(context);
+ fetcher_->set_upload_data("application/json", request);
+ fetcher_->Start();
+
+ animation_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
+ this, &SpellingMenuObserver::OnAnimationTimerExpired);
+
+ return true;
+}
+
+void SpellingMenuObserver::OnURLFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response,
+ const net::ResponseCookies& cookies,
+ const std::string& data) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ fetcher_.reset();
+ animation_timer_.Stop();
+
+ // Parse the response JSON and replace misspelled words in the |result_| text
+ // with their suggestions.
+ succeeded_ = ParseResponse(response, data);
+ if (!succeeded_)
+ result_ = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLING_CORRECT);
+
+ // Update the menu item with the result text. We enable this item only when
+ // the request text has misspelled words. (We disable this item not only when
+ // we receive a server error but also when the input text consists only of
+ // well-spelled words. For either case, we do not need to replace the input
+ // text.)
+ proxy_->UpdateMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION, succeeded_,
+ result_);
+}
+
+bool SpellingMenuObserver::ParseResponse(int response,
+ const std::string& data) {
+ // When this JSON-RPC call finishes successfully, the Spelling service returns
+ // an JSON object listed below.
+ // * result - an envelope object representing the result from the APIARY
+ // server, which is the JSON-API front-end for the Spelling service. This
+ // object consists of the following variable:
+ // - spellingCheckResponse (SpellingCheckResponse).
+ // * SpellingCheckResponse - an object representing the result from the
+ // Spelling service. This object consists of the following variable:
+ // - misspellings (optional array of Misspelling)
+ // * Misspelling - an object representing a misspelling region and its
+ // suggestions. This object consists of the following variables:
+ // - charStart (number) - the beginning of the misspelled region;
+ // - charLength (number) - the length of the misspelled region;
+ // - suggestions (array of string) - the suggestions for the misspelling
+ // text, and;
+ // - canAutoCorrect (optional boolean) - whether we can use the first
+ // suggestion for auto-correction.
+ // For example, the Spelling service returns the following JSON when we send a
+ // spelling request for "duck goes quisk" as of 16 August, 2011.
+ // {
+ // "result": {
+ // "spellingCheckResponse": {
+ // "misspellings": [{
+ // "charStart": 10,
+ // "charLength": 5,
+ // "suggestions": [{ "suggestion": "quack" }],
+ // "canAutoCorrect": false
+ // }]
+ // }
+ // }
+
+ // When a server error happened in the APIARY server (including when we cannot
+ // get any responses from the server), it returns an HTTP error.
+ if ((response / 100) != 2)
+ return false;
+
+ scoped_ptr<DictionaryValue> value(
+ static_cast<DictionaryValue*>(base::JSONReader::Read(data, true)));
+ if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+ return false;
+
+ // Retrieve the array of Misspelling objects. When the APIARY service failed
+ // calling the Spelling service, it returns a JSON representing the service
+ // error. (In this case, its HTTP status is 200.) We just return false for
+ // this case.
+ ListValue* misspellings = NULL;
+ const char kMisspellings[] = "result.spellingCheckResponse.misspellings";
+ if (!value->GetList(kMisspellings, &misspellings))
+ return false;
+
+ // For each misspelled region, we replace it with its first suggestion.
+ for (size_t i = 0; i < misspellings->GetSize(); ++i) {
+ // Retrieve the i-th misspelling region, which represents misspelling text
+ // in the request text and its alternative.
+ DictionaryValue* misspelling = NULL;
+ if (!misspellings->GetDictionary(i, &misspelling))
+ return false;
+
+ int start = 0;
+ int length = 0;
+ ListValue* suggestions = NULL;
+ if (!misspelling->GetInteger("charStart", &start) ||
+ !misspelling->GetInteger("charLength", &length) ||
+ !misspelling->GetList("suggestions", &suggestions)) {
+ return false;
+ }
+
+ // Retrieve the alternative text and replace the misspelling region with the
+ // alternative.
+ DictionaryValue* suggestion = NULL;
+ string16 text;
+ if (!suggestions->GetDictionary(0, &suggestion) ||
+ !suggestion->GetString("suggestion", &text)) {
+ return false;
+ }
+ result_.replace(start, length, text);
+ }
+
+ return true;
+}
+
+void SpellingMenuObserver::OnAnimationTimerExpired() {
+ if (!fetcher_.get())
+ return;
+
+ // Append '.' characters to the end of "Checking".
+ loading_frame_ = (loading_frame_ + 1) & 3;
+ string16 loading_message = loading_message_;
+ for (int i = 0; i < loading_frame_; ++i)
+ loading_message.push_back('.');
+
+ // Update the menu item with the text. We disable this item to prevent users
+ // from selecting it.
+ proxy_->UpdateMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION, false,
+ loading_message);
+}
diff --git a/chrome/browser/tab_contents/spelling_menu_observer.h b/chrome/browser/tab_contents/spelling_menu_observer.h
new file mode 100644
index 0000000..60e7a94
--- /dev/null
+++ b/chrome/browser/tab_contents/spelling_menu_observer.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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_TAB_CONTENTS_SPELLING_MENU_OBSERVER_H_
+#define CHROME_BROWSER_TAB_CONTENTS_SPELLING_MENU_OBSERVER_H_
+#pragma once
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/timer.h"
+#include "chrome/browser/tab_contents/render_view_context_menu_observer.h"
+#include "content/common/url_fetcher.h"
+
+class GURL;
+class RenderViewContextMenuProxy;
+
+// An observer that listens to events from the RenderViewContextMenu class and
+// shows suggestions from the Spelling ("do you mean") service to a context menu
+// while we show it. This class implements two interfaces:
+// * RenderViewContextMenuObserver
+// This interface is used for adding a menu item and update it while showing.
+// * URLFetcher::Delegate
+// This interface is used for sending a JSON_RPC request to the Spelling
+// service and retrieving its response.
+// These interfaces allow this class to make a JSON-RPC call to the Spelling
+// service in the background and update the context menu while showing. The
+// following snippet describes how to add this class to the observer list of the
+// RenderViewContextMenu class.
+//
+// void RenderViewContextMenu::InitMenu() {
+// spelling_menu_observer_.reset(new SpellingMenuObserver(this));
+// if (spelling_menu_observer_.get())
+// observers_.AddObserver(spelling_menu_observer.get());
+// }
+//
+class SpellingMenuObserver : public RenderViewContextMenuObserver,
+ public URLFetcher::Delegate {
+ public:
+ explicit SpellingMenuObserver(RenderViewContextMenuProxy* proxy);
+ virtual ~SpellingMenuObserver();
+
+ // RenderViewContextMenuObserver implementation.
+ virtual void InitMenu(const ContextMenuParams& params) OVERRIDE;
+ virtual bool IsCommandIdSupported(int command_id) OVERRIDE;
+ virtual bool IsCommandIdEnabled(int command_id) OVERRIDE;
+ virtual void ExecuteCommand(int command_id) OVERRIDE;
+
+ // URLFetcher::Delegate implementation.
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& data) OVERRIDE;
+
+ private:
+ // Invokes a JSON-RPC call in the background. This function sends a JSON-RPC
+ // request to the Spelling service. Chrome will call OnURLFetchComplete() when
+ // it receives a response from the service.
+ bool Invoke(const string16& text,
+ const std::string& locale,
+ net::URLRequestContextGetter* context);
+
+ // Parses the specified response from the Spelling service.
+ bool ParseResponse(int code, const std::string& data);
+
+ // The callback function for base::RepeatingTimer<SpellingMenuClient>. This
+ // function updates the "loading..." animation in the context-menu item.
+ void OnAnimationTimerExpired();
+
+ // The interface to add a context-menu item and update it. This class uses
+ // this interface to avoid accesing context-menu items directly.
+ RenderViewContextMenuProxy* proxy_;
+
+ // The string used for animation until we receive a response from the Spelling
+ // service. The current animation just adds periods at the end of this string:
+ // 'Loading' -> 'Loading.' -> 'Loading..' -> 'Loading...' (-> 'Loading')
+ string16 loading_message_;
+ int loading_frame_;
+
+ // A flag represending whether this call finished successfully. This means we
+ // receive an empty JSON string or a JSON string that consists of misspelled
+ // words. ('spelling_menu_observer.cc' describes its format.)
+ bool succeeded_;
+
+ // The string representing the result of this call. This string is a
+ // suggestion when this call finished successfully. Otherwise it is error
+ // text. Until we receive a response from the Spelling service, this string
+ // stores the input string. (Since the Spelling service sends only misspelled
+ // words, we replace these misspelled words in the input text with the
+ // suggested words to create suggestion text.
+ string16 result_;
+
+ // The URLFetcher object used for sending a JSON-RPC request.
+ scoped_ptr<URLFetcher> fetcher_;
+
+ // A timer used for loading animation.
+ base::RepeatingTimer<SpellingMenuObserver> animation_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpellingMenuObserver);
+};
+
+#endif // CHROME_BROWSER_TAB_CONTENTS_SPELLING_MENU_OBSERVER_H_
diff --git a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
index cdf0ba9..efd7593 100644
--- a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
+++ b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
@@ -110,3 +110,20 @@ bool RenderViewContextMenuViews::GetAcceleratorForCommandId(
return false;
}
}
+
+void RenderViewContextMenuViews::UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) {
+ views::MenuItemView* item = menu_->GetMenuItemByID(command_id);
+ if (!item)
+ return;
+
+ item->SetEnabled(enabled);
+ item->SetTitle(UTF16ToWide(title));
+
+ views::MenuItemView* parent = item->GetParentMenuItem();
+ if (!parent)
+ return;
+
+ parent->ChildrenChanged();
+}
diff --git a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h
index 023a6c0..220768d 100644
--- a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h
+++ b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/string16.h"
#include "chrome/browser/tab_contents/render_view_context_menu.h"
namespace views {
@@ -33,11 +34,18 @@ class RenderViewContextMenuViews : public RenderViewContextMenu {
void UpdateMenuItemStates();
+ // RenderViewContextMenuDelegate implementation.
+ virtual void UpdateMenuItem(int command_id,
+ bool enabled,
+ const string16& title) OVERRIDE;
+
protected:
- // RenderViewContextMenu implementation --------------------------------------
- virtual void PlatformInit();
- virtual bool GetAcceleratorForCommandId(int command_id,
- ui::Accelerator* accelerator);
+ // RenderViewContextMenu implementation.
+ virtual void PlatformInit() OVERRIDE;
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ ui::Accelerator* accelerator) OVERRIDE;
+
private:
// The context menu itself and its contents.
scoped_ptr<views::MenuModelAdapter> menu_delegate_;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index a8bcc15..f8b362d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2187,6 +2187,8 @@
'browser/tab_contents/render_view_context_menu_gtk.h',
'browser/tab_contents/render_view_context_menu_mac.h',
'browser/tab_contents/render_view_context_menu_mac.mm',
+ 'browser/tab_contents/render_view_context_menu_observer.cc',
+ 'browser/tab_contents/render_view_context_menu_observer.h',
'browser/tab_contents/render_view_host_delegate_helper.cc',
'browser/tab_contents/render_view_host_delegate_helper.h',
'browser/tab_contents/tab_contents_ssl_helper.cc',
@@ -2199,6 +2201,8 @@
'browser/tab_contents/tab_util.h',
'browser/tab_contents/thumbnail_generator.cc',
'browser/tab_contents/thumbnail_generator.h',
+ 'browser/tab_contents/spelling_menu_observer.cc',
+ 'browser/tab_contents/spelling_menu_observer.h',
'browser/tab_contents/web_drag_dest_gtk.cc',
'browser/tab_contents/web_drag_dest_gtk.h',
'browser/tab_contents/web_drag_source_win.cc',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index d406875..1ef81de 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1080,6 +1080,9 @@ const char kPluginMessageResponseTimeout[] =
// String which represents the dictionary name for our spell-checker.
const char kSpellCheckDictionary[] = "spellcheck.dictionary";
+// String which represents whether we use the spelling service.
+const char kSpellCheckUseSpellingService[] = "spellcheck.use_spelling_service";
+
// Dictionary of schemes used by the external protocol handler.
// The value is true if the scheme must be ignored.
const char kExcludedSchemes[] = "protocol_handler.excluded_schemes";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index accfb4c..cd31f67 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -384,6 +384,7 @@ extern const char kHungPluginDetectFrequency[];
extern const char kPluginMessageResponseTimeout[];
extern const char kSpellCheckDictionary[];
+extern const char kSpellCheckUseSpellingService[];
extern const char kExcludedSchemes[];