Changeset 71009 in webkit


Ignore:
Timestamp:
Oct 31, 2010 11:35:59 PM (13 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/EditingAllInOne.cpp
  • 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
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/CMakeLists.txt

    r70985 r71009  
    927927    editing/SplitTextNodeCommand.cpp
    928928    editing/SplitTextNodeContainingElementCommand.cpp
     929    editing/TextCheckingHelper.cpp
    929930    editing/TextIterator.cpp
    930931    editing/TypingCommand.cpp
  • trunk/WebCore/ChangeLog

    r71008 r71009  
     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/EditingAllInOne.cpp
     20        * editing/Editor.cpp:
     21        (WebCore::Editor::advanceToNextMisspelling):
     22        (WebCore::Editor::isSelectionUngrammatical):
     23        (WebCore::Editor::guessesForUngrammaticalSelection):
     24        (WebCore::Editor::guessesForMisspelledOrUngrammaticalSelection):
     25        (WebCore::Editor::markMisspellingsAfterTypingToPosition):
     26        (WebCore::Editor::markMisspellingsOrBadGrammar):
     27        (WebCore::Editor::markMisspellings):
     28        (WebCore::Editor::markBadGrammar):
     29        (WebCore::Editor::markAllMisspellingsAndBadGrammarInRanges):
     30        (WebCore::Editor::changeBackToReplacedString):
     31        * editing/Editor.h:
     32        * editing/TextCheckingHelper.cpp: Added.
     33        (WebCore::TextCheckingHelper::TextCheckingHelper):
     34        (WebCore::TextCheckingHelper::~TextCheckingHelper):
     35        (WebCore::TextCheckingHelper::paragraphAlignedRange):
     36        (WebCore::TextCheckingHelper::findFirstMisspelling):
     37        (WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar):
     38        (WebCore::TextCheckingHelper::findFirstGrammarDetail):
     39        (WebCore::TextCheckingHelper::findFirstBadGrammar):
     40        (WebCore::TextCheckingHelper::isUngrammatical):
     41        (WebCore::TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange):
     42        (WebCore::TextCheckingHelper::markAllMisspellings):
     43        (WebCore::TextCheckingHelper::markAllBadGrammar):
     44        * editing/TextCheckingHelper.h: Added.
     45
    1462010-10-31  Xan Lopez  <xlopez@igalia.com>
    247
  • trunk/WebCore/GNUmakefile.am

    r70985 r71009  
    13561356        WebCore/editing/SplitTextNodeContainingElementCommand.h \
    13571357        WebCore/editing/TextAffinity.h \
     1358        WebCore/editing/TextCheckingHelper.cpp \
     1359        WebCore/editing/TextCheckingHelper.h \
    13581360        WebCore/editing/TextGranularity.h \
    13591361        WebCore/editing/TextIterator.cpp \
  • trunk/WebCore/WebCore.gypi

    r70985 r71009  
    14381438            'editing/SplitTextNodeContainingElementCommand.h',
    14391439            'editing/TextAffinity.h',
     1440            'editing/TextCheckingHelper.cpp',
     1441            'editing/TextCheckingHelper.h',
    14401442            'editing/TextGranularity.h',
    14411443            'editing/TextIterator.cpp',
  • trunk/WebCore/WebCore.pro

    r70985 r71009  
    818818    editing/SplitTextNodeCommand.cpp \
    819819    editing/SplitTextNodeContainingElementCommand.cpp \
     820    editing/TextCheckingHelper.cpp \
    820821    editing/TextIterator.cpp \
    821822    editing/TypingCommand.cpp \
  • trunk/WebCore/WebCore.vcproj/WebCore.vcproj

    r70979 r71009  
    4794147941                        </File>
    4794247942                        <File
     47943                                RelativePath="..\editing\TextCheckingHelper.cpp"
     47944                                >
     47945                                <FileConfiguration
     47946                                        Name="Debug|Win32"
     47947                                        ExcludedFromBuild="true"
     47948                                        >
     47949                                        <Tool
     47950                                                Name="VCCLCompilerTool"
     47951                                        />
     47952                                </FileConfiguration>
     47953                                <FileConfiguration
     47954                                        Name="Release|Win32"
     47955                                        ExcludedFromBuild="true"
     47956                                        >
     47957                                        <Tool
     47958                                                Name="VCCLCompilerTool"
     47959                                        />
     47960                                </FileConfiguration>
     47961                                <FileConfiguration
     47962                                        Name="Debug_Internal|Win32"
     47963                                        ExcludedFromBuild="true"
     47964                                        >
     47965                                        <Tool
     47966                                                Name="VCCLCompilerTool"
     47967                                        />
     47968                                </FileConfiguration>
     47969                                <FileConfiguration
     47970                                        Name="Debug_Cairo|Win32"
     47971                                        ExcludedFromBuild="true"
     47972                                        >
     47973                                        <Tool
     47974                                                Name="VCCLCompilerTool"
     47975                                        />
     47976                                </FileConfiguration>
     47977                                <FileConfiguration
     47978                                        Name="Release_Cairo|Win32"
     47979                                        ExcludedFromBuild="true"
     47980                                        >
     47981                                        <Tool
     47982                                                Name="VCCLCompilerTool"
     47983                                        />
     47984                                </FileConfiguration>
     47985                                <FileConfiguration
     47986                                        Name="Debug_All|Win32"
     47987                                        ExcludedFromBuild="true"
     47988                                        >
     47989                                        <Tool
     47990                                                Name="VCCLCompilerTool"
     47991                                        />
     47992                                </FileConfiguration>
     47993                        </File>
     47994                        <File
     47995                                RelativePath="..\editing\TextCheckingHelper.h"
     47996                                >
     47997                        </File>
     47998                        <File
    4794347999                                RelativePath="..\editing\TextGranularity.h"
    4794448000                                >
  • trunk/WebCore/WebCore.xcodeproj/project.pbxproj

    r70991 r71009  
    30703070                A7D6B3490F61104500B79FD1 /* WorkerScriptLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D6B3470F61104500B79FD1 /* WorkerScriptLoader.h */; };
    30713071                A7D6B34A0F61104500B79FD1 /* WorkerScriptLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7D6B3480F61104500B79FD1 /* WorkerScriptLoader.cpp */; };
     3072                A7DBF8DD1276919C006B6008 /* TextCheckingHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */; };
     3073                A7DBF8DE1276919C006B6008 /* TextCheckingHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */; };
    30723074                A7F338A311C0EFCA00A320A7 /* ShadowElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7F338A111C0EFCA00A320A7 /* ShadowElement.cpp */; };
    30733075                A7F338A411C0EFCA00A320A7 /* ShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F338A211C0EFCA00A320A7 /* ShadowElement.h */; };
     
    91579159                A7D6B3470F61104500B79FD1 /* WorkerScriptLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WorkerScriptLoader.h; path = workers/WorkerScriptLoader.h; sourceTree = "<group>"; };
    91589160                A7D6B3480F61104500B79FD1 /* WorkerScriptLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WorkerScriptLoader.cpp; path = workers/WorkerScriptLoader.cpp; sourceTree = "<group>"; };
     9161                A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextCheckingHelper.cpp; sourceTree = "<group>"; };
     9162                A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCheckingHelper.h; sourceTree = "<group>"; };
    91599163                A7F338A111C0EFCA00A320A7 /* ShadowElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowElement.cpp; sourceTree = "<group>"; };
    91609164                A7F338A211C0EFCA00A320A7 /* ShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowElement.h; sourceTree = "<group>"; };
     
    1454714551                                93309DC8099E64910056E581 /* TextAffinity.h */,
    1454814552                                93309DC9099E64910056E581 /* TextGranularity.h */,
     14553                                A7DBF8DB1276919C006B6008 /* TextCheckingHelper.cpp */,
     14554                                A7DBF8DC1276919C006B6008 /* TextCheckingHelper.h */,
    1454914555                                93309DCC099E64910056E581 /* TextIterator.cpp */,
    1455014556                                93309DCD099E64910056E581 /* TextIterator.h */,
     
    2101321019                                B2C3DA360D006C1D00EF6F26 /* TextBreakIterator.h in Headers */,
    2101421020                                B2C3DA380D006C1D00EF6F26 /* TextBreakIteratorInternalICU.h in Headers */,
     21021                                A7DBF8DE1276919C006B6008 /* TextCheckingHelper.h in Headers */,
    2101521022                                B2C3DA3A0D006C1D00EF6F26 /* TextCodec.h in Headers */,
    2101621023                                B2C3DA3C0D006C1D00EF6F26 /* TextCodecICU.h in Headers */,
     
    2361423621                                B2C3DA370D006C1D00EF6F26 /* TextBreakIteratorICU.cpp in Sources */,
    2361523622                                B2AFFC980D00A5DF0030074D /* TextBreakIteratorInternalICUMac.mm in Sources */,
     23623                                A7DBF8DD1276919C006B6008 /* TextCheckingHelper.cpp in Sources */,
    2361623624                                B2C3DA390D006C1D00EF6F26 /* TextCodec.cpp in Sources */,
    2361723625                                B2C3DA3B0D006C1D00EF6F26 /* TextCodecICU.cpp in Sources */,
  • trunk/WebCore/editing/EditingAllInOne.cpp

    r70970 r71009  
    6666#include <SplitTextNodeCommand.cpp>
    6767#include <SplitTextNodeContainingElementCommand.cpp>
     68#include <TextCheckingHelper.cpp>
    6869#include <TextIterator.cpp>
    6970#include <TypingCommand.cpp>
  • trunk/WebCore/editing/Editor.cpp

    r70970 r71009  
    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
    19951675    bool startedWithSelection = false;
    19961676    if (selection.start().node()) {
     
    20581738    int foundOffset = 0;
    20591739    GrammarDetail grammarDetail;
    2060     String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
     1740    String foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    20611741    if (isSpelling) {
    20621742        misspelledWord = foundItem;
     
    20681748#else
    20691749    RefPtr<Range> firstMisspellingRange;
    2070     String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
     1750    String misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    20711751    String badGrammarPhrase;
    20721752
     
    20851765   
    20861766    if (isGrammarCheckingEnabled())
    2087         badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
     1767        badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    20881768#endif
    20891769#endif
     
    20981778#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    20991779        grammarSearchRange = spellingSearchRange->cloneRange(ec);
    2100         foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
     1780        foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
    21011781        if (isSpelling) {
    21021782            misspelledWord = foundItem;
     
    21071787        }
    21081788#else
    2109         misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false, firstMisspellingRange);
     1789        misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
    21101790
    21111791#ifndef BUILDING_ON_TIGER
     
    21171797            grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
    21181798        }
     1799
    21191800        if (isGrammarCheckingEnabled())
    2120             badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
     1801            badGrammarPhrase = TextCheckingHelper(client(), grammarSearchRange).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);
     
    26372191    if (shouldMarkGrammar) {
    26382192        // The spelling range should be contained in the paragraph-aligned extension of the grammar range.
    2639         paragraphRange = paragraphAlignedRangeForRange(grammarRange, grammarRangeStartOffset, paragraphString);
     2193        paragraphRange = TextCheckingHelper(client(), grammarRange).paragraphAlignedRange(grammarRangeStartOffset, paragraphString);
    26402194        RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
    26412195        spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
    26422196        grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
    26432197    } else {
    2644         paragraphRange = paragraphAlignedRangeForRange(spellingRange, spellingRangeStartOffset, paragraphString);
     2198        paragraphRange = TextCheckingHelper(client(), spellingRange).paragraphAlignedRange(spellingRangeStartOffset, paragraphString);
    26452199    }
    26462200    spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
     
    28322386    if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
    28332387        return;
    2834        
     2388   
    28352389    String paragraphString;
    28362390    int selectionOffset;
    2837     RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(selection.get(), selectionOffset, paragraphString);
     2391    RefPtr<Range> paragraphRange = TextCheckingHelper(client(), selection).paragraphAlignedRange(selectionOffset, paragraphString);
    28382392    replaceSelectionWithText(replacedString, false, false);
    28392393    RefPtr<Range> changedRange = TextIterator::subrange(paragraphRange.get(), selectionOffset, replacedString.length());
  • trunk/WebCore/editing/Editor.h

    r70970 r71009  
    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.