Changeset 70847 in webkit


Ignore:
Timestamp:
Oct 28, 2010 11:58:48 PM (14 years ago)
Author:
morrita@google.com
Message:

2010-10-26 MORITA Hajime <morrita@google.com>

Reviewed by Kent Tamura.

Refactoring: Spellchecking related static functions could form a class
https://bugs.webkit.org/show_bug.cgi?id=48287

Extracted spellcheck related static functions to TextCheckingHelper class,
which has EditorClient and Range as its member.

No new tests. Just a refactoring.

  • CMakeLists.txt:
  • GNUmakefile.am:
  • WebCore.gypi:
  • WebCore.pro:
  • WebCore.vcproj/WebCore.vcproj:
  • WebCore.xcodeproj/project.pbxproj:
  • editing/Editor.cpp: (WebCore::Editor::advanceToNextMisspelling): (WebCore::Editor::isSelectionUngrammatical): (WebCore::Editor::guessesForUngrammaticalSelection): (WebCore::Editor::guessesForMisspelledOrUngrammaticalSelection): (WebCore::Editor::markMisspellingsAfterTypingToPosition): (WebCore::Editor::markMisspellingsOrBadGrammar): (WebCore::Editor::markMisspellings): (WebCore::Editor::markBadGrammar): (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges): (WebCore::Editor::changeBackToReplacedString):
  • editing/Editor.h:
  • editing/TextCheckingHelper.cpp: Added. (WebCore::TextCheckingHelper::TextCheckingHelper): (WebCore::TextCheckingHelper::~TextCheckingHelper): (WebCore::TextCheckingHelper::paragraphAlignedRange): (WebCore::TextCheckingHelper::findFirstMisspelling): (WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar): (WebCore::TextCheckingHelper::findFirstGrammarDetail): (WebCore::TextCheckingHelper::findFirstBadGrammar): (WebCore::TextCheckingHelper::isUngrammatical): (WebCore::TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange): (WebCore::TextCheckingHelper::markAllMisspellings): (WebCore::TextCheckingHelper::markAllBadGrammar):
  • editing/TextCheckingHelper.h: Added.
