Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / chrome / renderer / spellchecker / spellcheck_provider.cc
blob7389502c1bb88b77fb300fd9c91c5126c9de4bd0
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/spellchecker/spellcheck_provider.h"
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/common/chrome_switches.h"
10 #include "chrome/common/spellcheck_marker.h"
11 #include "chrome/common/spellcheck_messages.h"
12 #include "chrome/common/spellcheck_result.h"
13 #include "chrome/renderer/spellchecker/spellcheck.h"
14 #include "content/public/renderer/render_view.h"
15 #include "third_party/WebKit/public/platform/WebVector.h"
16 #include "third_party/WebKit/public/web/WebElement.h"
17 #include "third_party/WebKit/public/web/WebFrame.h"
18 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
19 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
20 #include "third_party/WebKit/public/web/WebTextDecorationType.h"
21 #include "third_party/WebKit/public/web/WebView.h"
23 using blink::WebFrame;
24 using blink::WebString;
25 using blink::WebTextCheckingCompletion;
26 using blink::WebTextCheckingResult;
27 using blink::WebTextDecorationType;
28 using blink::WebVector;
30 static_assert(int(blink::WebTextDecorationTypeSpelling) ==
31 int(SpellCheckResult::SPELLING), "mismatching enums");
32 static_assert(int(blink::WebTextDecorationTypeGrammar) ==
33 int(SpellCheckResult::GRAMMAR), "mismatching enums");
34 static_assert(int(blink::WebTextDecorationTypeInvisibleSpellcheck) ==
35 int(SpellCheckResult::INVISIBLE), "mismatching enums");
37 SpellCheckProvider::SpellCheckProvider(
38 content::RenderView* render_view,
39 SpellCheck* spellcheck)
40 : content::RenderViewObserver(render_view),
41 content::RenderViewObserverTracker<SpellCheckProvider>(render_view),
42 spelling_panel_visible_(false),
43 spellcheck_(spellcheck) {
44 DCHECK(spellcheck_);
45 if (render_view) { // NULL in unit tests.
46 render_view->GetWebView()->setSpellCheckClient(this);
47 EnableSpellcheck(spellcheck_->is_spellcheck_enabled());
51 SpellCheckProvider::~SpellCheckProvider() {
54 void SpellCheckProvider::RequestTextChecking(
55 const base::string16& text,
56 WebTextCheckingCompletion* completion,
57 const std::vector<SpellCheckMarker>& markers) {
58 // Ignore invalid requests.
59 if (text.empty() || !HasWordCharacters(text, 0)) {
60 completion->didCancelCheckingText();
61 return;
64 // Try to satisfy check from cache.
65 if (SatisfyRequestFromCache(text, completion))
66 return;
68 // Send this text to a browser. A browser checks the user profile and send
69 // this text to the Spelling service only if a user enables this feature.
70 last_request_.clear();
71 last_results_.assign(blink::WebVector<blink::WebTextCheckingResult>());
73 #if defined(OS_MACOSX)
74 // Text check (unified request for grammar and spell check) is only
75 // available for browser process, so we ask the system spellchecker
76 // over IPC or return an empty result if the checker is not
77 // available.
78 Send(new SpellCheckHostMsg_RequestTextCheck(
79 routing_id(),
80 text_check_completions_.Add(completion),
81 text,
82 markers));
83 #else
84 Send(new SpellCheckHostMsg_CallSpellingService(
85 routing_id(),
86 text_check_completions_.Add(completion),
87 base::string16(text),
88 markers));
89 #endif // !OS_MACOSX
92 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) {
93 bool handled = true;
94 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message)
95 #if !defined(OS_MACOSX)
96 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService,
97 OnRespondSpellingService)
98 #endif
99 #if defined(OS_MACOSX)
100 IPC_MESSAGE_HANDLER(SpellCheckMsg_AdvanceToNextMisspelling,
101 OnAdvanceToNextMisspelling)
102 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondTextCheck, OnRespondTextCheck)
103 IPC_MESSAGE_HANDLER(SpellCheckMsg_ToggleSpellPanel, OnToggleSpellPanel)
104 #endif
105 IPC_MESSAGE_UNHANDLED(handled = false)
106 IPC_END_MESSAGE_MAP()
107 return handled;
110 void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
111 #if defined(OS_MACOSX)
112 bool enabled = false;
113 blink::WebElement element = render_view()->GetFocusedElement();
114 if (!element.isNull())
115 enabled = render_view()->IsEditableNode(element);
117 bool checked = false;
118 if (enabled && render_view()->GetWebView()) {
119 WebFrame* frame = render_view()->GetWebView()->focusedFrame();
120 if (frame->isContinuousSpellCheckingEnabled())
121 checked = true;
124 Send(new SpellCheckHostMsg_ToggleSpellCheck(routing_id(), enabled, checked));
125 #endif // OS_MACOSX
128 void SpellCheckProvider::spellCheck(
129 const WebString& text,
130 int& offset,
131 int& length,
132 WebVector<WebString>* optional_suggestions) {
133 base::string16 word(text);
134 std::vector<base::string16> suggestions;
135 spellcheck_->SpellCheckWord(
136 word.c_str(), word.size(), routing_id(),
137 &offset, &length, optional_suggestions ? & suggestions : NULL);
138 if (optional_suggestions) {
139 *optional_suggestions = suggestions;
140 UMA_HISTOGRAM_COUNTS("SpellCheck.api.check.suggestions", word.size());
141 } else {
142 UMA_HISTOGRAM_COUNTS("SpellCheck.api.check", word.size());
143 // If optional_suggestions is not requested, the API is called
144 // for marking. So we use this for counting markable words.
145 Send(new SpellCheckHostMsg_NotifyChecked(routing_id(), word, 0 < length));
149 void SpellCheckProvider::checkTextOfParagraph(
150 const blink::WebString& text,
151 blink::WebTextCheckingTypeMask mask,
152 blink::WebVector<blink::WebTextCheckingResult>* results) {
153 if (!results)
154 return;
156 if (!(mask & blink::WebTextCheckingTypeSpelling))
157 return;
159 // TODO(groby): As far as I can tell, this method is never invoked.
160 // UMA results seem to support that. Investigate, clean up if true.
161 NOTREACHED();
162 spellcheck_->SpellCheckParagraph(text, results);
163 UMA_HISTOGRAM_COUNTS("SpellCheck.api.paragraph", text.length());
166 void SpellCheckProvider::requestCheckingOfText(
167 const WebString& text,
168 const WebVector<uint32>& markers,
169 const WebVector<unsigned>& marker_offsets,
170 WebTextCheckingCompletion* completion) {
171 std::vector<SpellCheckMarker> spellcheck_markers;
172 for (size_t i = 0; i < markers.size(); ++i) {
173 spellcheck_markers.push_back(
174 SpellCheckMarker(markers[i], marker_offsets[i]));
176 RequestTextChecking(text, completion, spellcheck_markers);
177 UMA_HISTOGRAM_COUNTS("SpellCheck.api.async", text.length());
180 WebString SpellCheckProvider::autoCorrectWord(const WebString& word) {
181 const base::CommandLine& command_line =
182 *base::CommandLine::ForCurrentProcess();
183 if (command_line.HasSwitch(switches::kEnableSpellingAutoCorrect)) {
184 UMA_HISTOGRAM_COUNTS("SpellCheck.api.autocorrect", word.length());
185 return spellcheck_->GetAutoCorrectionWord(word, routing_id());
187 return base::string16();
190 void SpellCheckProvider::showSpellingUI(bool show) {
191 #if defined(OS_MACOSX)
192 UMA_HISTOGRAM_BOOLEAN("SpellCheck.api.showUI", show);
193 Send(new SpellCheckHostMsg_ShowSpellingPanel(routing_id(), show));
194 #endif
197 bool SpellCheckProvider::isShowingSpellingUI() {
198 return spelling_panel_visible_;
201 void SpellCheckProvider::updateSpellingUIWithMisspelledWord(
202 const WebString& word) {
203 #if defined(OS_MACOSX)
204 Send(new SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord(routing_id(),
205 word));
206 #endif
209 #if !defined(OS_MACOSX)
210 void SpellCheckProvider::OnRespondSpellingService(
211 int identifier,
212 bool succeeded,
213 const base::string16& line,
214 const std::vector<SpellCheckResult>& results) {
215 WebTextCheckingCompletion* completion =
216 text_check_completions_.Lookup(identifier);
217 if (!completion)
218 return;
219 text_check_completions_.Remove(identifier);
221 // If |succeeded| is false, we use local spellcheck as a fallback.
222 if (!succeeded) {
223 spellcheck_->RequestTextChecking(line, completion);
224 return;
227 // Double-check the returned spellchecking results with our spellchecker to
228 // visualize the differences between ours and the on-line spellchecker.
229 blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
230 spellcheck_->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER,
232 line,
233 results,
234 &textcheck_results);
235 completion->didFinishCheckingText(textcheck_results);
237 // Cache the request and the converted results.
238 last_request_ = line;
239 last_results_.swap(textcheck_results);
241 #endif
243 bool SpellCheckProvider::HasWordCharacters(
244 const base::string16& text,
245 int index) const {
246 const base::char16* data = text.data();
247 int length = text.length();
248 while (index < length) {
249 uint32 code = 0;
250 U16_NEXT(data, index, length, code);
251 UErrorCode error = U_ZERO_ERROR;
252 if (uscript_getScript(code, &error) != USCRIPT_COMMON)
253 return true;
255 return false;
258 #if defined(OS_MACOSX)
259 void SpellCheckProvider::OnAdvanceToNextMisspelling() {
260 if (!render_view()->GetWebView())
261 return;
262 render_view()->GetWebView()->focusedFrame()->executeCommand(
263 WebString::fromUTF8("AdvanceToNextMisspelling"));
266 void SpellCheckProvider::OnRespondTextCheck(
267 int identifier,
268 const std::vector<SpellCheckResult>& results) {
269 // TODO(groby): Unify with SpellCheckProvider::OnRespondSpellingService
270 DCHECK(spellcheck_);
271 WebTextCheckingCompletion* completion =
272 text_check_completions_.Lookup(identifier);
273 if (!completion)
274 return;
275 text_check_completions_.Remove(identifier);
276 blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
277 spellcheck_->CreateTextCheckingResults(SpellCheck::DO_NOT_MODIFY,
279 base::string16(),
280 results,
281 &textcheck_results);
282 completion->didFinishCheckingText(textcheck_results);
284 // TODO(groby): Add request caching once OSX reports back original request.
285 // (cf. SpellCheckProvider::OnRespondSpellingService)
286 // Cache the request and the converted results.
289 void SpellCheckProvider::OnToggleSpellPanel(bool is_currently_visible) {
290 if (!render_view()->GetWebView())
291 return;
292 // We need to tell the webView whether the spelling panel is visible or not so
293 // that it won't need to make ipc calls later.
294 spelling_panel_visible_ = is_currently_visible;
295 render_view()->GetWebView()->focusedFrame()->executeCommand(
296 WebString::fromUTF8("ToggleSpellPanel"));
298 #endif
300 void SpellCheckProvider::EnableSpellcheck(bool enable) {
301 if (!render_view()->GetWebView())
302 return;
304 WebFrame* frame = render_view()->GetWebView()->focusedFrame();
305 frame->enableContinuousSpellChecking(enable);
306 if (!enable)
307 frame->removeSpellingMarkers();
310 bool SpellCheckProvider::SatisfyRequestFromCache(
311 const base::string16& text,
312 WebTextCheckingCompletion* completion) {
313 size_t last_length = last_request_.length();
315 // Send back the |last_results_| if the |last_request_| is a substring of
316 // |text| and |text| does not have more words to check. Provider cannot cancel
317 // the spellcheck request here, because WebKit might have discarded the
318 // previous spellcheck results and erased the spelling markers in response to
319 // the user editing the text.
320 base::string16 request(text);
321 size_t text_length = request.length();
322 if (text_length >= last_length &&
323 !request.compare(0, last_length, last_request_)) {
324 if (text_length == last_length || !HasWordCharacters(text, last_length)) {
325 completion->didFinishCheckingText(last_results_);
326 return true;
328 int code = 0;
329 int length = static_cast<int>(text_length);
330 U16_PREV(text.data(), 0, length, code);
331 UErrorCode error = U_ZERO_ERROR;
332 if (uscript_getScript(code, &error) != USCRIPT_COMMON) {
333 completion->didCancelCheckingText();
334 return true;
337 // Create a subset of the cached results and return it if the given text is a
338 // substring of the cached text.
339 if (text_length < last_length &&
340 !last_request_.compare(0, text_length, request)) {
341 size_t result_size = 0;
342 for (size_t i = 0; i < last_results_.size(); ++i) {
343 size_t start = last_results_[i].location;
344 size_t end = start + last_results_[i].length;
345 if (start <= text_length && end <= text_length)
346 ++result_size;
348 if (result_size > 0) {
349 blink::WebVector<blink::WebTextCheckingResult> results(result_size);
350 for (size_t i = 0; i < result_size; ++i) {
351 results[i].decoration = last_results_[i].decoration;
352 results[i].location = last_results_[i].location;
353 results[i].length = last_results_[i].length;
354 results[i].replacement = last_results_[i].replacement;
356 completion->didFinishCheckingText(results);
357 return true;
361 return false;