OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. |
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 element = textControl->innerEditorElement(); | 201 element = textControl->innerEditorElement(); |
202 if (!element) | 202 if (!element) |
203 return; | 203 return; |
204 isTextField = isHTMLInputElement(*textControl) && | 204 isTextField = isHTMLInputElement(*textControl) && |
205 toHTMLInputElement(*textControl).isTextField(); | 205 toHTMLInputElement(*textControl).isTextField(); |
206 } | 206 } |
207 | 207 |
208 if (isTextField || !parent->isAlreadySpellChecked()) { | 208 if (isTextField || !parent->isAlreadySpellChecked()) { |
209 if (EditingStrategy::editingIgnoresContent(element)) | 209 if (EditingStrategy::editingIgnoresContent(element)) |
210 return; | 210 return; |
211 // We always recheck textfields because markers are removed from them on blu
r. | 211 // We always recheck textfields because markers are removed from them on |
| 212 // blur. |
212 VisibleSelection selection = | 213 VisibleSelection selection = |
213 VisibleSelection::selectionFromContentsOfNode(element); | 214 VisibleSelection::selectionFromContentsOfNode(element); |
214 markMisspellingsAndBadGrammar(selection); | 215 markMisspellingsAndBadGrammar(selection); |
215 if (!isTextField) | 216 if (!isTextField) |
216 parent->setAlreadySpellChecked(true); | 217 parent->setAlreadySpellChecked(true); |
217 } | 218 } |
218 } | 219 } |
219 | 220 |
220 void SpellChecker::ignoreSpelling() { | 221 void SpellChecker::ignoreSpelling() { |
221 removeMarkers(frame().selection().selection(), DocumentMarker::Spelling); | 222 removeMarkers(frame().selection().selection(), DocumentMarker::Spelling); |
222 } | 223 } |
223 | 224 |
224 void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection) { | 225 void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection) { |
225 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 226 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
226 frame().document()->lifecycle()); | 227 frame().document()->lifecycle()); |
227 | 228 |
228 // The basic approach is to search in two phases - from the selection end to t
he end of the doc, and | 229 // The basic approach is to search in two phases - from the selection end to |
229 // then we wrap and search from the doc start to (approximately) where we star
ted. | 230 // the end of the doc, and then we wrap and search from the doc start to |
| 231 // (approximately) where we started. |
230 | 232 |
231 // Start at the end of the selection, search to edge of document. Starting at
the selection end makes | 233 // Start at the end of the selection, search to edge of document. Starting at |
232 // repeated "check spelling" commands work. | 234 // the selection end makes repeated "check spelling" commands work. |
233 VisibleSelection selection(frame().selection().selection()); | 235 VisibleSelection selection(frame().selection().selection()); |
234 Position spellingSearchStart, spellingSearchEnd; | 236 Position spellingSearchStart, spellingSearchEnd; |
235 Range::selectNodeContents(frame().document(), spellingSearchStart, | 237 Range::selectNodeContents(frame().document(), spellingSearchStart, |
236 spellingSearchEnd); | 238 spellingSearchEnd); |
237 | 239 |
238 bool startedWithSelection = false; | 240 bool startedWithSelection = false; |
239 if (selection.start().anchorNode()) { | 241 if (selection.start().anchorNode()) { |
240 startedWithSelection = true; | 242 startedWithSelection = true; |
241 if (startBeforeSelection) { | 243 if (startBeforeSelection) { |
242 VisiblePosition start(selection.visibleStart()); | 244 VisiblePosition start(selection.visibleStart()); |
243 // We match AppKit's rule: Start 1 character before the selection. | 245 // We match AppKit's rule: Start 1 character before the selection. |
244 VisiblePosition oneBeforeStart = previousPositionOf(start); | 246 VisiblePosition oneBeforeStart = previousPositionOf(start); |
245 spellingSearchStart = | 247 spellingSearchStart = |
246 (oneBeforeStart.isNotNull() ? oneBeforeStart : start) | 248 (oneBeforeStart.isNotNull() ? oneBeforeStart : start) |
247 .toParentAnchoredPosition(); | 249 .toParentAnchoredPosition(); |
248 } else { | 250 } else { |
249 spellingSearchStart = selection.visibleEnd().toParentAnchoredPosition(); | 251 spellingSearchStart = selection.visibleEnd().toParentAnchoredPosition(); |
250 } | 252 } |
251 } | 253 } |
252 | 254 |
253 Position position = spellingSearchStart; | 255 Position position = spellingSearchStart; |
254 if (!isEditablePosition(position)) { | 256 if (!isEditablePosition(position)) { |
255 // This shouldn't happen in very often because the Spelling menu items aren'
t enabled unless the | 257 // This shouldn't happen in very often because the Spelling menu items |
256 // selection is editable. | 258 // aren't enabled unless the selection is editable. This can happen in Mail |
257 // This can happen in Mail for a mix of non-editable and editable content (l
ike Stationary), | 259 // for a mix of non-editable and editable content (like Stationary), when |
258 // when spell checking the whole document before sending the message. | 260 // spell checking the whole document before sending the message. In that |
259 // In that case the document might not be editable, but there are editable p
ockets that need to be spell checked. | 261 // case the document might not be editable, but there are editable pockets |
| 262 // that need to be spell checked. |
260 | 263 |
261 if (!frame().document()->documentElement()) | 264 if (!frame().document()->documentElement()) |
262 return; | 265 return; |
263 position = firstEditableVisiblePositionAfterPositionInRoot( | 266 position = firstEditableVisiblePositionAfterPositionInRoot( |
264 position, *frame().document()->documentElement()) | 267 position, *frame().document()->documentElement()) |
265 .deepEquivalent(); | 268 .deepEquivalent(); |
266 if (position.isNull()) | 269 if (position.isNull()) |
267 return; | 270 return; |
268 | 271 |
269 spellingSearchStart = position.parentAnchoredEquivalent(); | 272 spellingSearchStart = position.parentAnchoredEquivalent(); |
(...skipping 17 matching lines...) Expand all Loading... |
287 rootEditableElementOf(oneBeforeStart) == | 290 rootEditableElementOf(oneBeforeStart) == |
288 rootEditableElementOf(spellingSearchStart)) | 291 rootEditableElementOf(spellingSearchStart)) |
289 spellingSearchStart = | 292 spellingSearchStart = |
290 endOfWord(oneBeforeStart).toParentAnchoredPosition(); | 293 endOfWord(oneBeforeStart).toParentAnchoredPosition(); |
291 // else we were already at the start of the editable node | 294 // else we were already at the start of the editable node |
292 } | 295 } |
293 | 296 |
294 if (spellingSearchStart == spellingSearchEnd) | 297 if (spellingSearchStart == spellingSearchEnd) |
295 return; // nothing to search in | 298 return; // nothing to search in |
296 | 299 |
297 // We go to the end of our first range instead of the start of it, just to be
sure | 300 // We go to the end of our first range instead of the start of it, just to be |
298 // we don't get foiled by any word boundary problems at the start. It means we
might | 301 // sure we don't get foiled by any word boundary problems at the start. It |
299 // do a tiny bit more searching. | 302 // means we might do a tiny bit more searching. |
300 Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode(); | 303 Node* searchEndNodeAfterWrap = spellingSearchEnd.computeContainerNode(); |
301 int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode(); | 304 int searchEndOffsetAfterWrap = spellingSearchEnd.offsetInContainerNode(); |
302 | 305 |
303 std::pair<String, int> misspelledItem(String(), 0); | 306 std::pair<String, int> misspelledItem(String(), 0); |
304 String& misspelledWord = misspelledItem.first; | 307 String& misspelledWord = misspelledItem.first; |
305 int& misspellingOffset = misspelledItem.second; | 308 int& misspellingOffset = misspelledItem.second; |
306 misspelledItem = findFirstMisspelling(spellingSearchStart, spellingSearchEnd); | 309 misspelledItem = findFirstMisspelling(spellingSearchStart, spellingSearchEnd); |
307 | 310 |
308 // If we did not find a misspelled word, wrap and try again (but don't bother
if we started at the beginning of the | 311 // If we did not find a misspelled word, wrap and try again (but don't bother |
309 // block rather than at a selection). | 312 // if we started at the beginning of the block rather than at a selection). |
310 if (startedWithSelection && !misspelledWord) { | 313 if (startedWithSelection && !misspelledWord) { |
311 spellingSearchStart = Position::editingPositionOf(topNode, 0); | 314 spellingSearchStart = Position::editingPositionOf(topNode, 0); |
312 // going until the end of the very first chunk we tested is far enough | 315 // going until the end of the very first chunk we tested is far enough |
313 spellingSearchEnd = Position::editingPositionOf(searchEndNodeAfterWrap, | 316 spellingSearchEnd = Position::editingPositionOf(searchEndNodeAfterWrap, |
314 searchEndOffsetAfterWrap); | 317 searchEndOffsetAfterWrap); |
315 misspelledItem = | 318 misspelledItem = |
316 findFirstMisspelling(spellingSearchStart, spellingSearchEnd); | 319 findFirstMisspelling(spellingSearchStart, spellingSearchEnd); |
317 } | 320 } |
318 | 321 |
319 if (!misspelledWord.isEmpty()) { | 322 if (!misspelledWord.isEmpty()) { |
320 // We found a misspelling. Select the misspelling, update the spelling panel
, and store | 323 // We found a misspelling. Select the misspelling, update the spelling |
321 // a marker so we draw the red squiggle later. | 324 // panel, and store a marker so we draw the red squiggle later. |
322 | 325 |
323 const EphemeralRange misspellingRange = calculateCharacterSubrange( | 326 const EphemeralRange misspellingRange = calculateCharacterSubrange( |
324 EphemeralRange(spellingSearchStart, spellingSearchEnd), | 327 EphemeralRange(spellingSearchStart, spellingSearchEnd), |
325 misspellingOffset, misspelledWord.length()); | 328 misspellingOffset, misspelledWord.length()); |
326 frame().selection().setSelection(createVisibleSelection(misspellingRange)); | 329 frame().selection().setSelection(createVisibleSelection(misspellingRange)); |
327 frame().selection().revealSelection(); | 330 frame().selection().revealSelection(); |
328 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord); | 331 spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord); |
329 frame().document()->markers().addMarker(misspellingRange.startPosition(), | 332 frame().document()->markers().addMarker(misspellingRange.startPosition(), |
330 misspellingRange.endPosition(), | 333 misspellingRange.endPosition(), |
331 DocumentMarker::Spelling); | 334 DocumentMarker::Spelling); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 if (cmd.inputType() != InputEvent::InputType::InsertFromPaste) | 407 if (cmd.inputType() != InputEvent::InputType::InsertFromPaste) |
405 return; | 408 return; |
406 | 409 |
407 markMisspellingsAfterReplaceSelectionCommand(toReplaceSelectionCommand(cmd)); | 410 markMisspellingsAfterReplaceSelectionCommand(toReplaceSelectionCommand(cmd)); |
408 } | 411 } |
409 | 412 |
410 void SpellChecker::markMisspellingsAfterTypingCommand( | 413 void SpellChecker::markMisspellingsAfterTypingCommand( |
411 const TypingCommand& cmd) { | 414 const TypingCommand& cmd) { |
412 m_spellCheckRequester->cancelCheck(); | 415 m_spellCheckRequester->cancelCheck(); |
413 | 416 |
414 // Take a look at the selection that results after typing and determine whethe
r we need to spellcheck. | 417 // Take a look at the selection that results after typing and determine |
415 // Since the word containing the current selection is never marked, this does
a check to | 418 // whether we need to spellcheck. Since the word containing the current |
416 // see if typing made a new word that is not in the current selection. Basical
ly, you | 419 // selection is never marked, this does a check to see if typing made a new |
417 // get this by being at the end of a word and typing a space. | 420 // word that is not in the current selection. Basically, you get this by |
| 421 // being at the end of a word and typing a space. |
418 VisiblePosition start = createVisiblePosition( | 422 VisiblePosition start = createVisiblePosition( |
419 cmd.endingSelection().start(), cmd.endingSelection().affinity()); | 423 cmd.endingSelection().start(), cmd.endingSelection().affinity()); |
420 VisiblePosition previous = previousPositionOf(start); | 424 VisiblePosition previous = previousPositionOf(start); |
421 | 425 |
422 VisiblePosition wordStartOfPrevious = | 426 VisiblePosition wordStartOfPrevious = |
423 startOfWord(previous, LeftWordIfOnBoundary); | 427 startOfWord(previous, LeftWordIfOnBoundary); |
424 | 428 |
425 if (cmd.commandTypeOfOpenCommand() == | 429 if (cmd.commandTypeOfOpenCommand() == |
426 TypingCommand::InsertParagraphSeparator) { | 430 TypingCommand::InsertParagraphSeparator) { |
427 VisiblePosition nextWord = nextWordPosition(start); | 431 VisiblePosition nextWord = nextWordPosition(start); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
486 TextCheckingParagraph textToCheck(insertedRange, paragraphRange); | 490 TextCheckingParagraph textToCheck(insertedRange, paragraphRange); |
487 chunkAndMarkAllMisspellingsAndBadGrammar(textToCheck); | 491 chunkAndMarkAllMisspellingsAndBadGrammar(textToCheck); |
488 } | 492 } |
489 | 493 |
490 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar( | 494 void SpellChecker::chunkAndMarkAllMisspellingsAndBadGrammar( |
491 const TextCheckingParagraph& fullParagraphToCheck) { | 495 const TextCheckingParagraph& fullParagraphToCheck) { |
492 if (fullParagraphToCheck.isEmpty()) | 496 if (fullParagraphToCheck.isEmpty()) |
493 return; | 497 return; |
494 const EphemeralRange& paragraphRange = fullParagraphToCheck.paragraphRange(); | 498 const EphemeralRange& paragraphRange = fullParagraphToCheck.paragraphRange(); |
495 | 499 |
496 // Since the text may be quite big chunk it up and adjust to the sentence boun
dary. | 500 // Since the text may be quite big chunk it up and adjust to the sentence |
| 501 // boundary. |
497 const int kChunkSize = 16 * 1024; | 502 const int kChunkSize = 16 * 1024; |
498 | 503 |
499 // Check the full paragraph instead if the paragraph is short, which saves | 504 // Check the full paragraph instead if the paragraph is short, which saves |
500 // the cost on sentence boundary finding. | 505 // the cost on sentence boundary finding. |
501 if (fullParagraphToCheck.rangeLength() <= kChunkSize) { | 506 if (fullParagraphToCheck.rangeLength() <= kChunkSize) { |
502 SpellCheckRequest* request = | 507 SpellCheckRequest* request = |
503 SpellCheckRequest::create(TextCheckingProcessBatch, paragraphRange, 0); | 508 SpellCheckRequest::create(TextCheckingProcessBatch, paragraphRange, 0); |
504 if (request) | 509 if (request) |
505 m_spellCheckRequester->requestCheckingFor(request); | 510 m_spellCheckRequester->requestCheckingFor(request); |
506 return; | 511 return; |
507 } | 512 } |
508 | 513 |
509 CharacterIterator checkRangeIterator( | 514 CharacterIterator checkRangeIterator( |
510 fullParagraphToCheck.checkingRange(), | 515 fullParagraphToCheck.checkingRange(), |
511 TextIteratorEmitsObjectReplacementCharacter); | 516 TextIteratorEmitsObjectReplacementCharacter); |
512 for (int requestNum = 0; !checkRangeIterator.atEnd(); requestNum++) { | 517 for (int requestNum = 0; !checkRangeIterator.atEnd(); requestNum++) { |
513 EphemeralRange chunkRange = | 518 EphemeralRange chunkRange = |
514 checkRangeIterator.calculateCharacterSubrange(0, kChunkSize); | 519 checkRangeIterator.calculateCharacterSubrange(0, kChunkSize); |
515 EphemeralRange checkRange = requestNum | 520 EphemeralRange checkRange = requestNum |
516 ? expandEndToSentenceBoundary(chunkRange) | 521 ? expandEndToSentenceBoundary(chunkRange) |
517 : expandRangeToSentenceBoundary(chunkRange); | 522 : expandRangeToSentenceBoundary(chunkRange); |
518 | 523 |
519 SpellCheckRequest* request = SpellCheckRequest::create( | 524 SpellCheckRequest* request = SpellCheckRequest::create( |
520 TextCheckingProcessBatch, checkRange, requestNum); | 525 TextCheckingProcessBatch, checkRange, requestNum); |
521 if (request) | 526 if (request) |
522 m_spellCheckRequester->requestCheckingFor(request); | 527 m_spellCheckRequester->requestCheckingFor(request); |
523 | 528 |
524 if (!checkRangeIterator.atEnd()) { | 529 if (!checkRangeIterator.atEnd()) { |
525 checkRangeIterator.advance(1); | 530 checkRangeIterator.advance(1); |
526 // The layout should be already update due to the initialization of checkR
angeIterator, | 531 // The layout should be already update due to the initialization of |
527 // so comparePositions can be directly called. | 532 // checkRangeIterator, so comparePositions can be directly called. |
528 if (comparePositions(chunkRange.endPosition(), checkRange.endPosition()) < | 533 if (comparePositions(chunkRange.endPosition(), checkRange.endPosition()) < |
529 0) | 534 0) |
530 checkRangeIterator.advance(TextIterator::rangeLength( | 535 checkRangeIterator.advance(TextIterator::rangeLength( |
531 chunkRange.endPosition(), checkRange.endPosition())); | 536 chunkRange.endPosition(), checkRange.endPosition())); |
532 } | 537 } |
533 } | 538 } |
534 } | 539 } |
535 | 540 |
536 void SpellChecker::markAndReplaceFor( | 541 void SpellChecker::markAndReplaceFor( |
537 SpellCheckRequest* request, | 542 SpellCheckRequest* request, |
538 const Vector<TextCheckingResult>& results) { | 543 const Vector<TextCheckingResult>& results) { |
539 TRACE_EVENT0("blink", "SpellChecker::markAndReplaceFor"); | 544 TRACE_EVENT0("blink", "SpellChecker::markAndReplaceFor"); |
540 DCHECK(request); | 545 DCHECK(request); |
541 if (!frame().selection().isAvailable()) { | 546 if (!frame().selection().isAvailable()) { |
542 // "editing/spelling/spellcheck-async-remove-frame.html" reaches here. | 547 // "editing/spelling/spellcheck-async-remove-frame.html" reaches here. |
543 return; | 548 return; |
544 } | 549 } |
545 if (!request->isValid()) | 550 if (!request->isValid()) |
546 return; | 551 return; |
547 if (request->rootEditableElement()->document() != | 552 if (request->rootEditableElement()->document() != |
548 frame().selection().document()) { | 553 frame().selection().document()) { |
549 // we ignore |request| made for another document. | 554 // we ignore |request| made for another document. |
550 // "editing/spelling/spellcheck-sequencenum.html" and others reach here. | 555 // "editing/spelling/spellcheck-sequencenum.html" and others reach here. |
551 return; | 556 return; |
552 } | 557 } |
553 | 558 |
554 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets n
eeds to be audited. | 559 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
555 // see http://crbug.com/590369 for more details. | 560 // needs to be audited. See http://crbug.com/590369 for more details. |
556 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 561 frame().document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
557 | 562 |
558 TextCheckingParagraph paragraph(request->checkingRange(), | 563 TextCheckingParagraph paragraph(request->checkingRange(), |
559 request->checkingRange()); | 564 request->checkingRange()); |
560 | 565 |
561 // Expand the range to encompass entire paragraphs, since text checking needs
that much context. | 566 // Expand the range to encompass entire paragraphs, since text checking needs |
| 567 // that much context. |
562 int selectionOffset = 0; | 568 int selectionOffset = 0; |
563 int ambiguousBoundaryOffset = -1; | 569 int ambiguousBoundaryOffset = -1; |
564 bool selectionChanged = false; | 570 bool selectionChanged = false; |
565 bool restoreSelectionAfterChange = false; | 571 bool restoreSelectionAfterChange = false; |
566 bool adjustSelectionForParagraphBoundaries = false; | 572 bool adjustSelectionForParagraphBoundaries = false; |
567 | 573 |
568 { | 574 { |
569 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 575 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
570 frame().document()->lifecycle()); | 576 frame().document()->lifecycle()); |
571 | 577 |
(...skipping 18 matching lines...) Expand all Loading... |
590 const TextCheckingResult* result = &results[i]; | 596 const TextCheckingResult* result = &results[i]; |
591 int resultLocation = result->location + paragraph.checkingStart(); | 597 int resultLocation = result->location + paragraph.checkingStart(); |
592 int resultLength = result->length; | 598 int resultLength = result->length; |
593 bool resultEndsAtAmbiguousBoundary = | 599 bool resultEndsAtAmbiguousBoundary = |
594 ambiguousBoundaryOffset >= 0 && | 600 ambiguousBoundaryOffset >= 0 && |
595 resultLocation + resultLength == ambiguousBoundaryOffset; | 601 resultLocation + resultLength == ambiguousBoundaryOffset; |
596 | 602 |
597 // Only mark misspelling if: | 603 // Only mark misspelling if: |
598 // 1. Current text checking isn't done for autocorrection. | 604 // 1. Current text checking isn't done for autocorrection. |
599 // 2. Result falls within spellingRange. | 605 // 2. Result falls within spellingRange. |
600 // 3. The word in question doesn't end at an ambiguous boundary. For insta
nce, we would not mark | 606 // 3. The word in question doesn't end at an ambiguous boundary. For |
601 // "wouldn'" as misspelled right after apostrophe is typed. | 607 // instance, we would not mark "wouldn'" as misspelled right after |
| 608 // apostrophe is typed. |
602 if (result->decoration == TextDecorationTypeSpelling && | 609 if (result->decoration == TextDecorationTypeSpelling && |
603 resultLocation >= paragraph.checkingStart() && | 610 resultLocation >= paragraph.checkingStart() && |
604 resultLocation + resultLength <= spellingRangeEndOffset && | 611 resultLocation + resultLength <= spellingRangeEndOffset && |
605 !resultEndsAtAmbiguousBoundary) { | 612 !resultEndsAtAmbiguousBoundary) { |
606 DCHECK_GT(resultLength, 0); | 613 DCHECK_GT(resultLength, 0); |
607 DCHECK_GE(resultLocation, 0); | 614 DCHECK_GE(resultLocation, 0); |
608 const EphemeralRange misspellingRange = calculateCharacterSubrange( | 615 const EphemeralRange misspellingRange = calculateCharacterSubrange( |
609 paragraph.paragraphRange(), resultLocation, resultLength); | 616 paragraph.paragraphRange(), resultLocation, resultLength); |
610 frame().document()->markers().addMarker( | 617 frame().document()->markers().addMarker( |
611 misspellingRange.startPosition(), misspellingRange.endPosition(), | 618 misspellingRange.startPosition(), misspellingRange.endPosition(), |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
652 if (restoreSelectionAfterChange && selectionOffset >= 0 && | 659 if (restoreSelectionAfterChange && selectionOffset >= 0 && |
653 selectionOffset <= extendedParagraph.rangeLength()) { | 660 selectionOffset <= extendedParagraph.rangeLength()) { |
654 EphemeralRange selectionRange = | 661 EphemeralRange selectionRange = |
655 extendedParagraph.subrange(0, selectionOffset); | 662 extendedParagraph.subrange(0, selectionOffset); |
656 frame().selection().moveTo(selectionRange.endPosition(), | 663 frame().selection().moveTo(selectionRange.endPosition(), |
657 TextAffinity::Downstream); | 664 TextAffinity::Downstream); |
658 if (adjustSelectionForParagraphBoundaries) | 665 if (adjustSelectionForParagraphBoundaries) |
659 frame().selection().modify(FrameSelection::AlterationMove, | 666 frame().selection().modify(FrameSelection::AlterationMove, |
660 DirectionForward, CharacterGranularity); | 667 DirectionForward, CharacterGranularity); |
661 } else { | 668 } else { |
662 // If this fails for any reason, the fallback is to go one position beyond
the last replacement | 669 // If this fails for any reason, the fallback is to go one position beyond |
| 670 // the last replacement |
663 frame().selection().moveTo(frame().selection().selection().visibleEnd()); | 671 frame().selection().moveTo(frame().selection().selection().visibleEnd()); |
664 frame().selection().modify(FrameSelection::AlterationMove, | 672 frame().selection().modify(FrameSelection::AlterationMove, |
665 DirectionForward, CharacterGranularity); | 673 DirectionForward, CharacterGranularity); |
666 } | 674 } |
667 } | 675 } |
668 } | 676 } |
669 | 677 |
670 void SpellChecker::updateMarkersForWordsAffectedByEditing( | 678 void SpellChecker::updateMarkersForWordsAffectedByEditing( |
671 bool doNotRemoveIfSelectionAtWordBoundary) { | 679 bool doNotRemoveIfSelectionAtWordBoundary) { |
672 DCHECK(frame().selection().isAvailable()); | 680 DCHECK(frame().selection().isAvailable()); |
673 TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing"); | 681 TRACE_EVENT0("blink", "SpellChecker::updateMarkersForWordsAffectedByEditing"); |
674 if (!isSpellCheckingEnabledFor(frame().selection().selection())) | 682 if (!isSpellCheckingEnabledFor(frame().selection().selection())) |
675 return; | 683 return; |
676 | 684 |
677 Document* document = frame().document(); | 685 Document* document = frame().document(); |
678 DCHECK(document); | 686 DCHECK(document); |
679 | 687 |
680 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 688 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
681 // needs to be audited. See http://crbug.com/590369 for more details. | 689 // needs to be audited. See http://crbug.com/590369 for more details. |
682 document->updateStyleAndLayoutIgnorePendingStylesheets(); | 690 document->updateStyleAndLayoutIgnorePendingStylesheets(); |
683 | 691 |
684 // We want to remove the markers from a word if an editing command will change
the word. This can happen in one of | 692 // We want to remove the markers from a word if an editing command will change |
685 // several scenarios: | 693 // the word. This can happen in one of several scenarios: |
686 // 1. Insert in the middle of a word. | 694 // 1. Insert in the middle of a word. |
687 // 2. Appending non whitespace at the beginning of word. | 695 // 2. Appending non whitespace at the beginning of word. |
688 // 3. Appending non whitespace at the end of word. | 696 // 3. Appending non whitespace at the end of word. |
689 // Note that, appending only whitespaces at the beginning or end of word won't
change the word, so we don't need to | 697 // Note that, appending only whitespaces at the beginning or end of word won't |
690 // remove the markers on that word. | 698 // change the word, so we don't need to remove the markers on that word. Of |
691 // Of course, if current selection is a range, we potentially will edit two wo
rds that fall on the boundaries of | 699 // course, if current selection is a range, we potentially will edit two words |
692 // selection, and remove words between the selection boundaries. | 700 // that fall on the boundaries of selection, and remove words between the |
693 // | 701 // selection boundaries. |
694 VisiblePosition startOfSelection = | 702 VisiblePosition startOfSelection = |
695 frame().selection().selection().visibleStart(); | 703 frame().selection().selection().visibleStart(); |
696 VisiblePosition endOfSelection = frame().selection().selection().visibleEnd(); | 704 VisiblePosition endOfSelection = frame().selection().selection().visibleEnd(); |
697 if (startOfSelection.isNull()) | 705 if (startOfSelection.isNull()) |
698 return; | 706 return; |
699 // First word is the word that ends after or on the start of selection. | 707 // First word is the word that ends after or on the start of selection. |
700 VisiblePosition startOfFirstWord = | 708 VisiblePosition startOfFirstWord = |
701 startOfWord(startOfSelection, LeftWordIfOnBoundary); | 709 startOfWord(startOfSelection, LeftWordIfOnBoundary); |
702 VisiblePosition endOfFirstWord = | 710 VisiblePosition endOfFirstWord = |
703 endOfWord(startOfSelection, LeftWordIfOnBoundary); | 711 endOfWord(startOfSelection, LeftWordIfOnBoundary); |
704 // Last word is the word that begins before or on the end of selection | 712 // Last word is the word that begins before or on the end of selection |
705 VisiblePosition startOfLastWord = | 713 VisiblePosition startOfLastWord = |
706 startOfWord(endOfSelection, RightWordIfOnBoundary); | 714 startOfWord(endOfSelection, RightWordIfOnBoundary); |
707 VisiblePosition endOfLastWord = | 715 VisiblePosition endOfLastWord = |
708 endOfWord(endOfSelection, RightWordIfOnBoundary); | 716 endOfWord(endOfSelection, RightWordIfOnBoundary); |
709 | 717 |
710 if (startOfFirstWord.isNull()) { | 718 if (startOfFirstWord.isNull()) { |
711 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); | 719 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); |
712 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); | 720 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); |
713 } | 721 } |
714 | 722 |
715 if (endOfLastWord.isNull()) { | 723 if (endOfLastWord.isNull()) { |
716 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); | 724 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); |
717 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); | 725 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); |
718 } | 726 } |
719 | 727 |
720 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the
start of selection, | 728 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the |
721 // we choose next word as the first word. | 729 // start of selection, we choose next word as the first word. |
722 if (doNotRemoveIfSelectionAtWordBoundary && | 730 if (doNotRemoveIfSelectionAtWordBoundary && |
723 endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) { | 731 endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) { |
724 startOfFirstWord = nextWordPosition(startOfFirstWord); | 732 startOfFirstWord = nextWordPosition(startOfFirstWord); |
725 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); | 733 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); |
726 if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent()) | 734 if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent()) |
727 return; | 735 return; |
728 } | 736 } |
729 | 737 |
730 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at th
e end of selection, | 738 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at |
731 // we choose previous word as the last word. | 739 // the end of selection, we choose previous word as the last word. |
732 if (doNotRemoveIfSelectionAtWordBoundary && | 740 if (doNotRemoveIfSelectionAtWordBoundary && |
733 startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) { | 741 startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) { |
734 startOfLastWord = previousWordPosition(startOfLastWord); | 742 startOfLastWord = previousWordPosition(startOfLastWord); |
735 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); | 743 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); |
736 if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent()) | 744 if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent()) |
737 return; | 745 return; |
738 } | 746 } |
739 | 747 |
740 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || | 748 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || |
741 startOfLastWord.isNull() || endOfLastWord.isNull()) | 749 startOfLastWord.isNull() || endOfLastWord.isNull()) |
742 return; | 750 return; |
743 | 751 |
744 const Position& removeMarkerStart = startOfFirstWord.deepEquivalent(); | 752 const Position& removeMarkerStart = startOfFirstWord.deepEquivalent(); |
745 const Position& removeMarkerEnd = endOfLastWord.deepEquivalent(); | 753 const Position& removeMarkerEnd = endOfLastWord.deepEquivalent(); |
746 if (removeMarkerStart > removeMarkerEnd) { | 754 if (removeMarkerStart > removeMarkerEnd) { |
747 // editing/inserting/insert-br-008.html and more reach here. | 755 // editing/inserting/insert-br-008.html and more reach here. |
748 // TODO(yosin): To avoid |DCHECK(removeMarkerStart <= removeMarkerEnd)| | 756 // TODO(yosin): To avoid |DCHECK(removeMarkerStart <= removeMarkerEnd)| |
749 // in |EphemeralRange| constructor, we have this if-statement. Once we | 757 // in |EphemeralRange| constructor, we have this if-statement. Once we |
750 // fix |startOfWord()| and |endOfWord()|, we should remove this | 758 // fix |startOfWord()| and |endOfWord()|, we should remove this |
751 // if-statement. | 759 // if-statement. |
752 return; | 760 return; |
753 } | 761 } |
754 | 762 |
755 // Now we remove markers on everything between startOfFirstWord and endOfLastW
ord. | 763 // Now we remove markers on everything between startOfFirstWord and |
756 // However, if an autocorrection change a single word to multiple words, we wa
nt to remove correction mark from all the | 764 // endOfLastWord. However, if an autocorrection change a single word to |
757 // resulted words even we only edit one of them. For example, assuming autocor
rection changes "avantgarde" to "avant | 765 // multiple words, we want to remove correction mark from all the resulted |
758 // garde", we will have CorrectionIndicator marker on both words and on the wh
itespace between them. If we then edit garde, | 766 // words even we only edit one of them. For example, assuming autocorrection |
759 // we would like to remove the marker from word "avant" and whitespace as well
. So we need to get the continous range of | 767 // changes "avantgarde" to "avant garde", we will have CorrectionIndicator |
760 // of marker that contains the word in question, and remove marker on that who
le range. | 768 // marker on both words and on the whitespace between them. If we then edit |
| 769 // garde, we would like to remove the marker from word "avant" and whitespace |
| 770 // as well. So we need to get the continous range of of marker that contains |
| 771 // the word in question, and remove marker on that whole range. |
761 const EphemeralRange wordRange(removeMarkerStart, removeMarkerEnd); | 772 const EphemeralRange wordRange(removeMarkerStart, removeMarkerEnd); |
762 document->markers().removeMarkers( | 773 document->markers().removeMarkers( |
763 wordRange, DocumentMarker::MisspellingMarkers(), | 774 wordRange, DocumentMarker::MisspellingMarkers(), |
764 DocumentMarkerController::RemovePartiallyOverlappingMarker); | 775 DocumentMarkerController::RemovePartiallyOverlappingMarker); |
765 } | 776 } |
766 | 777 |
767 void SpellChecker::didEndEditingOnTextField(Element* e) { | 778 void SpellChecker::didEndEditingOnTextField(Element* e) { |
768 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField"); | 779 TRACE_EVENT0("blink", "SpellChecker::didEndEditingOnTextField"); |
769 | 780 |
770 // Remove markers when deactivating a selection in an <input type="text"/>. | 781 // Remove markers when deactivating a selection in an <input type="text"/>. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
825 return oldSelection.isContentEditable(); | 836 return oldSelection.isContentEditable(); |
826 } | 837 } |
827 | 838 |
828 void SpellChecker::respondToChangedSelection( | 839 void SpellChecker::respondToChangedSelection( |
829 const VisibleSelection& oldSelection, | 840 const VisibleSelection& oldSelection, |
830 FrameSelection::SetSelectionOptions options) { | 841 FrameSelection::SetSelectionOptions options) { |
831 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); | 842 TRACE_EVENT0("blink", "SpellChecker::respondToChangedSelection"); |
832 if (!isSpellCheckingEnabledFor(oldSelection)) | 843 if (!isSpellCheckingEnabledFor(oldSelection)) |
833 return; | 844 return; |
834 | 845 |
835 // When spell checking is off, existing markers disappear after the selection
changes. | 846 // When spell checking is off, existing markers disappear after the selection |
| 847 // changes. |
836 if (!isSpellCheckingEnabled()) { | 848 if (!isSpellCheckingEnabled()) { |
837 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); | 849 frame().document()->markers().removeMarkers(DocumentMarker::Spelling); |
838 frame().document()->markers().removeMarkers(DocumentMarker::Grammar); | 850 frame().document()->markers().removeMarkers(DocumentMarker::Grammar); |
839 return; | 851 return; |
840 } | 852 } |
841 | 853 |
842 if (!(options & FrameSelection::CloseTyping)) | 854 if (!(options & FrameSelection::CloseTyping)) |
843 return; | 855 return; |
844 if (!shouldCheckOldSelection(oldSelection)) | 856 if (!shouldCheckOldSelection(oldSelection)) |
845 return; | 857 return; |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1045 } | 1057 } |
1046 wordStart = wordEnd; | 1058 wordStart = wordEnd; |
1047 } | 1059 } |
1048 return results; | 1060 return results; |
1049 } | 1061 } |
1050 | 1062 |
1051 std::pair<String, int> SpellChecker::findFirstMisspelling(const Position& start, | 1063 std::pair<String, int> SpellChecker::findFirstMisspelling(const Position& start, |
1052 const Position& end) { | 1064 const Position& end) { |
1053 String misspelledWord; | 1065 String misspelledWord; |
1054 | 1066 |
1055 // Initialize out parameters; they will be updated if we find something to ret
urn. | 1067 // Initialize out parameters; they will be updated if we find something to |
| 1068 // return. |
1056 String firstFoundItem; | 1069 String firstFoundItem; |
1057 int firstFoundOffset = 0; | 1070 int firstFoundOffset = 0; |
1058 | 1071 |
1059 // Expand the search range to encompass entire paragraphs, since text checking
needs that much context. | 1072 // Expand the search range to encompass entire paragraphs, since text checking |
1060 // Determine the character offset from the start of the paragraph to the start
of the original search range, | 1073 // needs that much context. Determine the character offset from the start of |
1061 // since we will want to ignore results in this area. | 1074 // the paragraph to the start of the original search range, since we will want |
| 1075 // to ignore results in this area. |
1062 Position paragraphStart = | 1076 Position paragraphStart = |
1063 startOfParagraph(createVisiblePosition(start)).toParentAnchoredPosition(); | 1077 startOfParagraph(createVisiblePosition(start)).toParentAnchoredPosition(); |
1064 Position paragraphEnd = end; | 1078 Position paragraphEnd = end; |
1065 int totalRangeLength = | 1079 int totalRangeLength = |
1066 TextIterator::rangeLength(paragraphStart, paragraphEnd); | 1080 TextIterator::rangeLength(paragraphStart, paragraphEnd); |
1067 paragraphEnd = | 1081 paragraphEnd = |
1068 endOfParagraph(createVisiblePosition(start)).toParentAnchoredPosition(); | 1082 endOfParagraph(createVisiblePosition(start)).toParentAnchoredPosition(); |
1069 | 1083 |
1070 int rangeStartOffset = TextIterator::rangeLength(paragraphStart, start); | 1084 int rangeStartOffset = TextIterator::rangeLength(paragraphStart, start); |
1071 int totalLengthProcessed = 0; | 1085 int totalLengthProcessed = 0; |
1072 | 1086 |
1073 bool firstIteration = true; | 1087 bool firstIteration = true; |
1074 bool lastIteration = false; | 1088 bool lastIteration = false; |
1075 while (totalLengthProcessed < totalRangeLength) { | 1089 while (totalLengthProcessed < totalRangeLength) { |
1076 // Iterate through the search range by paragraphs, checking each one for spe
lling. | 1090 // Iterate through the search range by paragraphs, checking each one for |
| 1091 // spelling. |
1077 int currentLength = TextIterator::rangeLength(paragraphStart, paragraphEnd); | 1092 int currentLength = TextIterator::rangeLength(paragraphStart, paragraphEnd); |
1078 int currentStartOffset = firstIteration ? rangeStartOffset : 0; | 1093 int currentStartOffset = firstIteration ? rangeStartOffset : 0; |
1079 int currentEndOffset = currentLength; | 1094 int currentEndOffset = currentLength; |
1080 if (inSameParagraph(createVisiblePosition(paragraphStart), | 1095 if (inSameParagraph(createVisiblePosition(paragraphStart), |
1081 createVisiblePosition(end))) { | 1096 createVisiblePosition(end))) { |
1082 // Determine the character offset from the end of the original search rang
e to the end of the paragraph, | 1097 // Determine the character offset from the end of the original search |
1083 // since we will want to ignore results in this area. | 1098 // range to the end of the paragraph, since we will want to ignore results |
| 1099 // in this area. |
1084 currentEndOffset = TextIterator::rangeLength(paragraphStart, end); | 1100 currentEndOffset = TextIterator::rangeLength(paragraphStart, end); |
1085 lastIteration = true; | 1101 lastIteration = true; |
1086 } | 1102 } |
1087 if (currentStartOffset < currentEndOffset) { | 1103 if (currentStartOffset < currentEndOffset) { |
1088 String paragraphString = | 1104 String paragraphString = |
1089 plainText(EphemeralRange(paragraphStart, paragraphEnd)); | 1105 plainText(EphemeralRange(paragraphStart, paragraphEnd)); |
1090 if (paragraphString.length() > 0) { | 1106 if (paragraphString.length() > 0) { |
1091 int spellingLocation = 0; | 1107 int spellingLocation = 0; |
1092 | 1108 |
1093 Vector<TextCheckingResult> results = findMisspellings(paragraphString); | 1109 Vector<TextCheckingResult> results = findMisspellings(paragraphString); |
(...skipping 29 matching lines...) Expand all Loading... |
1123 startOfNextParagraph(createVisiblePosition(paragraphEnd)); | 1139 startOfNextParagraph(createVisiblePosition(paragraphEnd)); |
1124 paragraphStart = newParagraphStart.toParentAnchoredPosition(); | 1140 paragraphStart = newParagraphStart.toParentAnchoredPosition(); |
1125 paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPosition(); | 1141 paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPosition(); |
1126 firstIteration = false; | 1142 firstIteration = false; |
1127 totalLengthProcessed += currentLength; | 1143 totalLengthProcessed += currentLength; |
1128 } | 1144 } |
1129 return std::make_pair(firstFoundItem, firstFoundOffset); | 1145 return std::make_pair(firstFoundItem, firstFoundOffset); |
1130 } | 1146 } |
1131 | 1147 |
1132 } // namespace blink | 1148 } // namespace blink |
OLD | NEW |