Location:
trunk/WebCore
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/CMakeLists.txt

    r70511 r70847  
    925925    editing/SplitTextNodeCommand.cpp
    926926    editing/SplitTextNodeContainingElementCommand.cpp
     927    editing/TextCheckingHelper.cpp
    927928    editing/TextIterator.cpp
    928929    editing/TypingCommand.cpp
  • trunk/WebCore/ChangeLog

    r70846 r70847  
     12010-10-26  MORITA Hajime  <morrita@google.com>
     2
     3        Reviewed by Kent Tamura.
     4
     5        Refactoring: Spellchecking related static functions could form a class
     6        https://bugs.webkit.org/show_bug.cgi?id=48287
     7
     8        Extracted spellcheck related static functions to TextCheckingHelper class,
     9        which has EditorClient and Range as its member.
     10       
     11        No new tests. Just a refactoring.
     12
     13        * CMakeLists.txt:
     14        * GNUmakefile.am:
     15        * WebCore.gypi:
     16        * WebCore.pro:
     17        * WebCore.vcproj/WebCore.vcproj:
     18        * WebCore.xcodeproj/project.pbxproj:
     19        * editing/Editor.cpp:
     20        (WebCore::Editor::advanceToNextMisspelling):
     21        (WebCore::Editor::isSelectionUngrammatical):
     22        (WebCore::Editor::guessesForUngrammaticalSelection):
     23        (WebCore::Editor::guessesForMisspelledOrUngrammaticalSelection):
     24        (WebCore::Editor::markMisspellingsAfterTypingToPosition):
     25        (WebCore::Editor::markMisspellingsOrBadGrammar):
     26        (WebCore::Editor::markMisspellings):
     27        (WebCore::Editor::markBadGrammar):
     28        (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
     29        (WebCore::Editor::changeBackToReplacedString):
     30        * editing/Editor.h:
     31        * editing/TextCheckingHelper.cpp: Added.
     32        (WebCore::TextCheckingHelper::TextCheckingHelper):
     33        (WebCore::TextCheckingHelper::~TextCheckingHelper):
     34        (WebCore::TextCheckingHelper::paragraphAlignedRange):
     35        (WebCore::TextCheckingHelper::findFirstMisspelling):
     36        (WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar):
     37        (WebCore::TextCheckingHelper::findFirstGrammarDetail):
     38        (WebCore::TextCheckingHelper::findFirstBadGrammar):
     39        (WebCore::TextCheckingHelper::isUngrammatical):
     40        (WebCore::TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange):
     41        (WebCore::TextCheckingHelper::markAllMisspellings):
     42        (WebCore::TextCheckingHelper::markAllBadGrammar):
     43        * editing/TextCheckingHelper.h: Added.
     44
    1452010-10-28  Adam Barth  <abarth@webkit.org>
    246
  • trunk/WebCore/GNUmakefile.am

    r70745 r70847  
    13531353        WebCore/editing/SplitTextNodeContainingElementCommand.h \
    13541354        WebCore/editing/TextAffinity.h \
     1355        WebCore/editing/TextCheckingHelper.cpp \
     1356        WebCore/editing/TextCheckingHelper.h \
    13551357        WebCore/editing/TextGranularity.h \
    13561358        WebCore/editing/TextIterator.cpp \
  • trunk/WebCore/WebCore.gypi

    r70846 r70847  
    14351435            'editing/SplitTextNodeContainingElementCommand.h',
    14361436            'editing/TextAffinity.h',
     1437            'editing/TextCheckingHelper.cpp',
     1438            'editing/TextCheckingHelper.h',
    14371439            'editing/TextGranularity.h',
    14381440            'editing/TextIterator.cpp',
  • trunk/WebCore/WebCore.pro

    r70745 r70847  
    815815    editing/SplitTextNodeCommand.cpp \
    816816    editing/SplitTextNodeContainingElementCommand.cpp \
     817    editing/TextCheckingHelper.cpp \
    817818    editing/TextIterator.cpp \
    818819    editing/TypingCommand.cpp \
  • trunk/WebCore/WebCore.vcproj/WebCore.vcproj

    r70745 r70847  
    4788547885                        </File>
    4788647886                        <File
     47887                                RelativePath="..\editing\TextCheckingHelper.cpp"
     47888                                >
     47889                                <FileConfiguration
     47890                                        Name="Debug|Win32"
     47891                                        ExcludedFromBuild="true"
     47892                                        >
     47893                                        <Tool
     47894                                                Name="VCCLCompilerTool"
     47895                                        />
     47896                                </FileConfiguration>
     47897                                <FileConfiguration
     47898                                        Name="Release|Win32"
     47899                                        ExcludedFromBuild="true"
     47900                                        >
     47901                                        <Tool
     47902                                                Name="VCCLCompilerTool"
     47903                                        />
     47904                                </FileConfiguration>
     47905                                <FileConfiguration
     47906                                        Name="Debug_Internal|Win32"
     47907                                        ExcludedFromBuild="true"
     47908                                        >
     47909                                        <Tool
     47910                                                Name="VCCLCompilerTool"
     47911                                        />
     47912                                </FileConfiguration>
     47913                                <FileConfiguration
     47914                                        Name="Debug_Cairo|Win32"
     47915                                        ExcludedFromBuild="true"
     47916                                        >
     47917                                        <Tool
     47918                                                Name="VCCLCompilerTool"
     47919                                        />
     47920                                </FileConfiguration>
     47921                                <FileConfiguration
     47922                                        Name="Release_Cairo|Win32"
     47923                                        ExcludedFromBuild="true"
     47924                                        >
     47925                                        <Tool
     47926                                                Name="VCCLCompilerTool"
     47927                                        />
     47928                                </FileConfiguration>
     47929                                <FileConfiguration
     47930                                        Name="Debug_All|Win32"
     47931                                        ExcludedFromBuild="true"
     47932                                        >
     47933                                        <Tool
     47934                                                Name="VCCLCompilerTool"
     47935                                        />
     47936                                </FileConfiguration>
     47937                        </File>
     47938                        <File
     47939                                RelativePath="..\editing\TextCheckingHelper.h"
     47940                                >
     47941                        </File>
     47942                        <File
    4788747943                                RelativePath="..\editing\TextGranularity.h"
    4788847944                                >
  • trunk/WebCore/WebCore.xcodeproj/project.pbxproj

    r70745 r70847  
    30553055                A7D6B3490F61104500B79FD1 /* WorkerScriptLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D6B3470F61104500B79FD1 /* WorkerScriptLoader.h */; };
    30563056                A7D6B34A0F61104500B79FD1 /* WorkerScriptLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7D6B3480F61104500B79FD1 /* WorkerScriptLoader.cpp */; };
     3057                A7DBF8DD1276919C006B6008 /* TextCheckingHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */; };
     3058                A7DBF8DE1276919C006B6008 /* TextCheckingHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */; };
    30573059                A7F338A311C0EFCA00A320A7 /* ShadowElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7F338A111C0EFCA00A320A7 /* ShadowElement.cpp */; };
    30583060                A7F338A411C0EFCA00A320A7 /* ShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F338A211C0EFCA00A320A7 /* ShadowElement.h */; };
     
    91269128                A7D6B3470F61104500B79FD1 /* WorkerScriptLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WorkerScriptLoader.h; path = workers/WorkerScriptLoader.h; sourceTree = "<group>"; };
    91279129                A7D6B3480F61104500B79FD1 /* WorkerScriptLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WorkerScriptLoader.cpp; path = workers/WorkerScriptLoader.cpp; sourceTree = "<group>"; };
     9130                A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextCheckingHelper.cpp; sourceTree = "<group>"; };
     9131                A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCheckingHelper.h; sourceTree = "<group>"; };
    91289132                A7F338A111C0EFCA00A320A7 /* ShadowElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowElement.cpp; sourceTree = "<group>"; };
    91299133                A7F338A211C0EFCA00A320A7 /* ShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowElement.h; sourceTree = "<group>"; };
     
    1451714521                                93309DC8099E64910056E581 /* TextAffinity.h */,
    1451814522                                93309DC9099E64910056E581 /* TextGranularity.h */,
     14523                                A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */,
     14524                                A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */,
    1451914525                                93309DCC099E64910056E581 /* TextIterator.cpp */,
    1452014526                                93309DCD099E64910056E581 /* TextIterator.h */,
     
    2094120947                                B2C3DA360D006C1D00EF6F26 /* TextBreakIterator.h in Headers */,
    2094220948                                B2C3DA380D006C1D00EF6F26 /* TextBreakIteratorInternalICU.h in Headers */,
     20949                                A7DBF8DE1276919C006B6008 /* TextCheckingHelper.h in Headers */,
    2094320950                                B2C3DA3A0D006C1D00EF6F26 /* TextCodec.h in Headers */,
    2094420951                                B2C3DA3C0D006C1D00EF6F26 /* TextCodecICU.h in Headers */,
     
    2354923556                                B2C3DA370D006C1D00EF6F26 /* TextBreakIteratorICU.cpp in Sources */,
    2355023557                                B2AFFC980D00A5DF0030074D /* TextBreakIteratorInternalICUMac.mm in Sources */,
     23558                                A7DBF8DD1276919C006B6008 /* TextCheckingHelper.cpp in Sources */,
    2355123559                                B2C3DA390D006C1D00EF6F26 /* TextCodec.cpp in Sources */,
    2355223560                                B2C3DA3B0D006C1D00EF6F26 /* TextCodecICU.cpp in Sources */,
  • trunk/WebCore/editing/Editor.cpp

    r70826 r70847  
    6363#include "Page.h"
    6464#include "Pasteboard.h"
     65#include "TextCheckingHelper.h"
    6566#include "RemoveFormatCommand.h"
    6667#include "RenderBlock.h"
     
    16601661}
    16611662
    1662 static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll, RefPtr<Range>& firstMisspellingRange)
    1663 {
    1664     ASSERT_ARG(client, client);
    1665     ASSERT_ARG(searchRange, searchRange);
    1666    
    1667     WordAwareIterator it(searchRange);
    1668     firstMisspellingOffset = 0;
    1669    
    1670     String firstMisspelling;
    1671     int currentChunkOffset = 0;
    1672 
    1673     while (!it.atEnd()) {
    1674         const UChar* chars = it.characters();
    1675         int len = it.length();
    1676        
    1677         // Skip some work for one-space-char hunks
    1678         if (!(len == 1 && chars[0] == ' ')) {
    1679            
    1680             int misspellingLocation = -1;
    1681             int misspellingLength = 0;
    1682             client->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
    1683 
    1684             // 5490627 shows that there was some code path here where the String constructor below crashes.
    1685             // We don't know exactly what combination of bad input caused this, so we're making this much
    1686             // more robust against bad input on release builds.
    1687             ASSERT(misspellingLength >= 0);
    1688             ASSERT(misspellingLocation >= -1);
    1689             ASSERT(!misspellingLength || misspellingLocation >= 0);
    1690             ASSERT(misspellingLocation < len);
    1691             ASSERT(misspellingLength <= len);
    1692             ASSERT(misspellingLocation + misspellingLength <= len);
    1693            
    1694             if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
    1695                
    1696                 // Compute range of misspelled word
    1697                 RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength);
    1698 
    1699                 // Remember first-encountered misspelling and its offset.
    1700                 if (!firstMisspelling) {
    1701                     firstMisspellingOffset = currentChunkOffset + misspellingLocation;
    1702                     firstMisspelling = String(chars + misspellingLocation, misspellingLength);
    1703                     firstMisspellingRange = misspellingRange;
    1704                 }
    1705 
    1706                 // Store marker for misspelled word.
    1707                 ExceptionCode ec = 0;
    1708                 misspellingRange->startContainer(ec)->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
    1709                 ASSERT(!ec);
    1710 
    1711                 // Bail out if we're marking only the first misspelling, and not all instances.
    1712                 if (!markAll)
    1713                     break;
    1714             }
    1715         }
    1716        
    1717         currentChunkOffset += len;
    1718         it.advance();
    1719     }
    1720    
    1721     return firstMisspelling;
    1722 }
    1723 
    1724 #ifndef BUILDING_ON_TIGER
    1725 
    1726 static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString)
    1727 {
    1728     ASSERT_ARG(arbitraryRange, arbitraryRange);
    1729    
    1730     ExceptionCode ec = 0;
    1731    
    1732     // Expand range to paragraph boundaries
    1733     RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
    1734     setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
    1735     setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
    1736    
    1737     // Compute offset from start of expanded range to start of original range
    1738     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
    1739     offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
    1740    
    1741     // Fill in out parameter with string representing entire paragraph range.
    1742     // Someday we might have a caller that doesn't use this, but for now all callers do.
    1743     paragraphString = plainText(paragraphRange.get());
    1744 
    1745     return paragraphRange;
    1746 }
    1747 
    1748 static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int /*badGrammarPhraseLength*/, Range *searchRange, int startOffset, int endOffset, bool markAll)
    1749 {
    1750     // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
    1751     // Optionally add a DocumentMarker for each detail in the range.
    1752     int earliestDetailLocationSoFar = -1;
    1753     int earliestDetailIndex = -1;
    1754     for (unsigned i = 0; i < grammarDetails.size(); i++) {
    1755         const GrammarDetail* detail = &grammarDetails[i];
    1756         ASSERT(detail->length > 0 && detail->location >= 0);
    1757        
    1758         int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
    1759        
    1760         // Skip this detail if it starts before the original search range
    1761         if (detailStartOffsetInParagraph < startOffset)
    1762             continue;
    1763        
    1764         // Skip this detail if it starts after the original search range
    1765         if (detailStartOffsetInParagraph >= endOffset)
    1766             continue;
    1767        
    1768         if (markAll) {
    1769             RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length);
    1770             ExceptionCode ec = 0;
    1771             badGrammarRange->startContainer(ec)->document()->markers()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
    1772             ASSERT(!ec);
    1773         }
    1774        
    1775         // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
    1776         if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
    1777             earliestDetailIndex = i;
    1778             earliestDetailLocationSoFar = detail->location;
    1779         }
    1780     }
    1781    
    1782     return earliestDetailIndex;
    1783 }
    1784    
    1785 static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
    1786 {
    1787     ASSERT_ARG(client, client);
    1788     ASSERT_ARG(searchRange, searchRange);
    1789    
    1790     // Initialize out parameters; these will be updated if we find something to return.
    1791     outGrammarDetail.location = -1;
    1792     outGrammarDetail.length = 0;
    1793     outGrammarDetail.guesses.clear();
    1794     outGrammarDetail.userDescription = "";
    1795     outGrammarPhraseOffset = 0;
    1796    
    1797     String firstBadGrammarPhrase;
    1798 
    1799     // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
    1800     // Determine the character offset from the start of the paragraph to the start of the original search range,
    1801     // since we will want to ignore results in this area.
    1802     int searchRangeStartOffset;
    1803     String paragraphString;
    1804     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString);
    1805        
    1806     // Determine the character offset from the start of the paragraph to the end of the original search range,
    1807     // since we will want to ignore results in this area also.
    1808     int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
    1809        
    1810     // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
    1811     int startOffset = 0;
    1812     while (startOffset < searchRangeEndOffset) {
    1813         Vector<GrammarDetail> grammarDetails;
    1814         int badGrammarPhraseLocation = -1;
    1815         int badGrammarPhraseLength = 0;
    1816         client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
    1817        
    1818         if (!badGrammarPhraseLength) {
    1819             ASSERT(badGrammarPhraseLocation == -1);
    1820             return String();
    1821         }
    1822 
    1823         ASSERT(badGrammarPhraseLocation >= 0);
    1824         badGrammarPhraseLocation += startOffset;
    1825 
    1826        
    1827         // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
    1828         int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
    1829         if (badGrammarIndex >= 0) {
    1830             ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size());
    1831             outGrammarDetail = grammarDetails[badGrammarIndex];
    1832         }
    1833 
    1834         // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
    1835         // kept going so we could mark all instances).
    1836         if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
    1837             outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset;
    1838             firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength);
    1839            
    1840             // Found one. We're done now, unless we're marking each instance.
    1841             if (!markAll)
    1842                 break;
    1843         }
    1844 
    1845         // These results were all between the start of the paragraph and the start of the search range; look
    1846         // beyond this phrase.
    1847         startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
    1848     }
    1849    
    1850     return firstBadGrammarPhrase;
    1851 }
    1852    
    1853 #endif /* not BUILDING_ON_TIGER */
    1854 
    1855 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    1856 
    1857 static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Range* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
    1858 {
    1859     ASSERT_ARG(client, client);
    1860     ASSERT_ARG(searchRange, searchRange);
    1861    
    1862     String firstFoundItem;
    1863     String misspelledWord;
    1864     String badGrammarPhrase;
    1865     ExceptionCode ec = 0;
    1866    
    1867     // Initialize out parameters; these will be updated if we find something to return.
    1868     outIsSpelling = true;
    1869     outFirstFoundOffset = 0;
    1870     outGrammarDetail.location = -1;
    1871     outGrammarDetail.length = 0;
    1872     outGrammarDetail.guesses.clear();
    1873     outGrammarDetail.userDescription = "";
    1874    
    1875     // Expand the search range to encompass entire paragraphs, since text checking needs that much context.
    1876     // Determine the character offset from the start of the paragraph to the start of the original search range,
    1877     // since we will want to ignore results in this area.
    1878     RefPtr<Range> paragraphRange = searchRange->cloneRange(ec);
    1879     setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition()));
    1880     int totalRangeLength = TextIterator::rangeLength(paragraphRange.get());
    1881     setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition()));
    1882    
    1883     RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->startPosition());
    1884     int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
    1885     int totalLengthProcessed = 0;
    1886    
    1887     bool firstIteration = true;
    1888     bool lastIteration = false;
    1889     while (totalLengthProcessed < totalRangeLength) {
    1890         // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
    1891         int currentLength = TextIterator::rangeLength(paragraphRange.get());
    1892         int currentStartOffset = firstIteration ? searchRangeStartOffset : 0;
    1893         int currentEndOffset = currentLength;
    1894         if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPosition())) {
    1895             // Determine the character offset from the end of the original search range to the end of the paragraph,
    1896             // since we will want to ignore results in this area.
    1897             RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->endPosition());
    1898             currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get());
    1899             lastIteration = true;
    1900         }
    1901         if (currentStartOffset < currentEndOffset) {
    1902             String paragraphString = plainText(paragraphRange.get());
    1903             if (paragraphString.length() > 0) {
    1904                 bool foundGrammar = false;
    1905                 int spellingLocation = 0;
    1906                 int grammarPhraseLocation = 0;
    1907                 int grammarDetailLocation = 0;
    1908                 unsigned grammarDetailIndex = 0;
    1909                
    1910                 Vector<TextCheckingResult> results;
    1911                 uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
    1912                 client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
    1913                
    1914                 for (unsigned i = 0; i < results.size(); i++) {
    1915                     const TextCheckingResult* result = &results[i];
    1916                     if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
    1917                         ASSERT(result->length > 0 && result->location >= 0);
    1918                         spellingLocation = result->location;
    1919                         misspelledWord = paragraphString.substring(result->location, result->length);
    1920                         ASSERT(misspelledWord.length());
    1921                         break;
    1922                     }
    1923                     if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
    1924                         ASSERT(result->length > 0 && result->location >= 0);
    1925                         // We can't stop after the first grammar result, since there might still be a spelling result after
    1926                         // it begins but before the first detail in it, but we can stop if we find a second grammar result.
    1927                         if (foundGrammar)
    1928                             break;
    1929                         for (unsigned j = 0; j < result->details.size(); j++) {
    1930                             const GrammarDetail* detail = &result->details[j];
    1931                             ASSERT(detail->length > 0 && detail->location >= 0);
    1932                             if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
    1933                                 grammarDetailIndex = j;
    1934                                 grammarDetailLocation = result->location + detail->location;
    1935                                 foundGrammar = true;
    1936                             }
    1937                         }
    1938                         if (foundGrammar) {
    1939                             grammarPhraseLocation = result->location;
    1940                             outGrammarDetail = result->details[grammarDetailIndex];
    1941                             badGrammarPhrase = paragraphString.substring(result->location, result->length);
    1942                             ASSERT(badGrammarPhrase.length());
    1943                         }
    1944                     }
    1945                 }
    1946 
    1947                 if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
    1948                     int spellingOffset = spellingLocation - currentStartOffset;
    1949                     if (!firstIteration) {
    1950                         RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
    1951                         spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
    1952                     }
    1953                     outIsSpelling = true;
    1954                     outFirstFoundOffset = spellingOffset;
    1955                     firstFoundItem = misspelledWord;
    1956                     break;
    1957                 }
    1958                 if (checkGrammar && !badGrammarPhrase.isEmpty()) {
    1959                     int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
    1960                     if (!firstIteration) {
    1961                         RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
    1962                         grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
    1963                     }
    1964                     outIsSpelling = false;
    1965                     outFirstFoundOffset = grammarPhraseOffset;
    1966                     firstFoundItem = badGrammarPhrase;
    1967                     break;
    1968                 }
    1969             }
    1970         }
    1971         if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
    1972             break;
    1973         VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition());
    1974         setStart(paragraphRange.get(), newParagraphStart);
    1975         setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart));
    1976         firstIteration = false;
    1977         totalLengthProcessed += currentLength;
    1978     }
    1979     return firstFoundItem;
    1980 }
    1981 
    1982 #endif
    1983 
    19841663void Editor::advanceToNextMisspelling(bool startBeforeSelection)
    19851664{
     
    19931672    VisibleSelection selection(frame()->selection()->selection());
    19941673    RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
     1674    TextCheckingHelper checker(client(), spellingSearchRange);
     1675
    19951676    bool startedWithSelection = false;
    19961677    if (selection.start().node()) {
     
    20581739    int foundOffset = 0;
    20591740    GrammarDetail grammarDetail;
    2060     String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
     1741    String foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    20611742    if (isSpelling) {
    20621743        misspelledWord = foundItem;
     
    20681749#else
    20691750    RefPtr<Range> firstMisspellingRange;
    2070     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
     1751    String misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    20711752    String badGrammarPhrase;
    20721753
     
    20851766   
    20861767    if (isGrammarCheckingEnabled())
    2087         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
     1768        badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    20881769#endif
    20891770#endif
     
    20981779#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    20991780        grammarSearchRange = spellingSearchRange->cloneRange(ec);
    2100         foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
     1781        foundItem = checker.findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    21011782        if (isSpelling) {
    21021783            misspelledWord = foundItem;
     
    21071788        }
    21081789#else
    2109         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
     1790        misspelledWord = checker.findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    21101791
    21111792#ifndef BUILDING_ON_TIGER
     
    21181799        }
    21191800        if (isGrammarCheckingEnabled())
    2120             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
     1801            badGrammarPhrase = checker.findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    21211802#endif
    21221803#endif
     
    21821863}
    21831864
    2184 #ifndef BUILDING_ON_TIGER
    2185 static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector)
    2186 {
    2187     if (!client)
    2188         return false;
    2189 
    2190     ExceptionCode ec;
    2191     if (!range || range->collapsed(ec))
    2192         return false;
    2193    
    2194     // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
    2195     // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
    2196     // or overlapping the range; the ranges must exactly match.
    2197     guessesVector.clear();
    2198     int grammarPhraseOffset;
    2199    
    2200     GrammarDetail grammarDetail;
    2201     String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false);   
    2202    
    2203     // No bad grammar in these parts at all.
    2204     if (badGrammarPhrase.isEmpty())
    2205         return false;
    2206    
    2207     // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
    2208     if (grammarPhraseOffset > 0)
    2209         return false;
    2210    
    2211     ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
    2212    
    2213     // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
    2214     if (grammarDetail.location + grammarPhraseOffset)
    2215         return false;
    2216    
    2217     // Bad grammar at start of range, but end of bad grammar is before or after end of range
    2218     if (grammarDetail.length != TextIterator::rangeLength(range))
    2219         return false;
    2220    
    2221     // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
    2222     // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
    2223     // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
    2224     // or a grammar error.
    2225     client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
    2226    
    2227     return true;
    2228 }
    2229 #endif
    2230 
    22311865bool Editor::isSelectionUngrammatical()
    22321866{
     
    22351869#else
    22361870    Vector<String> ignoredGuesses;
    2237     return isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), ignoredGuesses);
     1871    return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(ignoredGuesses);
    22381872#endif
    22391873}
     
    22451879#else
    22461880    Vector<String> guesses;
    2247     // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
    2248     isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), guesses);
     1881    // Ignore the result of isUngrammatical; we just want the guesses, whether or not there are any
     1882    TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).isUngrammatical(guesses);
    22491883    return guesses;
    22501884#endif
     
    22621896}
    22631897
     1898Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
     1899{
    22641900#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    2265 
    2266 static Vector<String> guessesForMisspelledOrUngrammaticalRange(EditorClient* client, Range *range, bool checkGrammar, bool& misspelled, bool& ungrammatical)
    2267 {
    2268     Vector<String> guesses;
    2269     ExceptionCode ec;
    2270     misspelled = false;
    2271     ungrammatical = false;
    2272    
    2273     if (!client || !range || range->collapsed(ec))
    2274         return guesses;
    2275 
    2276     // Expand the range to encompass entire paragraphs, since text checking needs that much context.
    2277     int rangeStartOffset;
    2278     String paragraphString;
    2279     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(range, rangeStartOffset, paragraphString);
    2280     int rangeLength = TextIterator::rangeLength(range);
    2281     if (!rangeLength || !paragraphString.length())
    2282         return guesses;
    2283 
    2284     Vector<TextCheckingResult> results;
    2285     uint64_t checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
    2286     client->checkTextOfParagraph(paragraphString.characters(), paragraphString.length(), checkingTypes, results);
    2287    
    2288     for (unsigned i = 0; i < results.size(); i++) {
    2289         const TextCheckingResult* result = &results[i];
    2290         if (result->type == TextCheckingTypeSpelling && result->location == rangeStartOffset && result->length == rangeLength) {
    2291             String misspelledWord = paragraphString.substring(rangeStartOffset, rangeLength);
    2292             ASSERT(misspelledWord.length());
    2293             client->getGuessesForWord(misspelledWord, guesses);
    2294             client->updateSpellingUIWithMisspelledWord(misspelledWord);
    2295             misspelled = true;
    2296             return guesses;
    2297         }
    2298     }
    2299    
    2300     if (!checkGrammar)
    2301         return guesses;
    2302        
    2303     for (unsigned i = 0; i < results.size(); i++) {
    2304         const TextCheckingResult* result = &results[i];
    2305         if (result->type == TextCheckingTypeGrammar && result->location <= rangeStartOffset && result->location + result->length >= rangeStartOffset + rangeLength) {
    2306             for (unsigned j = 0; j < result->details.size(); j++) {
    2307                 const GrammarDetail* detail = &result->details[j];
    2308                 ASSERT(detail->length > 0 && detail->location >= 0);
    2309                 if (result->location + detail->location == rangeStartOffset && detail->length == rangeLength) {
    2310                     String badGrammarPhrase = paragraphString.substring(result->location, result->length);
    2311                     ASSERT(badGrammarPhrase.length());
    2312                     for (unsigned k = 0; k < detail->guesses.size(); k++)
    2313                         guesses.append(detail->guesses[k]);
    2314                     client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail);
    2315                     ungrammatical = true;
    2316                     return guesses;
    2317                 }
    2318             }
    2319         }
    2320     }
    2321     return guesses;
    2322 }
    2323 
    2324 #endif
    2325 
    2326 Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
    2327 {
    2328 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    2329     return guessesForMisspelledOrUngrammaticalRange(client(), frame()->selection()->toNormalizedRange().get(), isGrammarCheckingEnabled(), misspelled, ungrammatical);
     1901    return TextCheckingHelper(client(), frame()->selection()->toNormalizedRange()).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical);
    23301902#else
    23311903    misspelled = isSelectionMisspelled();
     
    25042076#endif
    25052077}
    2506 
    2507 static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange, RefPtr<Range>& firstMisspellingRange)
    2508 {
    2509     // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
    2510     // all we need to do is mark every instance.
    2511     int ignoredOffset;
    2512     findFirstMisspellingInRange(client, searchRange, ignoredOffset, true, firstMisspellingRange);
    2513 }
    2514 
    2515 #ifndef BUILDING_ON_TIGER
    2516 static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
    2517 {
    2518     // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
    2519     // do is mark every instance.
    2520     GrammarDetail ignoredGrammarDetail;
    2521     int ignoredOffset;
    2522     findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true);
    2523 }
    2524 #endif
    2525    
    2526 static void markMisspellingsOrBadGrammar(Editor* editor, const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
     2078   
     2079void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, bool checkSpelling, RefPtr<Range>& firstMisspellingRange)
    25272080{
    25282081    // This function is called with a selection already expanded to word boundaries.
     
    25312084    // This function is used only for as-you-type checking, so if that's off we do nothing. Note that
    25322085    // grammar checking can only be on if spell checking is also on.
    2533     if (!editor->isContinuousSpellCheckingEnabled())
     2086    if (!isContinuousSpellCheckingEnabled())
    25342087        return;
    25352088   
     
    25432096        return;
    25442097
    2545     if (!editor->isSpellCheckingEnabledInFocusedNode())
     2098    if (!isSpellCheckingEnabledInFocusedNode())
    25462099        return;
    25472100
    25482101    // Get the spell checker if it is available
    2549     if (!editor->client())
    2550         return;
    2551    
     2102    if (!client())
     2103        return;
     2104   
     2105    TextCheckingHelper checker(client(), searchRange);
    25522106    if (checkSpelling)
    2553         markAllMisspellingsInRange(editor->client(), searchRange.get(), firstMisspellingRange);
     2107        checker.markAllMisspellings(firstMisspellingRange);
    25542108    else {
    25552109#ifdef BUILDING_ON_TIGER
    25562110        ASSERT_NOT_REACHED();
    25572111#else
    2558         if (editor->isGrammarCheckingEnabled())
    2559             markAllBadGrammarInRange(editor->client(), searchRange.get());
     2112        if (isGrammarCheckingEnabled())
     2113            checker.markAllBadGrammar();
    25602114#endif
    25612115    }   
     
    25772131void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
    25782132{
    2579     markMisspellingsOrBadGrammar(this, selection, true, firstMisspellingRange);
     2133    markMisspellingsOrBadGrammar(selection, true, firstMisspellingRange);
    25802134}
    25812135   
     
    25842138#ifndef BUILDING_ON_TIGER
    25852139    RefPtr<Range> firstMisspellingRange;
    2586     markMisspellingsOrBadGrammar(this, selection, false, firstMisspellingRange);
     2140    markMisspellingsOrBadGrammar(selection, false, firstMisspellingRange);
    25872141#else
    25882142    UNUSED_PARAM(selection);
     
    26342188    String paragraphString;
    26352189    RefPtr<Range> paragraphRange;
     2190    TextCheckingHelper checker(client(), grammarRange);
    26362191
    26372192    if (shouldMarkGrammar) {
    26382193        // The spelling range should be contained in the paragraph-aligned extension of the grammar range.
    2639         paragraphRange = paragraphAlignedRangeForRange(grammarRange, grammarRangeStartOffset, paragraphString);
     2194        paragraphRange = checker.paragraphAlignedRange(grammarRangeStartOffset, paragraphString);
    26402195        RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
    26412196        spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
    26422197        grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
    26432198    } else {
    2644         paragraphRange = paragraphAlignedRangeForRange(spellingRange, spellingRangeStartOffset, paragraphString);
     2199        paragraphRange = checker.paragraphAlignedRange(spellingRangeStartOffset, paragraphString);
    26452200    }
    26462201    spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
     
    28322387    if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
    28332388        return;
    2834        
     2389   
    28352390    String paragraphString;
    28362391    int selectionOffset;
    2837     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(selection.get(), selectionOffset, paragraphString);
     2392    RefPtr<Range> paragraphRange = TextCheckingHelper(client(), selection).paragraphAlignedRange(selectionOffset, paragraphString);
    28382393    replaceSelectionWithText(replacedString, false, false);
    28392394    RefPtr<Range> changedRange = TextIterator::subrange(paragraphRange.get(), selectionOffset, replacedString.length());
  • trunk/WebCore/editing/Editor.h

    r70826 r70847  
    405405    void writeSelectionToPasteboard(Pasteboard*);
    406406    void revealSelectionAfterEditingOperation();
     407    void markMisspellingsOrBadGrammar(const VisibleSelection&, bool checkSpelling, RefPtr<Range>& firstMisspellingRange);
    407408
    408409    void selectComposition();
Note: See TracChangeset for help on using the changeset viewer.