| 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 |