diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index 46a19d89d..1606c9bea 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -367,6 +367,12 @@ typedef struct _cef_browser_host_t { void (CEF_CALLBACK *replace_misspelling)(struct _cef_browser_host_t* self, const cef_string_t* word); + /// + // Add the specified |word| to the spelling dictionary. + /// + void (CEF_CALLBACK *add_word_to_dictionary)(struct _cef_browser_host_t* self, + const cef_string_t* word); + /// // Returns true (1) if window rendering is disabled. /// diff --git a/include/cef_browser.h b/include/cef_browser.h index d1f3a3c9f..4174f4760 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -413,6 +413,12 @@ class CefBrowserHost : public virtual CefBase { /*--cef()--*/ virtual void ReplaceMisspelling(const CefString& word) =0; + /// + // Add the specified |word| to the spelling dictionary. + /// + /*--cef()--*/ + virtual void AddWordToDictionary(const CefString& word) =0; + /// // Returns true if window rendering is disabled. /// diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index d478764f7..a23eb1266 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -1308,6 +1308,7 @@ typedef enum { MENU_ID_SPELLCHECK_SUGGESTION_4 = 204, MENU_ID_SPELLCHECK_SUGGESTION_LAST = 204, MENU_ID_NO_SPELLING_SUGGESTIONS = 205, + MENU_ID_ADD_TO_DICTIONARY = 206, // All user-defined menu IDs should come between MENU_ID_USER_FIRST and // MENU_ID_USER_LAST to avoid overlapping the Chromium and CEF ID ranges diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index 7d0fd3c8e..a76bd0255 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -36,6 +36,8 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/spellchecker/spellcheck_factory.h" +#include "chrome/browser/spellchecker/spellcheck_service.h" #include "components/pdf/common/pdf_messages.h" #include "content/browser/gpu/compositor_util.h" #include "content/browser/renderer_host/render_widget_host_impl.h" @@ -61,6 +63,10 @@ #include "ui/gfx/font_render_params.h" #endif +#if defined(OS_MACOSX) +#include "chrome/browser/spellchecker/spellcheck_platform_mac.h" +#endif + #if defined(USE_AURA) #include "ui/views/widget/widget.h" #endif @@ -876,6 +882,29 @@ void CefBrowserHostImpl::ReplaceMisspelling(const CefString& word) { web_contents()->ReplaceMisspelling(word); } +void CefBrowserHostImpl::AddWordToDictionary(const CefString& word) { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefBrowserHostImpl::AddWordToDictionary, this, word)); + return; + } + + if (!web_contents()) + return; + + content::BrowserContext* browser_context = + web_contents()->GetBrowserContext(); + if (browser_context) { + SpellcheckService* spellcheck = + SpellcheckServiceFactory::GetForContext(browser_context); + if (spellcheck) + spellcheck->GetCustomDictionary()->AddWord(word); + } +#if defined(OS_MACOSX) + spellcheck_mac::AddWord(word); +#endif +} + void CefBrowserHostImpl::WasResized() { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 6bce5ecb6..1e6f2a9aa 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -160,6 +160,7 @@ class CefBrowserHostImpl : public CefBrowserHost, virtual bool IsMouseCursorChangeDisabled() OVERRIDE; virtual bool IsWindowRenderingDisabled() OVERRIDE; virtual void ReplaceMisspelling(const CefString& word) OVERRIDE; + virtual void AddWordToDictionary(const CefString& word) OVERRIDE; virtual void WasResized() OVERRIDE; virtual void WasHidden(bool hidden) OVERRIDE; virtual void NotifyScreenInfoChanged() OVERRIDE; diff --git a/libcef/browser/menu_creator.cc b/libcef/browser/menu_creator.cc index 76980a249..26f787214 100644 --- a/libcef/browser/menu_creator.cc +++ b/libcef/browser/menu_creator.cc @@ -233,23 +233,33 @@ void CefMenuCreator::CreateDefaultModel() { model_->SetEnabled(MENU_ID_SELECT_ALL, false); if(!params_.misspelled_word.empty()) { - if (!params_.dictionary_suggestions.empty()) + // Always add a separator before the list of dictionary suggestions or + // "No spelling suggestions". + model_->AddSeparator(); + + if (!params_.dictionary_suggestions.empty()) { + for (size_t i = 0; + i < params_.dictionary_suggestions.size() && + MENU_ID_SPELLCHECK_SUGGESTION_0 + i <= + MENU_ID_SPELLCHECK_SUGGESTION_LAST; + ++i) { + model_->AddItem(MENU_ID_SPELLCHECK_SUGGESTION_0 + static_cast(i), + params_.dictionary_suggestions[i].c_str()); + } + + // When there are dictionary suggestions add a separator before "Add to + // dictionary". model_->AddSeparator(); - - for (size_t i = 0; - i < params_.dictionary_suggestions.size() && - MENU_ID_SPELLCHECK_SUGGESTION_0 + i <= - MENU_ID_SPELLCHECK_SUGGESTION_LAST; - ++i) { - model_->AddItem(MENU_ID_SPELLCHECK_SUGGESTION_0 + static_cast(i), - params_.dictionary_suggestions[i].c_str()); - } - - if (params_.dictionary_suggestions.empty()) { + } else { model_->AddItem( MENU_ID_NO_SPELLING_SUGGESTIONS, - GetLabel(IDS_MENU_NO_SPELLING_SUGGESTIONS)); + GetLabel(IDS_CONTENT_CONTEXT_NO_SPELLING_SUGGESTIONS)); + model_->SetEnabled(MENU_ID_NO_SPELLING_SUGGESTIONS, false); } + + model_->AddItem( + MENU_ID_ADD_TO_DICTIONARY, + GetLabel(IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY)); } } else if (!params_.selection_text.empty()) { // Something is selected. @@ -336,6 +346,11 @@ void CefMenuCreator::ExecuteDefaultCommand(int command_id) { browser_->GetFocusedFrame()->ViewSource(); break; + // Spell checking. + case MENU_ID_ADD_TO_DICTIONARY: + browser_->GetHost()->AddWordToDictionary(params_.misspelled_word); + break; + default: break; } diff --git a/libcef/resources/cef_strings.grd b/libcef/resources/cef_strings.grd index 49040658e..e2924227c 100644 --- a/libcef/resources/cef_strings.grd +++ b/libcef/resources/cef_strings.grd @@ -333,7 +333,10 @@ need to be translated for each locale.--> en-US - + + &Add to dictionary + + &No spelling suggestions diff --git a/libcef_dll/cpptoc/browser_host_cpptoc.cc b/libcef_dll/cpptoc/browser_host_cpptoc.cc index 54eff0fb4..5c90f7902 100644 --- a/libcef_dll/cpptoc/browser_host_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_host_cpptoc.cc @@ -434,6 +434,23 @@ void CEF_CALLBACK browser_host_replace_misspelling( CefString(word)); } +void CEF_CALLBACK browser_host_add_word_to_dictionary( + struct _cef_browser_host_t* self, const cef_string_t* word) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: word; type: string_byref_const + DCHECK(word); + if (!word) + return; + + // Execute + CefBrowserHostCppToC::Get(self)->AddWordToDictionary( + CefString(word)); +} + int CEF_CALLBACK browser_host_is_window_rendering_disabled( struct _cef_browser_host_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -806,6 +823,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC(CefBrowserHost* cls) struct_.struct_.is_mouse_cursor_change_disabled = browser_host_is_mouse_cursor_change_disabled; struct_.struct_.replace_misspelling = browser_host_replace_misspelling; + struct_.struct_.add_word_to_dictionary = browser_host_add_word_to_dictionary; struct_.struct_.is_window_rendering_disabled = browser_host_is_window_rendering_disabled; struct_.struct_.was_resized = browser_host_was_resized; diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.cc b/libcef_dll/ctocpp/browser_host_ctocpp.cc index 77245666f..dd5b09fc0 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_host_ctocpp.cc @@ -350,6 +350,22 @@ void CefBrowserHostCToCpp::ReplaceMisspelling(const CefString& word) { word.GetStruct()); } +void CefBrowserHostCToCpp::AddWordToDictionary(const CefString& word) { + if (CEF_MEMBER_MISSING(struct_, add_word_to_dictionary)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: word; type: string_byref_const + DCHECK(!word.empty()); + if (word.empty()) + return; + + // Execute + struct_->add_word_to_dictionary(struct_, + word.GetStruct()); +} + bool CefBrowserHostCToCpp::IsWindowRenderingDisabled() { if (CEF_MEMBER_MISSING(struct_, is_window_rendering_disabled)) return false; diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.h b/libcef_dll/ctocpp/browser_host_ctocpp.h index f4dada2b1..8cb5a624c 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.h +++ b/libcef_dll/ctocpp/browser_host_ctocpp.h @@ -63,6 +63,7 @@ class CefBrowserHostCToCpp virtual void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE; virtual bool IsMouseCursorChangeDisabled() OVERRIDE; virtual void ReplaceMisspelling(const CefString& word) OVERRIDE; + virtual void AddWordToDictionary(const CefString& word) OVERRIDE; virtual bool IsWindowRenderingDisabled() OVERRIDE; virtual void WasResized() OVERRIDE; virtual void WasHidden(bool hidden) OVERRIDE;