Changeset 83060 in webkit


Ignore:
Timestamp:
Apr 6, 2011 9:09:28 AM (13 years ago)
Author:
commit-queue@webkit.org
Message:

2011-04-06 Jia Pu <jpu@apple.com>

Reviewed by Darin Adler.

[Mac] WebCore need to notify AppKit spell checker after user has modified autocorrected text.
https://bugs.webkit.org/show_bug.cgi?id=57665
<rdar://problem/7350477>

We need to track how user modified an autocorrected word. If he changed it back to original
text, we want to record AutocorrectionReverted response. And if he changed it to something
else, we want to record AutocorrectionEdited response.

To achieve this, we need to distringuish between text replacement caused by autocorrection
from that due to other causes, such as reversion, text substitution, etc. So we added a new
marker type "Autocorrected". We also need to be able to check for correction, even when we
don't intend to actually carry out replacement. For this, we introduced a new TextCheckingOption
value, "CheckForCorrection".

We also added DocumentMarkerController::markersInRange() to retrieve a vector of markers in
specified range, and of specified type.

  • dom/DocumentMarker.h:
  • dom/DocumentMarkerController.cpp: (WebCore::DocumentMarkerController::markersInRange): (WebCore::DocumentMarkerController::hasMarkers):
  • dom/DocumentMarkerController.h:
  • editing/Editor.cpp: (WebCore::markerTypesForAutocorrection): (WebCore::markersHaveIdenticalDescription): (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges): (WebCore::Editor::recordSpellcheckerResponseForModifiedCorrection): (WebCore::Editor::changeBackToReplacedString): (WebCore::Editor::markMisspellingsAndBadGrammar): (WebCore::Editor::applyCorrectionPanelInfo): (WebCore::Editor::unappliedSpellCorrection): (WebCore::Editor::textCheckingTypeMaskFor):
  • editing/Editor.h:
  • editing/SpellingCorrectionCommand.cpp: (WebCore::SpellingCorrectionCommand::doApply):
