Changeset 235149 in webkit


Ignore:
Timestamp:
Aug 21, 2018, 5:45:55 PM (7 years ago)
Author:
dbates@webkit.org
Message:

[iOS][WK2] Misspelled words are not underlined
https://bugs.webkit.org/show_bug.cgi?id=188800
<rdar://problem/34811332>

Reviewed by Wenson Hsieh.

Implement enough of TextChecker for iOS to compute the list of misspelled words in a
paragraph and advertise support for continuous spell checking. The WebCore editing
machinery queries TextChecker for the list of the misspelled words, creating document
markers that demarcate the misspelled words. When we paint a line of text we paint
the spelling correction dots under each misspelled word.

On iOS we make use of UITextChecker to perform spell checking of a string. We maintain
a side table that maps a "spell document tag" to a UITextChecker* to conform to the
shape of the TextChecker interface.

  • Platform/spi/ios/UIKitSPI.h: Forward declare some SPI.
  • UIProcess/ios/TextCheckerIOS.mm:

(WebKit::mutableState): Added.
(WebKit::TextChecker::state): Turns around and returns mutableState().
(WebKit::TextChecker::isContinuousSpellCheckingAllowed): Returns true if we are building
with USE(UNIFIED_TEXT_CHECKING). Otherwise, do what we do now.
(WebKit::TextChecker::setContinuousSpellCheckingEnabled): Update state.
(WebKit::TextChecker::continuousSpellCheckingEnabledStateChanged): Ditto.
(WebKit::spellDocumentTagMap): Returns HashMap that maps a "spell document tag" (int64_t) to
a RetainPtr<UITextChecker>>.
(WebKit::TextChecker::uniqueSpellDocumentTag): Generates a unique identifier for the page
this text checker is associated with.
(WebKit::TextChecker::closeSpellDocumentWithTag): Removes the entry for the specified identifier
from the HashMap.
(WebKit::textCheckerFor): Query the HashMap for the UITextChecker for the specified spell
document tag, if it exists. Otherwise, create a new UITextChecker and add a new map entry
that associates it with the specified spell document tag.
(WebKit::TextChecker::checkTextOfParagraph): Spell check the specified string and return a list
that represents the misspelled words.
(WebKit::TextChecker::checkSpellingOfString): Added a comment to explain that iOS does not implement
this function and instead implements checkTextOfParagraph().
(WebKit::TextChecker::checkGrammarOfString): Ditto.

