Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: chrome/renderer/spellchecker/spellcheck_provider.cc

Issue 2159283003: [WIP][DO NOT LAND] Componentize spellcheck Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/renderer/spellchecker/spellcheck_provider.h"
6
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 "chrome/renderer/spellchecker/spellcheck_language.h"
15 #include "content/public/renderer/render_view.h"
16 #include "third_party/WebKit/public/platform/WebVector.h"
17 #include "third_party/WebKit/public/web/WebDocument.h"
18 #include "third_party/WebKit/public/web/WebElement.h"
19 #include "third_party/WebKit/public/web/WebLocalFrame.h"
20 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
21 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
22 #include "third_party/WebKit/public/web/WebTextDecorationType.h"
23 #include "third_party/WebKit/public/web/WebView.h"
24
25 using blink::WebElement;
26 using blink::WebLocalFrame;
27 using blink::WebString;
28 using blink::WebTextCheckingCompletion;
29 using blink::WebTextCheckingResult;
30 using blink::WebTextDecorationType;
31 using blink::WebVector;
32
33 static_assert(int(blink::WebTextDecorationTypeSpelling) ==
34 int(SpellCheckResult::SPELLING), "mismatching enums");
35 static_assert(int(blink::WebTextDecorationTypeGrammar) ==
36 int(SpellCheckResult::GRAMMAR), "mismatching enums");
37 static_assert(int(blink::WebTextDecorationTypeInvisibleSpellcheck) ==
38 int(SpellCheckResult::INVISIBLE), "mismatching enums");
39
40 SpellCheckProvider::SpellCheckProvider(
41 content::RenderView* render_view,
42 SpellCheck* spellcheck)
43 : content::RenderViewObserver(render_view),
44 content::RenderViewObserverTracker<SpellCheckProvider>(render_view),
45 spelling_panel_visible_(false),
46 spellcheck_(spellcheck) {
47 DCHECK(spellcheck_);
48 if (render_view) { // NULL in unit tests.
49 render_view->GetWebView()->setSpellCheckClient(this);
50 EnableSpellcheck(spellcheck_->IsSpellcheckEnabled());
51 }
52 }
53
54 SpellCheckProvider::~SpellCheckProvider() {
55 }
56
57 void SpellCheckProvider::RequestTextChecking(
58 const base::string16& text,
59 WebTextCheckingCompletion* completion,
60 const std::vector<SpellCheckMarker>& markers) {
61 // Ignore invalid requests.
62 if (text.empty() || !HasWordCharacters(text, 0)) {
63 completion->didCancelCheckingText();
64 return;
65 }
66
67 // Try to satisfy check from cache.
68 if (SatisfyRequestFromCache(text, completion))
69 return;
70
71 // Send this text to a browser. A browser checks the user profile and send
72 // this text to the Spelling service only if a user enables this feature.
73 last_request_.clear();
74 last_results_.assign(blink::WebVector<blink::WebTextCheckingResult>());
75
76 #if defined(USE_BROWSER_SPELLCHECKER)
77 // Text check (unified request for grammar and spell check) is only
78 // available for browser process, so we ask the system spellchecker
79 // over IPC or return an empty result if the checker is not
80 // available.
81 Send(new SpellCheckHostMsg_RequestTextCheck(
82 routing_id(),
83 text_check_completions_.Add(completion),
84 text,
85 markers));
86 #else
87 Send(new SpellCheckHostMsg_CallSpellingService(
88 routing_id(),
89 text_check_completions_.Add(completion),
90 base::string16(text),
91 markers));
92 #endif // !USE_BROWSER_SPELLCHECKER
93 }
94
95 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) {
96 bool handled = true;
97 IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message)
98 #if !defined(USE_BROWSER_SPELLCHECKER)
99 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService,
100 OnRespondSpellingService)
101 #endif
102 #if defined(USE_BROWSER_SPELLCHECKER)
103 IPC_MESSAGE_HANDLER(SpellCheckMsg_AdvanceToNextMisspelling,
104 OnAdvanceToNextMisspelling)
105 IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondTextCheck, OnRespondTextCheck)
106 IPC_MESSAGE_HANDLER(SpellCheckMsg_ToggleSpellPanel, OnToggleSpellPanel)
107 #endif
108 IPC_MESSAGE_UNHANDLED(handled = false)
109 IPC_END_MESSAGE_MAP()
110 return handled;
111 }
112
113 void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
114 #if defined(USE_BROWSER_SPELLCHECKER)
115 WebLocalFrame* frame = render_view()->GetWebView()->focusedFrame();
116 WebElement element = frame->document().isNull() ? WebElement() :
117 frame->document().focusedElement();
118 bool enabled = !element.isNull() && element.isEditable();
119 bool checked = enabled && frame->isContinuousSpellCheckingEnabled();
120
121 Send(new SpellCheckHostMsg_ToggleSpellCheck(routing_id(), enabled, checked));
122 #endif // USE_BROWSER_SPELLCHECKER
123 }
124
125 void SpellCheckProvider::spellCheck(
126 const WebString& text,
127 int& offset,
128 int& length,
129 WebVector<WebString>* optional_suggestions) {
130 base::string16 word(text);
131 std::vector<base::string16> suggestions;
132 const int kWordStart = 0;
133 spellcheck_->SpellCheckWord(
134 word.c_str(), kWordStart, word.size(), routing_id(),
135 &offset, &length, optional_suggestions ? & suggestions : NULL);
136 if (optional_suggestions) {
137 *optional_suggestions = suggestions;
138 UMA_HISTOGRAM_COUNTS("SpellCheck.api.check.suggestions", word.size());
139 } else {
140 UMA_HISTOGRAM_COUNTS("SpellCheck.api.check", word.size());
141 // If optional_suggestions is not requested, the API is called
142 // for marking. So we use this for counting markable words.
143 Send(new SpellCheckHostMsg_NotifyChecked(routing_id(), word, 0 < length));
144 }
145 }
146
147 void SpellCheckProvider::checkTextOfParagraph(
148 const blink::WebString& text,
149 blink::WebTextCheckingTypeMask mask,
150 blink::WebVector<blink::WebTextCheckingResult>* results) {
151 if (!results)
152 return;
153
154 if (!(mask & blink::WebTextCheckingTypeSpelling))
155 return;
156
157 // TODO(groby): As far as I can tell, this method is never invoked.
158 // UMA results seem to support that. Investigate, clean up if true.
159 NOTREACHED();
160 spellcheck_->SpellCheckParagraph(text, results);
161 UMA_HISTOGRAM_COUNTS("SpellCheck.api.paragraph", text.length());
162 }
163
164 void SpellCheckProvider::requestCheckingOfText(
165 const WebString& text,
166 const WebVector<uint32_t>& markers,
167 const WebVector<unsigned>& marker_offsets,
168 WebTextCheckingCompletion* completion) {
169 std::vector<SpellCheckMarker> spellcheck_markers;
170 for (size_t i = 0; i < markers.size(); ++i) {
171 spellcheck_markers.push_back(
172 SpellCheckMarker(markers[i], marker_offsets[i]));
173 }
174 RequestTextChecking(text, completion, spellcheck_markers);
175 UMA_HISTOGRAM_COUNTS("SpellCheck.api.async", text.length());
176 }
177
178 void SpellCheckProvider::showSpellingUI(bool show) {
179 #if defined(USE_BROWSER_SPELLCHECKER)
180 UMA_HISTOGRAM_BOOLEAN("SpellCheck.api.showUI", show);
181 Send(new SpellCheckHostMsg_ShowSpellingPanel(routing_id(), show));
182 #endif
183 }
184
185 bool SpellCheckProvider::isShowingSpellingUI() {
186 return spelling_panel_visible_;
187 }
188
189 void SpellCheckProvider::updateSpellingUIWithMisspelledWord(
190 const WebString& word) {
191 #if defined(USE_BROWSER_SPELLCHECKER)
192 Send(new SpellCheckHostMsg_UpdateSpellingPanelWithMisspelledWord(routing_id(),
193 word));
194 #endif
195 }
196
197 #if !defined(USE_BROWSER_SPELLCHECKER)
198 void SpellCheckProvider::OnRespondSpellingService(
199 int identifier,
200 bool succeeded,
201 const base::string16& line,
202 const std::vector<SpellCheckResult>& results) {
203 WebTextCheckingCompletion* completion =
204 text_check_completions_.Lookup(identifier);
205 if (!completion)
206 return;
207 text_check_completions_.Remove(identifier);
208
209 // If |succeeded| is false, we use local spellcheck as a fallback.
210 if (!succeeded) {
211 spellcheck_->RequestTextChecking(line, completion);
212 return;
213 }
214
215 // Double-check the returned spellchecking results with our spellchecker to
216 // visualize the differences between ours and the on-line spellchecker.
217 blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
218 spellcheck_->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER,
219 0,
220 line,
221 results,
222 &textcheck_results);
223 completion->didFinishCheckingText(textcheck_results);
224
225 // Cache the request and the converted results.
226 last_request_ = line;
227 last_results_.swap(textcheck_results);
228 }
229 #endif
230
231 bool SpellCheckProvider::HasWordCharacters(
232 const base::string16& text,
233 int index) const {
234 const base::char16* data = text.data();
235 int length = text.length();
236 while (index < length) {
237 uint32_t code = 0;
238 U16_NEXT(data, index, length, code);
239 UErrorCode error = U_ZERO_ERROR;
240 if (uscript_getScript(code, &error) != USCRIPT_COMMON)
241 return true;
242 }
243 return false;
244 }
245
246 #if defined(USE_BROWSER_SPELLCHECKER)
247 void SpellCheckProvider::OnAdvanceToNextMisspelling() {
248 if (!render_view()->GetWebView())
249 return;
250 render_view()->GetWebView()->focusedFrame()->executeCommand(
251 WebString::fromUTF8("AdvanceToNextMisspelling"));
252 }
253
254 void SpellCheckProvider::OnRespondTextCheck(
255 int identifier,
256 const base::string16& line,
257 const std::vector<SpellCheckResult>& results) {
258 // TODO(groby): Unify with SpellCheckProvider::OnRespondSpellingService
259 DCHECK(spellcheck_);
260 WebTextCheckingCompletion* completion =
261 text_check_completions_.Lookup(identifier);
262 if (!completion)
263 return;
264 text_check_completions_.Remove(identifier);
265 blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
266 spellcheck_->CreateTextCheckingResults(SpellCheck::DO_NOT_MODIFY,
267 0,
268 line,
269 results,
270 &textcheck_results);
271 completion->didFinishCheckingText(textcheck_results);
272
273 // TODO(groby): Add request caching once OSX reports back original request.
274 // (cf. SpellCheckProvider::OnRespondSpellingService)
275 // Cache the request and the converted results.
276 }
277
278 void SpellCheckProvider::OnToggleSpellPanel(bool is_currently_visible) {
279 if (!render_view()->GetWebView())
280 return;
281 // We need to tell the webView whether the spelling panel is visible or not so
282 // that it won't need to make ipc calls later.
283 spelling_panel_visible_ = is_currently_visible;
284 render_view()->GetWebView()->focusedFrame()->executeCommand(
285 WebString::fromUTF8("ToggleSpellPanel"));
286 }
287 #endif
288
289 void SpellCheckProvider::EnableSpellcheck(bool enable) {
290 if (!render_view()->GetWebView())
291 return;
292
293 WebLocalFrame* frame = render_view()->GetWebView()->focusedFrame();
294 // TODO(yabinh): The null check should be unnecessary.
295 // See crbug.com/625068
296 if (!frame)
297 return;
298
299 frame->enableContinuousSpellChecking(enable);
300 if (!enable)
301 frame->removeSpellingMarkers();
302 }
303
304 bool SpellCheckProvider::SatisfyRequestFromCache(
305 const base::string16& text,
306 WebTextCheckingCompletion* completion) {
307 size_t last_length = last_request_.length();
308
309 // Send back the |last_results_| if the |last_request_| is a substring of
310 // |text| and |text| does not have more words to check. Provider cannot cancel
311 // the spellcheck request here, because WebKit might have discarded the
312 // previous spellcheck results and erased the spelling markers in response to
313 // the user editing the text.
314 base::string16 request(text);
315 size_t text_length = request.length();
316 if (text_length >= last_length &&
317 !request.compare(0, last_length, last_request_)) {
318 if (text_length == last_length || !HasWordCharacters(text, last_length)) {
319 completion->didFinishCheckingText(last_results_);
320 return true;
321 }
322 int code = 0;
323 int length = static_cast<int>(text_length);
324 U16_PREV(text.data(), 0, length, code);
325 UErrorCode error = U_ZERO_ERROR;
326 if (uscript_getScript(code, &error) != USCRIPT_COMMON) {
327 completion->didCancelCheckingText();
328 return true;
329 }
330 }
331 // Create a subset of the cached results and return it if the given text is a
332 // substring of the cached text.
333 if (text_length < last_length &&
334 !last_request_.compare(0, text_length, request)) {
335 size_t result_size = 0;
336 for (size_t i = 0; i < last_results_.size(); ++i) {
337 size_t start = last_results_[i].location;
338 size_t end = start + last_results_[i].length;
339 if (start <= text_length && end <= text_length)
340 ++result_size;
341 }
342 if (result_size > 0) {
343 blink::WebVector<blink::WebTextCheckingResult> results(result_size);
344 for (size_t i = 0; i < result_size; ++i) {
345 results[i].decoration = last_results_[i].decoration;
346 results[i].location = last_results_[i].location;
347 results[i].length = last_results_[i].length;
348 results[i].replacement = last_results_[i].replacement;
349 }
350 completion->didFinishCheckingText(results);
351 return true;
352 }
353 }
354
355 return false;
356 }
357
358 void SpellCheckProvider::OnDestruct() {
359 delete this;
360 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698