Location:
trunk/Source/WebCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r83053 r83060  
     12011-04-06  Jia Pu  <jpu@apple.com>
     2
     3        Reviewed by Darin Adler.
     4
     5        [Mac] WebCore need to notify AppKit spell checker after user has modified autocorrected text.
     6        https://bugs.webkit.org/show_bug.cgi?id=57665
     7        <rdar://problem/7350477>
     8
     9        We need to track how user modified an autocorrected word. If he changed it back to original
     10        text, we want to record AutocorrectionReverted response. And if he changed it to something
     11        else, we want to record AutocorrectionEdited response.
     12
     13        To achieve this, we need to distringuish between text replacement caused by autocorrection
     14        from that due to other causes, such as reversion, text substitution, etc. So we added a new
     15        marker type "Autocorrected". We also need to be able to check for correction, even when we
     16        don't intend to actually carry out replacement. For this, we introduced a new TextCheckingOption
     17        value, "CheckForCorrection".
     18
     19        We also added DocumentMarkerController::markersInRange() to retrieve a vector of markers in
     20        specified range, and of specified type.
     21
     22        * dom/DocumentMarker.h:
     23        * dom/DocumentMarkerController.cpp:
     24        (WebCore::DocumentMarkerController::markersInRange):
     25        (WebCore::DocumentMarkerController::hasMarkers):
     26        * dom/DocumentMarkerController.h:
     27        * editing/Editor.cpp:
     28        (WebCore::markerTypesForAutocorrection):
     29        (WebCore::markersHaveIdenticalDescription):
     30        (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
     31        (WebCore::Editor::recordSpellcheckerResponseForModifiedCorrection):
     32        (WebCore::Editor::changeBackToReplacedString):
     33        (WebCore::Editor::markMisspellingsAndBadGrammar):
     34        (WebCore::Editor::applyCorrectionPanelInfo):
     35        (WebCore::Editor::unappliedSpellCorrection):
     36        (WebCore::Editor::textCheckingTypeMaskFor):
     37        * editing/Editor.h:
     38        * editing/SpellingCorrectionCommand.cpp:
     39        (WebCore::SpellingCorrectionCommand::doApply):
     40
    1412011-04-06  Sheriff Bot  <webkit.review.bot@gmail.com>
    242
  • trunk/Source/WebCore/dom/DocumentMarker.h

    r80023 r83060  
    3838        Grammar = 1 << 1,
    3939        TextMatch = 1 << 2,
    40         // Text has been modified by spell correction. On some platforms, this prevents the text
    41         // to be autocorrected again. On post Snow Leopard Mac OS X, if a Replacement marker contains
    42         // non-empty description, a reversion UI will be shown.
     40        // Text has been modified by spell correction, reversion of spell correction or other type of substitution.
     41        // On some platforms, this prevents the text from being autocorrected again. On post Snow Leopard Mac OS X,
     42        // if a Replacement marker contains non-empty description, a reversion UI will be shown.
    4343        Replacement = 1 << 3,
    4444        // Renderer needs to add underline indicating that the text has been modified by spell
     
    5050        // Correction suggestion has been offered, but got rejected by user.
    5151        RejectedCorrection = 1 << 5,
    52         // On some platforms, this prevents the text to be spellchecked again.
    53         SpellCheckingExemption = 1 << 6,
    54         AllMarkers = Spelling | Grammar | TextMatch | Replacement | CorrectionIndicator | RejectedCorrection | SpellCheckingExemption
     52        // Text has been modified by autocorrection. The description of this marker is the original text before autocorrection.
     53        Autocorrected = 1 << 6,
     54        // On some platforms, this prevents the text from being spellchecked again.
     55        SpellCheckingExemption = 1 << 7,
     56        AllMarkers = Spelling | Grammar | TextMatch | Replacement | CorrectionIndicator | RejectedCorrection | Autocorrected | SpellCheckingExemption
    5557    };
    5658    MarkerType type;
  • trunk/Source/WebCore/dom/DocumentMarkerController.cpp

    r80075 r83060  
    318318}
    319319
     320Vector<DocumentMarker> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerType markerType)
     321{
     322    if (!possiblyHasMarkers(markerType))
     323        return Vector<DocumentMarker>();
     324
     325    Vector<DocumentMarker> foundMarkers;
     326
     327    Node* startContainer = range->startContainer();
     328    ASSERT(startContainer);
     329    Node* endContainer = range->endContainer();
     330    ASSERT(endContainer);
     331
     332    Node* pastLastNode = range->pastLastNode();
     333    for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
     334        Vector<DocumentMarker> markers = markersForNode(node);
     335        Vector<DocumentMarker>::const_iterator end = markers.end();
     336        for (Vector<DocumentMarker>::const_iterator it = markers.begin(); it != end; ++it) {
     337            if (!(markerType & it->type))
     338                continue;
     339            if (node == startContainer && it->endOffset <= static_cast<unsigned>(range->startOffset()))
     340                continue;
     341            if (node == endContainer && it->startOffset >= static_cast<unsigned>(range->endOffset()))
     342                continue;
     343            foundMarkers.append(*it);
     344        }
     345    }
     346    return foundMarkers;
     347}
     348
    320349Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
    321350{
     
    603632            if (!(markerTypes & it->type))
    604633                continue;
    605             if (node == startContainer && node == endContainer) {
    606                 // The range spans only one node.
    607                 if (it->endOffset > static_cast<unsigned>(range->startOffset()) && it->startOffset < static_cast<unsigned>(range->endOffset()))
    608                     return true;
    609             } else {
    610                 if (node == startContainer) {
    611                     if (it->endOffset > static_cast<unsigned>(range->startOffset()))
    612                         return true;
    613                 } else if (node == endContainer) {
    614                     if (it->startOffset < static_cast<unsigned>(range->endOffset()))
    615                         return true;
    616                 } else
    617                     return true;
    618             }
     634            if (node == startContainer && it->endOffset <= static_cast<unsigned>(range->startOffset()))
     635                continue;
     636            if (node == endContainer && it->startOffset >= static_cast<unsigned>(range->endOffset()))
     637                continue;
     638            return true;
    619639        }
    620640    }
  • trunk/Source/WebCore/dom/DocumentMarkerController.h

    r80023 r83060  
    6969    DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers);
    7070    Vector<DocumentMarker> markersForNode(Node*);
     71    Vector<DocumentMarker> markersInRange(Range*, DocumentMarker::MarkerType);
    7172    Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers);
    7273    void clearDescriptionOnMarkersIntersectingRange(Range*, DocumentMarker::MarkerTypes);
  • trunk/Source/WebCore/editing/Editor.cpp

    r83049 r83060  
    109109        markerTypesForAutoCorrection.append(DocumentMarker::CorrectionIndicator);
    110110        markerTypesForAutoCorrection.append(DocumentMarker::SpellCheckingExemption);
     111        markerTypesForAutoCorrection.append(DocumentMarker::Autocorrected);
    111112    }
    112113    return markerTypesForAutoCorrection;
     
    122123    return markerTypesForReplacement;
    123124}
     125
     126#if SUPPORT_AUTOCORRECTION_PANEL
     127static bool markersHaveIdenticalDescription(const Vector<DocumentMarker>& markers)
     128{
     129    if (markers.isEmpty())
     130        return true;
     131
     132    const String& description = markers[0].description;
     133    for (size_t i = 1; i < markers.size(); ++i) {
     134        if (description != markers[i].description)
     135            return false;
     136    }
     137    return true;
     138}
     139#endif
    124140
    125141// When an event handler has moved the selection outside of a text control
     
    22132229    bool shouldPerformReplacement = textCheckingOptions & PerformReplacement;
    22142230    bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
     2231    bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
    22152232
    22162233    // This function is called with selections already expanded to word boundaries.
     
    22402257        return;
    22412258
    2242     if (shouldPerformReplacement || shouldMarkSpelling) {
     2259    if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) {
    22432260        if (m_frame->selection()->selectionType() == VisibleSelection::CaretSelection) {
    22442261            // Attempt to save the caret position so we can restore it later if needed
     
    23032320                }
    23042321            }
    2305         } else if ((shouldPerformReplacement || shouldShowCorrectionPanel) && resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingParagraph.checkingStart()
     2322        } else if (resultLocation + resultLength <= spellingRangeEndOffset && resultLocation + resultLength >= spellingParagraph.checkingStart()
    23062323                    && (result->type == TextCheckingTypeLink
    23072324                    || result->type == TextCheckingTypeQuote
     
    23292346                continue;
    23302347
     2348            String replacedString;
     2349
    23312350            // Don't correct spelling in an already-corrected word.
    23322351            if (result->type == TextCheckingTypeCorrection) {
    2333                 Node* node = rangeToReplace->startContainer();
    2334                 int startOffset = rangeToReplace->startOffset();
    2335                 int endOffset = startOffset + replacementLength;
    2336                 Vector<DocumentMarker> markers = node->document()->markers()->markersForNode(node);
    2337                 size_t markerCount = markers.size();
    2338                 for (size_t i = 0; i < markerCount; ++i) {
    2339                     const DocumentMarker& marker = markers[i];
    2340                     if ((marker.type == DocumentMarker::Replacement || marker.type == DocumentMarker::RejectedCorrection) && static_cast<int>(marker.startOffset) < endOffset && static_cast<int>(marker.endOffset) > startOffset) {
    2341                         doReplacement = false;
    2342                         break;
    2343                     }
    2344                     if (static_cast<int>(marker.startOffset) >= endOffset)
    2345                         break;
    2346                 }
     2352                replacedString = plainText(rangeToReplace.get());
     2353                DocumentMarkerController* markers = m_frame->document()->markers();
     2354                if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::Replacement)) {
     2355                    doReplacement = false;
     2356                    recordSpellcheckerResponseForModifiedCorrection(rangeToReplace.get(), replacedString, result->replacement);
     2357                } else if (markers->hasMarkers(rangeToReplace.get(), DocumentMarker::RejectedCorrection))
     2358                    doReplacement = false;
    23472359            }
    23482360
    2349             if (!doReplacement)
     2361            if (!(shouldPerformReplacement || shouldShowCorrectionPanel) || !doReplacement)
    23502362                continue;
    23512363
     
    23812393                    applyCommand(CreateLinkCommand::create(m_frame->document(), result->replacement));
    23822394            } else if (canEdit() && shouldInsertText(result->replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
    2383                 String replacedString;
    2384                 if (result->type == TextCheckingTypeCorrection)
    2385                     replacedString = plainText(rangeToReplace.get());
    2386 
    23872395                bool useSpellingCorrectionCommand = false;
    23882396#if SUPPORT_AUTOCORRECTION_PANEL
     
    24132421                    // Add a marker so that corrections can easily be undone and won't be re-corrected.
    24142422                    RefPtr<Range> replacedRange = spellingParagraph.subrange(resultLocation, replacementLength);
    2415                     replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::Replacement, replacedString);
    2416                     replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::CorrectionIndicator);
    2417                     replacedRange->startContainer()->document()->markers()->addMarker(replacedRange.get(), DocumentMarker::SpellCheckingExemption);
     2423                    Vector<DocumentMarker::MarkerType> markerTypesToAdd = markerTypesForAutocorrection();
     2424                    DocumentMarkerController* markers = replacedRange->startContainer()->document()->markers();
     2425                    for (size_t i = 0; i < markerTypesToAdd.size(); ++i) {
     2426                        DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
     2427                        if (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected)
     2428                            markers->addMarker(replacedRange.get(), markerType, replacedString);
     2429                        else
     2430                            markers->addMarker(replacedRange.get(), markerType);
     2431                    }
    24182432                }
    24192433            }
     
    24432457}
    24442458
     2459void Editor::recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction)
     2460{
     2461#if SUPPORT_AUTOCORRECTION_PANEL
     2462    if (!rangeOfCorrection)
     2463        return;
     2464    DocumentMarkerController* markers = rangeOfCorrection->startContainer()->document()->markers();
     2465    Vector<DocumentMarker> correctedOnceMarkers = markers->markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
     2466    if (correctedOnceMarkers.isEmpty())
     2467        return;
     2468   
     2469    // Spelling corrected text has been edited. We need to determine whether user has reverted it to original text or
     2470    // edited it to something else, and notify spellchecker accordingly.
     2471    if (markersHaveIdenticalDescription(correctedOnceMarkers) && correctedOnceMarkers[0].description == corrected)
     2472        client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
     2473    else
     2474        client()->recordAutocorrectionResponse(EditorClient::AutocorrectionEdited, corrected, correction);
     2475    markers->removeMarkers(rangeOfCorrection, DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
     2476#else
     2477    UNUSED_PARAM(rangeOfCorrection);
     2478    UNUSED_PARAM(corrected);
     2479    UNUSED_PARAM(correction);
     2480#endif
     2481}
     2482
    24452483void Editor::changeBackToReplacedString(const String& replacedString)
    24462484{
     
    24622500    changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
    24632501#if SUPPORT_AUTOCORRECTION_PANEL
     2502    changedRange->startContainer()->document()->markers()->removeMarkers(changedRange.get(), DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
    24642503    changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption);
    24652504#endif
     
    24762515    if (!isContinuousSpellCheckingEnabled())
    24772516        return;
    2478     TextCheckingOptions textCheckingOptions = MarkSpelling;
     2517    TextCheckingOptions textCheckingOptions = MarkSpelling | CheckForCorrection;
    24792518    if (markGrammar && isGrammarCheckingEnabled())
    24802519        textCheckingOptions |= MarkGrammar;
     
    27332772    size_t size = markerTypesToAdd.size();
    27342773    for (size_t i = 0; i < size; ++i) {
    2735         if (m_correctionPanelInfo.panelType == CorrectionPanelInfo::PanelTypeReversion)
    2736             markers->addMarker(replacementRange.get(), markerTypesToAdd[i]);
    2737         else
    2738             markers->addMarker(replacementRange.get(), markerTypesToAdd[i], m_correctionPanelInfo.replacedString);
     2774        DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
     2775        String description;
     2776        if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeReversion && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
     2777            description = m_correctionPanelInfo.replacedString;
     2778        markers->addMarker(replacementRange.get(), markerType, description);
    27392779    }
    27402780#else // SUPPORT_AUTOCORRECTION_PANEL
     
    27732813
    27742814    DocumentMarkerController* markers = m_frame->document()->markers();
    2775     markers->removeMarkers(range.get(), DocumentMarker::Spelling);
     2815    markers->removeMarkers(range.get(), DocumentMarker::Spelling | DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
    27762816    markers->addMarker(range.get(), DocumentMarker::Replacement);
    27772817    markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
     
    35953635    bool shouldMarkGrammar = textCheckingOptions & MarkGrammar;
    35963636    bool shouldShowCorrectionPanel = textCheckingOptions & ShowCorrectionPanel;
     3637    bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & CheckForCorrection);
    35973638
    35983639    TextCheckingTypeMask checkingTypes = 0;
     
    36013642    if (shouldMarkGrammar)
    36023643        checkingTypes |= TextCheckingTypeGrammar;
    3603     if (shouldShowCorrectionPanel)
     3644    if (shouldCheckForCorrection)
    36043645        checkingTypes |= TextCheckingTypeCorrection;
    36053646
  • trunk/Source/WebCore/editing/Editor.h

    r82952 r83060  
    230230        PerformReplacement = 1 << 2,
    231231        ShowCorrectionPanel = 1 << 3,
     232        CheckForCorrection = 1 << 4,
    232233    };
    233234    typedef unsigned TextCheckingOptions;
     
    417418    void markMisspellingsOrBadGrammar(const VisibleSelection&, bool checkSpelling, RefPtr<Range>& firstMisspellingRange);
    418419    TextCheckingTypeMask textCheckingTypeMaskFor(TextCheckingOptions);
     420    void recordSpellcheckerResponseForModifiedCorrection(Range*, const String& corrected, const String& correction);
    419421
    420422    void selectComposition();
  • trunk/Source/WebCore/editing/SpellingCorrectionCommand.cpp

    r80023 r83060  
    9595        return;
    9696
    97     applyCommandToComposite(SetSelectionCommand::create(m_selectionToBeCorrected, SelectionController::CloseTyping | SelectionController::ClearTypingStyle));
     97    applyCommandToComposite(SetSelectionCommand::create(m_selectionToBeCorrected, SelectionController::SpellCorrectionTriggered | SelectionController::CloseTyping | SelectionController::ClearTypingStyle));
    9898#if SUPPORT_AUTOCORRECTION_PANEL
    9999    applyCommandToComposite(SpellingCorrectionRecordUndoCommand::create(document(), m_corrected, m_correction));
Note: See TracChangeset for help on using the changeset viewer.