Location:
trunk/Source/WebKit
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r235148 r235149  
     12018-08-21  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS][WK2] Misspelled words are not underlined
     4        https://bugs.webkit.org/show_bug.cgi?id=188800
     5        <rdar://problem/34811332>
     6
     7        Reviewed by Wenson Hsieh.
     8
     9        Implement enough of TextChecker for iOS to compute the list of misspelled words in a
     10        paragraph and advertise support for continuous spell checking. The WebCore editing
     11        machinery queries TextChecker for the list of the misspelled words, creating document
     12        markers that demarcate the misspelled words. When we paint a line of text we paint
     13        the spelling correction dots under each misspelled word.
     14
     15        On iOS we make use of UITextChecker to perform spell checking of a string. We maintain
     16        a side table that maps a "spell document tag" to a UITextChecker* to conform to the
     17        shape of the TextChecker interface.
     18
     19        * Platform/spi/ios/UIKitSPI.h: Forward declare some SPI.
     20        * UIProcess/ios/TextCheckerIOS.mm:
     21        (WebKit::mutableState): Added.
     22        (WebKit::TextChecker::state): Turns around and returns mutableState().
     23        (WebKit::TextChecker::isContinuousSpellCheckingAllowed): Returns true if we are building
     24        with USE(UNIFIED_TEXT_CHECKING). Otherwise, do what we do now.
     25        (WebKit::TextChecker::setContinuousSpellCheckingEnabled): Update state.
     26        (WebKit::TextChecker::continuousSpellCheckingEnabledStateChanged): Ditto.
     27        (WebKit::spellDocumentTagMap): Returns HashMap that maps a "spell document tag" (int64_t) to
     28        a RetainPtr<UITextChecker>>.
     29        (WebKit::TextChecker::uniqueSpellDocumentTag): Generates a unique identifier for the page
     30        this text checker is associated with.
     31        (WebKit::TextChecker::closeSpellDocumentWithTag): Removes the entry for the specified identifier
     32        from the HashMap.
     33        (WebKit::textCheckerFor): Query the HashMap for the UITextChecker for the specified spell
     34        document tag, if it exists. Otherwise, create a new UITextChecker and add a new map entry
     35        that associates it with the specified spell document tag.
     36        (WebKit::TextChecker::checkTextOfParagraph): Spell check the specified string and return a list
     37        that represents the misspelled words.
     38        (WebKit::TextChecker::checkSpellingOfString): Added a comment to explain that iOS does not implement
     39        this function and instead implements checkTextOfParagraph().
     40        (WebKit::TextChecker::checkGrammarOfString): Ditto.
     41
    1422018-08-21  Alex Christensen  <achristensen@webkit.org>
    243
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r233905 r235149  
    4545#import <UIKit/UIInterface_Private.h>
    4646#import <UIKit/UIKeyboardImpl.h>
     47#import <UIKit/UIKeyboardInputModeController.h>
    4748#import <UIKit/UIKeyboardIntl.h>
    4849#import <UIKit/UIKeyboard_Private.h>
     
    5859#import <UIKit/UITableViewCell_Private.h>
    5960#import <UIKit/UITapGestureRecognizer_Private.h>
     61#import <UIKit/UITextChecker_Private.h>
    6062#import <UIKit/UITextEffectsWindow.h>
    6163#import <UIKit/UITextInput_Private.h>
     
    887889@end
    888890
     891@interface UITextChecker ()
     892- (id)_initWithAsynchronousLoading:(BOOL)asynchronousLoading;
     893- (BOOL)_doneLoading;
     894- (NSRange)rangeOfMisspelledWordInString:(NSString *)stringToCheck range:(NSRange)range startingAt:(NSInteger)startingOffset wrap:(BOOL)wrapFlag languages:(NSArray *)languagesArray;
     895@end
     896
     897@interface UIKeyboardInputMode : UITextInputMode <NSCopying>
     898@property (nonatomic, readonly, retain) NSArray <NSString *> *multilingualLanguages;
     899@property (nonatomic, readonly, retain) NSString *languageWithRegion;
     900@end
     901
     902@interface UIKeyboardInputModeController : NSObject
     903@end
     904
     905@interface UIKeyboardInputModeController ()
     906+ (UIKeyboardInputModeController *)sharedInputModeController;
     907@property (readwrite, retain) UIKeyboardInputMode *currentInputMode;
     908@end
     909
    889910#if ENABLE(DRAG_SUPPORT)
    890911
  • trunk/Source/WebKit/UIProcess/ios/TextCheckerIOS.mm

    r235120 r235149  
    3030
    3131#import "TextCheckerState.h"
     32#import "UIKitSPI.h"
    3233#import <WebCore/NotImplemented.h>
     34#import <wtf/HashMap.h>
     35#import <wtf/NeverDestroyed.h>
     36#import <wtf/RetainPtr.h>
    3337#import <wtf/text/StringView.h>
    3438
     
    3640
    3741namespace WebKit {
    38    
    39 TextCheckerState textCheckerState;
     42
     43static TextCheckerState& mutableState()
     44{
     45    static NeverDestroyed<TextCheckerState> state = makeNeverDestroyed([] {
     46        TextCheckerState initialState;
     47        initialState.isContinuousSpellCheckingEnabled = TextChecker::isContinuousSpellCheckingAllowed();
     48        initialState.isGrammarCheckingEnabled = false;
     49        return initialState;
     50    }());
     51    return state;
     52}
    4053
    4154const TextCheckerState& TextChecker::state()
    4255{
    43     notImplemented();
    44     return textCheckerState;
     56    return mutableState();
    4557}
    4658
    4759bool TextChecker::isContinuousSpellCheckingAllowed()
    4860{
    49     notImplemented();
     61#if USE(UNIFIED_TEXT_CHECKING)
     62    return true;
     63#else
    5064    return false;
    51 }
    52 
    53 void TextChecker::setContinuousSpellCheckingEnabled(bool)
    54 {
    55     notImplemented();
     65#endif
     66}
     67
     68void TextChecker::setContinuousSpellCheckingEnabled(bool enabled)
     69{
     70    mutableState().isContinuousSpellCheckingEnabled = enabled;
    5671}
    5772
     
    123138}
    124139
    125 void TextChecker::continuousSpellCheckingEnabledStateChanged(bool)
    126 {
    127     notImplemented();
     140void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled)
     141{
     142    mutableState().isContinuousSpellCheckingEnabled = enabled;
    128143}
    129144
     
    133148}
    134149
     150#if USE(UNIFIED_TEXT_CHECKING)
     151
     152static HashMap<int64_t, RetainPtr<UITextChecker>>& spellDocumentTagMap()
     153{
     154    static NeverDestroyed<HashMap<int64_t, RetainPtr<UITextChecker>>> tagMap;
     155    return tagMap;
     156}
     157
     158#endif
     159
    135160int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy*)
    136161{
    137     notImplemented();
     162#if USE(UNIFIED_TEXT_CHECKING)
     163    static int64_t nextSpellDocumentTag;
     164    return ++nextSpellDocumentTag;
     165#else
    138166    return 0;
    139 }
    140 
    141 void TextChecker::closeSpellDocumentWithTag(int64_t)
    142 {
    143     notImplemented();
    144 }
    145 
    146 #if USE(UNIFIED_TEXT_CHECKING)
    147 
    148 Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t, StringView, int32_t, OptionSet<TextCheckingType>, bool)
    149 {
    150     notImplemented();
    151     return Vector<TextCheckingResult>();
     167#endif
     168}
     169
     170void TextChecker::closeSpellDocumentWithTag(int64_t spellDocumentTag)
     171{
     172#if USE(UNIFIED_TEXT_CHECKING)
     173    spellDocumentTagMap().remove(spellDocumentTag);
     174#else
     175    UNUSED_PARAM(spellDocumentTag);
     176#endif
     177}
     178
     179#if USE(UNIFIED_TEXT_CHECKING)
     180
     181static RetainPtr<UITextChecker> textCheckerFor(int64_t spellDocumentTag)
     182{
     183    auto addResult = spellDocumentTagMap().add(spellDocumentTag, nullptr);
     184    if (addResult.isNewEntry)
     185        addResult.iterator->value = adoptNS([[UITextChecker alloc] _initWithAsynchronousLoading:YES]);
     186    return addResult.iterator->value;
     187}
     188
     189Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, StringView text, int32_t /* insertionPoint */, OptionSet<TextCheckingType> checkingTypes, bool /* initialCapitalizationEnabled */)
     190{
     191    Vector<TextCheckingResult> results;
     192    if (!checkingTypes.contains(TextCheckingType::Spelling))
     193        return results;
     194
     195    auto textChecker = textCheckerFor(spellDocumentTag);
     196    if (![textChecker _doneLoading])
     197        return results;
     198
     199    NSArray<NSString *> *keyboardLanguages = @[ ];
     200    auto *currentInputMode = [UIKeyboardInputModeController sharedInputModeController].currentInputMode;
     201    if (currentInputMode.multilingualLanguages.count)
     202        keyboardLanguages = currentInputMode.multilingualLanguages;
     203    else if (currentInputMode.primaryLanguage)
     204        keyboardLanguages = @[ currentInputMode.languageWithRegion ];
     205
     206    auto stringToCheck = text.createNSStringWithoutCopying();
     207    auto range = NSMakeRange(0, [stringToCheck length]);
     208    NSUInteger offsetSoFar = 0;
     209    do {
     210        auto misspelledRange = [textChecker rangeOfMisspelledWordInString:stringToCheck.get() range:range startingAt:offsetSoFar wrap:NO languages:keyboardLanguages];
     211        if (misspelledRange.location == NSNotFound)
     212            break;
     213
     214        TextCheckingResult result;
     215        result.type = TextCheckingType::Spelling;
     216        result.location = misspelledRange.location;
     217        result.length = misspelledRange.length;
     218        results.append(WTFMove(result));
     219
     220        offsetSoFar = misspelledRange.location + misspelledRange.length;
     221    } while (offsetSoFar < [stringToCheck length]);
     222    return results;
    152223}
    153224
     
    156227void TextChecker::checkSpellingOfString(int64_t, StringView, int32_t&, int32_t&)
    157228{
     229    // iOS uses checkTextOfParagraph() instead.
    158230    notImplemented();
    159231}
     
    161233void TextChecker::checkGrammarOfString(int64_t, StringView, Vector<WebCore::GrammarDetail>&, int32_t&, int32_t&)
    162234{
     235    // iOS uses checkTextOfParagraph() instead.
    163236    notImplemented();
    164237}
Note: See TracChangeset for help on using the changeset viewer.