Changeset 182603 in webkit


Ignore:
Timestamp:
Apr 9, 2015 11:31:51 AM (9 years ago)
Author:
Chris Dumez
Message:

[WK2][iOS] editorState() should not cause a synchronous layout
https://bugs.webkit.org/show_bug.cgi?id=142536
<rdar://problem/20041506>

Reviewed by Enrica Casucci.

Source/WebCore:

Add didChangeSelectionAndUpdateLayout() callback to EditorClient
that is called at the end of FrameSelection::updateAndRevealSelection().

  • editing/FrameSelection.cpp:

(WebCore::FrameSelection::updateAndRevealSelection):

  • loader/EmptyClients.h:
  • page/EditorClient.h:

Source/WebKit/mac:

Provide implementation for EditorClient::didChangeSelectionAndUpdateLayout().

  • WebCoreSupport/WebEditorClient.h:

Source/WebKit/win:

Provide implementation for EditorClient::didChangeSelectionAndUpdateLayout().

  • WebCoreSupport/WebEditorClient.h:

Source/WebKit2:

platformEditorState() on iOS does a synchronous layout to compute some
of the EditorState members (e.g. caretRectAtStart / caretRectAtEnd).
This is bad for performance as this is called every time the selection
is changed (which happens for e.g. when you set the value of a focused
HTMLInputElement).

This patch updates the behavior on iOS to only send a partial EditorState
on selection change so that the UIProcess gets most of the information
(the ones that do not require style recalc or layout) ASAP. A full Editor
state is then sent after the asynchronous layout is done.

With this change, I see a 38% improvement on Speedometer (26.4 +/- 0.37
-> 36.5 +/- 0.54) on iPhone 6 Plus.

  • Shared/EditorState.cpp:

(WebKit::EditorState::encode):
(WebKit::EditorState::decode):
(WebKit::EditorState::PostLayoutData::encode):
(WebKit::EditorState::PostLayoutData::decode):

  • Shared/EditorState.h:

(WebKit::EditorState::EditorState): Deleted.

  • UIProcess/ios/WKContentView.mm:

(-[WKContentView _didCommitLayerTree:]):

  • UIProcess/ios/WKContentViewInteraction.mm:

(WebKit::WKSelectionDrawingInfo::WKSelectionDrawingInfo):
(-[WKContentView webSelectionRects]):
(-[WKContentView _addShortcut:]):
(-[WKContentView selectedText]):
(-[WKContentView isReplaceAllowed]):
(-[WKContentView _promptForReplace:]):
(-[WKContentView _transliterateChinese:]):
(-[WKContentView textStylingAtPosition:inDirection:]):
(-[WKContentView canPerformAction:withSender:]):
(-[WKContentView _showDictionary:]):
(-[WKContentView _characterBeforeCaretSelection]):
(-[WKContentView _characterInRelationToCaretSelection:]):
(-[WKContentView _selectionAtDocumentStart]):
(-[WKContentView selectedTextRange]):
(-[WKContentView hasContent]):

  • WebProcess/WebCoreSupport/WebEditorClient.cpp:

(WebKit::WebEditorClient::didChangeSelectionAndUpdateLayout):

  • WebProcess/WebCoreSupport/WebEditorClient.h:
  • WebProcess/WebPage/WebPage.cpp:

(WebKit::WebPage::editorState):
(WebKit::WebPage::didChangeSelection):
(WebKit::WebPage::sendPostLayoutEditorStateIfNeeded):

  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/efl/WebPageEfl.cpp:

(WebKit::WebPage::platformEditorState):

  • WebProcess/WebPage/gtk/WebPageGtk.cpp:

(WebKit::WebPage::platformEditorState):

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::platformEditorState):

  • WebProcess/WebPage/mac/WebPageMac.mm:

(WebKit::WebPage::platformEditorState):

Location:
trunk/Source
Files:
21 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r182601 r182603  
     12015-04-09  Chris Dumez  <cdumez@apple.com>
     2
     3        [WK2][iOS] editorState() should not cause a synchronous layout
     4        https://bugs.webkit.org/show_bug.cgi?id=142536
     5        <rdar://problem/20041506>
     6
     7        Reviewed by Enrica Casucci.
     8
     9        Add didChangeSelectionAndUpdateLayout() callback to EditorClient
     10        that is called at the end of FrameSelection::updateAndRevealSelection().
     11
     12        * editing/FrameSelection.cpp:
     13        (WebCore::FrameSelection::updateAndRevealSelection):
     14        * loader/EmptyClients.h:
     15        * page/EditorClient.h:
     16
    1172015-04-08  Anders Carlsson  <andersca@apple.com>
    218
  • trunk/Source/WebCore/editing/FrameSelection.cpp

    r181166 r182603  
    382382
    383383    notifyAccessibilityForSelectionChange();
     384
     385    m_frame->editor().client()->didChangeSelectionAndUpdateLayout();
    384386}
    385387
  • trunk/Source/WebCore/loader/EmptyClients.h

    r182573 r182603  
    447447    virtual void respondToChangedContents() override { }
    448448    virtual void respondToChangedSelection(Frame*) override { }
     449    virtual void didChangeSelectionAndUpdateLayout() override { }
    449450    virtual void discardedComposition(Frame*) override { }
    450451    virtual void didEndEditing() override { }
  • trunk/Source/WebCore/page/EditorClient.h

    r177152 r182603  
    9494    virtual void respondToChangedContents() = 0;
    9595    virtual void respondToChangedSelection(Frame*) = 0;
     96    virtual void didChangeSelectionAndUpdateLayout() = 0;
    9697    virtual void didEndEditing() = 0;
    9798    virtual void willWriteSelectionToPasteboard(Range*) = 0;
  • trunk/Source/WebKit/mac/ChangeLog

    r182581 r182603  
     12015-04-09  Chris Dumez  <cdumez@apple.com>
     2
     3        [WK2][iOS] editorState() should not cause a synchronous layout
     4        https://bugs.webkit.org/show_bug.cgi?id=142536
     5        <rdar://problem/20041506>
     6
     7        Reviewed by Enrica Casucci.
     8
     9        Provide implementation for EditorClient::didChangeSelectionAndUpdateLayout().
     10
     11        * WebCoreSupport/WebEditorClient.h:
     12
    1132015-04-08  Brent Fulgham  <bfulgham@apple.com>
    214
  • trunk/Source/WebKit/mac/WebCoreSupport/WebEditorClient.h

    r177152 r182603  
    111111    virtual void respondToChangedContents() override;
    112112    virtual void respondToChangedSelection(WebCore::Frame*) override;
     113    virtual void didChangeSelectionAndUpdateLayout() override { }
    113114    virtual void discardedComposition(WebCore::Frame*) override;
    114115
  • trunk/Source/WebKit/win/ChangeLog

    r182573 r182603  
     12015-04-09  Chris Dumez  <cdumez@apple.com>
     2
     3        [WK2][iOS] editorState() should not cause a synchronous layout
     4        https://bugs.webkit.org/show_bug.cgi?id=142536
     5        <rdar://problem/20041506>
     6
     7        Reviewed by Enrica Casucci.
     8
     9        Provide implementation for EditorClient::didChangeSelectionAndUpdateLayout().
     10
     11        * WebCoreSupport/WebEditorClient.h:
     12
    1132015-04-08  Brady Eidson  <beidson@apple.com>
    214
  • trunk/Source/WebKit/win/WebCoreSupport/WebEditorClient.h

    r177152 r182603  
    6161    virtual void respondToChangedContents();
    6262    virtual void respondToChangedSelection(WebCore::Frame*);
     63    virtual void didChangeSelectionAndUpdateLayout() override { }
    6364    virtual void discardedComposition(WebCore::Frame*) override;
    6465
  • trunk/Source/WebKit2/ChangeLog

    r182602 r182603  
     12015-04-09  Chris Dumez  <cdumez@apple.com>
     2
     3        [WK2][iOS] editorState() should not cause a synchronous layout
     4        https://bugs.webkit.org/show_bug.cgi?id=142536
     5        <rdar://problem/20041506>
     6
     7        Reviewed by Enrica Casucci.
     8
     9        platformEditorState() on iOS does a synchronous layout to compute some
     10        of the EditorState members (e.g. caretRectAtStart / caretRectAtEnd).
     11        This is bad for performance as this is called every time the selection
     12        is changed (which happens for e.g. when you set the value of a focused
     13        HTMLInputElement).
     14
     15        This patch updates the behavior on iOS to only send a partial EditorState
     16        on selection change so that the UIProcess gets most of the information
     17        (the ones that do not require style recalc or layout) ASAP. A full Editor
     18        state is then sent after the asynchronous layout is done.
     19
     20        With this change, I see a 38% improvement on Speedometer (26.4 +/- 0.37
     21        -> 36.5 +/- 0.54) on iPhone 6 Plus.
     22
     23        * Shared/EditorState.cpp:
     24        (WebKit::EditorState::encode):
     25        (WebKit::EditorState::decode):
     26        (WebKit::EditorState::PostLayoutData::encode):
     27        (WebKit::EditorState::PostLayoutData::decode):
     28        * Shared/EditorState.h:
     29        (WebKit::EditorState::EditorState): Deleted.
     30        * UIProcess/ios/WKContentView.mm:
     31        (-[WKContentView _didCommitLayerTree:]):
     32        * UIProcess/ios/WKContentViewInteraction.mm:
     33        (WebKit::WKSelectionDrawingInfo::WKSelectionDrawingInfo):
     34        (-[WKContentView webSelectionRects]):
     35        (-[WKContentView _addShortcut:]):
     36        (-[WKContentView selectedText]):
     37        (-[WKContentView isReplaceAllowed]):
     38        (-[WKContentView _promptForReplace:]):
     39        (-[WKContentView _transliterateChinese:]):
     40        (-[WKContentView textStylingAtPosition:inDirection:]):
     41        (-[WKContentView canPerformAction:withSender:]):
     42        (-[WKContentView _showDictionary:]):
     43        (-[WKContentView _characterBeforeCaretSelection]):
     44        (-[WKContentView _characterInRelationToCaretSelection:]):
     45        (-[WKContentView _selectionAtDocumentStart]):
     46        (-[WKContentView selectedTextRange]):
     47        (-[WKContentView hasContent]):
     48        * WebProcess/WebCoreSupport/WebEditorClient.cpp:
     49        (WebKit::WebEditorClient::didChangeSelectionAndUpdateLayout):
     50        * WebProcess/WebCoreSupport/WebEditorClient.h:
     51        * WebProcess/WebPage/WebPage.cpp:
     52        (WebKit::WebPage::editorState):
     53        (WebKit::WebPage::didChangeSelection):
     54        (WebKit::WebPage::sendPostLayoutEditorStateIfNeeded):
     55        * WebProcess/WebPage/WebPage.h:
     56        * WebProcess/WebPage/efl/WebPageEfl.cpp:
     57        (WebKit::WebPage::platformEditorState):
     58        * WebProcess/WebPage/gtk/WebPageGtk.cpp:
     59        (WebKit::WebPage::platformEditorState):
     60        * WebProcess/WebPage/ios/WebPageIOS.mm:
     61        (WebKit::WebPage::platformEditorState):
     62        * WebProcess/WebPage/mac/WebPageMac.mm:
     63        (WebKit::WebPage::platformEditorState):
     64
    1652015-04-09  Antti Koivisto  <antti@apple.com>
    266
  • trunk/Source/WebKit2/Shared/EditorState.cpp

    r180768 r182603  
    4242    encoder << isInPlugin;
    4343    encoder << hasComposition;
     44    encoder << isMissingPostLayoutData;
    4445
    4546#if PLATFORM(IOS)
    46     encoder << isReplaceAllowed;
    47     encoder << hasContent;
    48     encoder << characterAfterSelection;
    49     encoder << characterBeforeSelection;
    50     encoder << twoCharacterBeforeSelection;
    51     encoder << caretRectAtStart;
    52     encoder << caretRectAtEnd;
    53     encoder << selectionRects;
    54     encoder << selectedTextLength;
    55     encoder << wordAtSelection;
     47    if (!isMissingPostLayoutData)
     48        m_postLayoutData.encode(encoder);
     49
    5650    encoder << firstMarkedRect;
    5751    encoder << lastMarkedRect;
    5852    encoder << markedText;
    59     encoder << typingAttributes;
    6053#endif
    6154
     
    9184        return false;
    9285
     86    if (!decoder.decode(result.isMissingPostLayoutData))
     87        return false;
     88
    9389#if PLATFORM(IOS)
    94     if (!decoder.decode(result.isReplaceAllowed))
    95         return false;
    96     if (!decoder.decode(result.hasContent))
    97         return false;
    98     if (!decoder.decode(result.characterAfterSelection))
    99         return false;
    100     if (!decoder.decode(result.characterBeforeSelection))
    101         return false;
    102     if (!decoder.decode(result.twoCharacterBeforeSelection))
    103         return false;
    104     if (!decoder.decode(result.caretRectAtStart))
    105         return false;
    106     if (!decoder.decode(result.caretRectAtEnd))
    107         return false;
    108     if (!decoder.decode(result.selectionRects))
    109         return false;
    110     if (!decoder.decode(result.selectedTextLength))
    111         return false;
    112     if (!decoder.decode(result.wordAtSelection))
    113         return false;
     90    if (!result.isMissingPostLayoutData) {
     91        if (!PostLayoutData::decode(decoder, result.postLayoutData()))
     92            return false;
     93    }
     94
    11495    if (!decoder.decode(result.firstMarkedRect))
    11596        return false;
     
    11798        return false;
    11899    if (!decoder.decode(result.markedText))
    119         return false;
    120     if (!decoder.decode(result.typingAttributes))
    121100        return false;
    122101#endif
     
    130109}
    131110
     111#if PLATFORM(IOS)
     112void EditorState::PostLayoutData::encode(IPC::ArgumentEncoder& encoder) const
     113{
     114    encoder << selectionRects;
     115    encoder << caretRectAtStart;
     116    encoder << caretRectAtEnd;
     117    encoder << wordAtSelection;
     118    encoder << selectedTextLength;
     119    encoder << characterAfterSelection;
     120    encoder << characterBeforeSelection;
     121    encoder << twoCharacterBeforeSelection;
     122    encoder << typingAttributes;
     123    encoder << isReplaceAllowed;
     124    encoder << hasContent;
    132125}
     126
     127bool EditorState::PostLayoutData::decode(IPC::ArgumentDecoder& decoder, PostLayoutData& result)
     128{
     129    if (!decoder.decode(result.selectionRects))
     130        return false;
     131    if (!decoder.decode(result.caretRectAtStart))
     132        return false;
     133    if (!decoder.decode(result.caretRectAtEnd))
     134        return false;
     135    if (!decoder.decode(result.wordAtSelection))
     136        return false;
     137    if (!decoder.decode(result.selectedTextLength))
     138        return false;
     139    if (!decoder.decode(result.characterAfterSelection))
     140        return false;
     141    if (!decoder.decode(result.characterBeforeSelection))
     142        return false;
     143    if (!decoder.decode(result.twoCharacterBeforeSelection))
     144        return false;
     145    if (!decoder.decode(result.typingAttributes))
     146        return false;
     147    if (!decoder.decode(result.isReplaceAllowed))
     148        return false;
     149    if (!decoder.decode(result.hasContent))
     150        return false;
     151
     152    return true;
     153}
     154#endif
     155
     156}
  • trunk/Source/WebKit2/Shared/EditorState.h

    r180768 r182603  
    4545
    4646struct EditorState {
    47     EditorState()
    48         : shouldIgnoreCompositionSelectionChange(false)
    49         , selectionIsNone(true)
    50         , selectionIsRange(false)
    51         , isContentEditable(false)
    52         , isContentRichlyEditable(false)
    53         , isInPasswordField(false)
    54         , isInPlugin(false)
    55         , hasComposition(false)
    56 #if PLATFORM(IOS)
    57         , isReplaceAllowed(false)
    58         , hasContent(false)
    59         , characterAfterSelection(0)
    60         , characterBeforeSelection(0)
    61         , twoCharacterBeforeSelection(0)
    62         , selectedTextLength(0)
    63         , typingAttributes(AttributeNone)
    64 #endif
    65     {
    66     }
     47    bool shouldIgnoreCompositionSelectionChange { false };
    6748
    68     bool shouldIgnoreCompositionSelectionChange;
    69 
    70     bool selectionIsNone; // This will be false when there is a caret selection.
    71     bool selectionIsRange;
    72     bool isContentEditable;
    73     bool isContentRichlyEditable;
    74     bool isInPasswordField;
    75     bool isInPlugin;
    76     bool hasComposition;
     49    bool selectionIsNone { true }; // This will be false when there is a caret selection.
     50    bool selectionIsRange { false };
     51    bool isContentEditable { false };
     52    bool isContentRichlyEditable { false };
     53    bool isInPasswordField { false };
     54    bool isInPlugin { false };
     55    bool hasComposition { false };
     56    bool isMissingPostLayoutData { false };
    7757
    7858#if PLATFORM(IOS)
    79     bool isReplaceAllowed;
    80     bool hasContent;
    81     UChar32 characterAfterSelection;
    82     UChar32 characterBeforeSelection;
    83     UChar32 twoCharacterBeforeSelection;
    84     WebCore::IntRect caretRectAtStart;
    85     WebCore::IntRect caretRectAtEnd;
    86     Vector<WebCore::SelectionRect> selectionRects;
    87     uint64_t selectedTextLength;
    88     String wordAtSelection;
    8959    WebCore::IntRect firstMarkedRect;
    9060    WebCore::IntRect lastMarkedRect;
    9161    String markedText;
    92     uint32_t typingAttributes;
     62
     63    struct PostLayoutData {
     64        Vector<WebCore::SelectionRect> selectionRects;
     65        WebCore::IntRect caretRectAtStart;
     66        WebCore::IntRect caretRectAtEnd;
     67        String wordAtSelection;
     68        uint64_t selectedTextLength { 0 };
     69        UChar32 characterAfterSelection { 0 };
     70        UChar32 characterBeforeSelection { 0 };
     71        UChar32 twoCharacterBeforeSelection { 0 };
     72        uint32_t typingAttributes { AttributeNone };
     73        bool isReplaceAllowed { false };
     74        bool hasContent { false };
     75
     76        void encode(IPC::ArgumentEncoder&) const;
     77        static bool decode(IPC::ArgumentDecoder&, PostLayoutData&);
     78    };
     79
     80    const PostLayoutData& postLayoutData() const;
     81    PostLayoutData& postLayoutData();
    9382#endif
    9483
     
    9988    void encode(IPC::ArgumentEncoder&) const;
    10089    static bool decode(IPC::ArgumentDecoder&, EditorState&);
     90
     91#if PLATFORM(IOS)
     92private:
     93    PostLayoutData m_postLayoutData;
     94#endif
    10195};
     96
     97#if PLATFORM(IOS)
     98inline auto EditorState::postLayoutData() -> PostLayoutData&
     99{
     100    ASSERT_WITH_MESSAGE(!isMissingPostLayoutData, "Attempt to access post layout data before receiving it");
     101    return m_postLayoutData;
     102}
     103
     104inline auto EditorState::postLayoutData() const -> const PostLayoutData&
     105{
     106    ASSERT_WITH_MESSAGE(!isMissingPostLayoutData, "Attempt to access post layout data before receiving it");
     107    return m_postLayoutData;
     108}
     109#endif
    102110
    103111}
  • trunk/Source/WebKit2/UIProcess/ios/WKContentView.mm

    r180539 r182603  
    491491    }
    492492   
    493     [self _updateChangedSelection];
     493    // Updating the selection requires a full editor state. If the editor state is missing post layout
     494    // data then it means there is a layout pending and we're going to be called again after the layout
     495    // so we delay the selection update.
     496    if (!_page->editorState().isMissingPostLayoutData)
     497        [self _updateChangedSelection];
    494498}
    495499
  • trunk/Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm

    r182126 r182603  
    8686
    8787    type = SelectionType::Range;
    88     caretRect = editorState.caretRectAtEnd;
    89     selectionRects = editorState.selectionRects;
     88    auto& postLayoutData = editorState.postLayoutData();
     89    caretRect = postLayoutData.caretRectAtEnd;
     90    selectionRects = postLayoutData.selectionRects;
    9091}
    9192
     
    987988- (NSArray *)webSelectionRects
    988989{
    989     unsigned size = _page->editorState().selectionRects.size();
     990    const auto& selectionRects = _page->editorState().postLayoutData().selectionRects;
     991    unsigned size = selectionRects.size();
    990992    if (!size)
    991993        return nil;
     
    993995    NSMutableArray *webRects = [NSMutableArray arrayWithCapacity:size];
    994996    for (unsigned i = 0; i < size; i++) {
    995         const WebCore::SelectionRect& coreRect = _page->editorState().selectionRects[i];
     997        const WebCore::SelectionRect& coreRect = selectionRects[i];
    996998        WebSelectionRect *webRect = [WebSelectionRect selectionRect];
    997999        webRect.rect = coreRect.rect();
     
    12781280{
    12791281    if (_textSelectionAssistant && [_textSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
    1280         [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().selectionRects[0].rect()];
     1282        [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
    12811283    else if (_webSelectionAssistant && [_webSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
    1282         [_webSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().selectionRects[0].rect()];
     1284        [_webSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
    12831285}
    12841286
    12851287- (NSString *)selectedText
    12861288{
    1287     return (NSString *)_page->editorState().wordAtSelection;
     1289    return (NSString *)_page->editorState().postLayoutData().wordAtSelection;
    12881290}
    12891291
    12901292- (BOOL)isReplaceAllowed
    12911293{
    1292     return _page->editorState().isReplaceAllowed;
     1294    return _page->editorState().postLayoutData().isReplaceAllowed;
    12931295}
    12941296
     
    13051307- (void)_promptForReplace:(id)sender
    13061308{
    1307     if (_page->editorState().wordAtSelection.isEmpty())
     1309    const auto& wordAtSelection = _page->editorState().postLayoutData().wordAtSelection;
     1310    if (wordAtSelection.isEmpty())
    13081311        return;
    13091312
    13101313    if ([_textSelectionAssistant respondsToSelector:@selector(scheduleReplacementsForText:)])
    1311         [_textSelectionAssistant scheduleReplacementsForText:_page->editorState().wordAtSelection];
     1314        [_textSelectionAssistant scheduleReplacementsForText:wordAtSelection];
    13121315}
    13131316
     
    13151318{
    13161319    if ([_textSelectionAssistant respondsToSelector:@selector(scheduleChineseTransliterationForText:)])
    1317         [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().wordAtSelection];
     1320        [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().postLayoutData().wordAtSelection];
    13181321}
    13191322
     
    13351338    NSMutableDictionary* result = [NSMutableDictionary dictionary];
    13361339
     1340    auto typingAttributes = _page->editorState().postLayoutData().typingAttributes;
    13371341    CTFontSymbolicTraits symbolicTraits = 0;
    1338     if (_page->editorState().typingAttributes & AttributeBold)
     1342    if (typingAttributes & AttributeBold)
    13391343        symbolicTraits |= kCTFontBoldTrait;
    1340     if (_page->editorState().typingAttributes & AttributeItalics)
     1344    if (typingAttributes & AttributeItalics)
    13411345        symbolicTraits |= kCTFontTraitItalic;
    13421346
     
    13521356        [result setObject:(id)font.get() forKey:NSFontAttributeName];
    13531357   
    1354     if (_page->editorState().typingAttributes & AttributeUnderline)
     1358    if (typingAttributes & AttributeUnderline)
    13551359        [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
    13561360
     
    13901394            return NO;
    13911395
    1392         NSUInteger textLength = _page->editorState().selectedTextLength;
     1396        NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
    13931397        // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
    13941398        // acceptable, but the interface takes a string.
     
    14191423
    14201424    if (action == @selector(_promptForReplace:)) {
    1421         if (!_page->editorState().selectionIsRange || !_page->editorState().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
     1425        if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
    14221426            return NO;
    14231427        if ([[self selectedText] _containsCJScriptsOnly])
     
    14271431
    14281432    if (action == @selector(_transliterateChinese:)) {
    1429         if (!_page->editorState().selectionIsRange || !_page->editorState().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
     1433        if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
    14301434            return NO;
    14311435        return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
     
    14331437
    14341438    if (action == @selector(_reanalyze:)) {
    1435         if (!_page->editorState().selectionIsRange || !_page->editorState().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
     1439        if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
    14361440            return NO;
    14371441        return UIKeyboardCurrentInputModeAllowsChineseOrJapaneseReanalysisForText([self selectedText]);
     
    15311535- (void)_showDictionary:(NSString *)text
    15321536{
    1533     CGRect presentationRect = _page->editorState().selectionRects[0].rect();
     1537    CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
    15341538    if (_textSelectionAssistant)
    15351539        [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
     
    19381942- (UTF32Char)_characterBeforeCaretSelection
    19391943{
    1940     return _page->editorState().characterBeforeSelection;
     1944    return _page->editorState().postLayoutData().characterBeforeSelection;
    19411945}
    19421946
     
    19451949    switch (amount) {
    19461950    case 0:
    1947         return _page->editorState().characterAfterSelection;
     1951        return _page->editorState().postLayoutData().characterAfterSelection;
    19481952    case -1:
    1949         return _page->editorState().characterBeforeSelection;
     1953        return _page->editorState().postLayoutData().characterBeforeSelection;
    19501954    case -2:
    1951         return _page->editorState().twoCharacterBeforeSelection;
     1955        return _page->editorState().postLayoutData().twoCharacterBeforeSelection;
    19521956    default:
    19531957        return 0;
     
    19571961- (BOOL)_selectionAtDocumentStart
    19581962{
    1959     return !_page->editorState().characterBeforeSelection;
     1963    return !_page->editorState().postLayoutData().characterBeforeSelection;
    19601964}
    19611965
     
    21312135- (UITextRange *)selectedTextRange
    21322136{
    2133     FloatRect startRect = _page->editorState().caretRectAtStart;
    2134     FloatRect endRect = _page->editorState().caretRectAtEnd;
     2137    auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
     2138    FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
     2139    FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
    21352140    double inverseScale = [self inverseScale];
    21362141    // We want to keep the original caret width, while the height scales with
     
    21602165                                   endRect:endRect
    21612166                            selectionRects:[self webSelectionRects]
    2162                         selectedTextLength:_page->editorState().selectedTextLength];
     2167                        selectedTextLength:postLayoutEditorStateData.selectedTextLength];
    21632168}
    21642169
     
    26812686- (BOOL)hasContent
    26822687{
    2683     return _page->editorState().hasContent;
     2688    return _page->editorState().postLayoutData().hasContent;
    26842689}
    26852690
  • trunk/Source/WebKit2/WebProcess/WebCoreSupport/WebEditorClient.cpp

    r179409 r182603  
    191191}
    192192
     193void WebEditorClient::didChangeSelectionAndUpdateLayout()
     194{
     195    m_page->sendPostLayoutEditorStateIfNeeded();
     196}
     197
    193198void WebEditorClient::discardedComposition(Frame*)
    194199{
  • trunk/Source/WebKit2/WebProcess/WebCoreSupport/WebEditorClient.h

    r177152 r182603  
    6565    virtual void respondToChangedContents() override;
    6666    virtual void respondToChangedSelection(WebCore::Frame*) override;
     67    virtual void didChangeSelectionAndUpdateLayout() override;
    6768    virtual void discardedComposition(WebCore::Frame*) override;
    6869    virtual void didEndEditing() override;
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp

    r182581 r182603  
    740740#endif
    741741
    742 EditorState WebPage::editorState() const
     742EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayoutData) const
    743743{
    744744    Frame& frame = m_page->focusController().focusedOrMainFrame();
     
    765765    result.shouldIgnoreCompositionSelectionChange = frame.editor().ignoreCompositionSelectionChange();
    766766   
    767     platformEditorState(frame, result);
     767    platformEditorState(frame, result, shouldIncludePostLayoutData);
    768768
    769769    return result;
     
    43664366void WebPage::didChangeSelection()
    43674367{
     4368    Frame& frame = m_page->focusController().focusedOrMainFrame();
     4369    FrameView* view = frame.view();
     4370    bool needsLayout = view && view->needsLayout();
     4371
     4372    // If there is a layout pending, we should avoid populating EditorState that require layout to be done or it will
     4373    // trigger a synchronous layout every time the selection changes. sendPostLayoutEditorStateIfNeeded() will be called
     4374    // to send the full editor state after layout is done if we send a partial editor state here.
     4375    auto editorState = this->editorState(needsLayout ? IncludePostLayoutDataHint::No : IncludePostLayoutDataHint::Yes);
     4376    ASSERT_WITH_MESSAGE(needsLayout == (view && view->needsLayout()), "Calling editorState() should not cause a synchronous layout.");
     4377    m_isEditorStateMissingPostLayoutData = editorState.isMissingPostLayoutData;
     4378
    43684379#if PLATFORM(MAC) && USE(ASYNC_NSTEXTINPUTCLIENT)
    4369     Frame& frame = m_page->focusController().focusedOrMainFrame();
    43704380    // Abandon the current inline input session if selection changed for any other reason but an input method direct action.
    43714381    // FIXME: This logic should be in WebCore.
     
    43744384    if (frame.editor().hasComposition() && !frame.editor().ignoreCompositionSelectionChange() && !frame.selection().isNone()) {
    43754385        frame.editor().cancelComposition();
    4376         send(Messages::WebPageProxy::CompositionWasCanceled(editorState()));
     4386        send(Messages::WebPageProxy::CompositionWasCanceled(editorState));
    43774387    } else
    4378         send(Messages::WebPageProxy::EditorStateChanged(editorState()));
     4388        send(Messages::WebPageProxy::EditorStateChanged(editorState));
    43794389#else
    4380     send(Messages::WebPageProxy::EditorStateChanged(editorState()), pageID(), IPC::DispatchMessageEvenWhenWaitingForSyncReply);
     4390    send(Messages::WebPageProxy::EditorStateChanged(editorState), pageID(), IPC::DispatchMessageEvenWhenWaitingForSyncReply);
    43814391#endif
    43824392
     
    43844394    m_drawingArea->scheduleCompositingLayerFlush();
    43854395#endif
     4396}
     4397
     4398void WebPage::sendPostLayoutEditorStateIfNeeded()
     4399{
     4400    if (!m_isEditorStateMissingPostLayoutData)
     4401        return;
     4402
     4403    send(Messages::WebPageProxy::EditorStateChanged(editorState(IncludePostLayoutDataHint::Yes)), pageID(), IPC::DispatchMessageEvenWhenWaitingForSyncReply);
     4404    m_isEditorStateMissingPostLayoutData = false;
    43864405}
    43874406
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h

    r182556 r182603  
    328328#endif // ENABLE(WEBGL)
    329329   
    330     EditorState editorState() const;
     330    enum class IncludePostLayoutDataHint { No, Yes };
     331    EditorState editorState(IncludePostLayoutDataHint = IncludePostLayoutDataHint::Yes) const;
     332    void sendPostLayoutEditorStateIfNeeded();
    331333
    332334    String renderTreeExternalRepresentation() const;
     
    879881    void platformInitialize();
    880882    void platformDetach();
    881     void platformEditorState(WebCore::Frame&, EditorState& result) const;
     883    void platformEditorState(WebCore::Frame&, EditorState& result, IncludePostLayoutDataHint) const;
    882884
    883885    void didReceiveWebPageMessage(IPC::Connection&, IPC::MessageDecoder&);
     
    13541356    bool m_mainFrameProgressCompleted;
    13551357    bool m_shouldDispatchFakeMouseMoveEvents;
     1358    bool m_isEditorStateMissingPostLayoutData { false };
    13561359};
    13571360
  • trunk/Source/WebKit2/WebProcess/WebPage/efl/WebPageEfl.cpp

    r180465 r182603  
    8383}
    8484
    85 void WebPage::platformEditorState(Frame&, EditorState&) const
     85void WebPage::platformEditorState(Frame&, EditorState&, IncludePostLayoutDataHint) const
    8686{
    8787}
  • trunk/Source/WebKit2/WebProcess/WebPage/gtk/WebPageGtk.cpp

    r180465 r182603  
    6969}
    7070
    71 void WebPage::platformEditorState(Frame& frame, EditorState& result) const
     71void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint) const
    7272{
    7373    result.cursorRect = frame.selection().absoluteCaretBounds();
  • trunk/Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm

    r182162 r182603  
    129129}
    130130
    131 void WebPage::platformEditorState(Frame& frame, EditorState& result) const
    132 {
    133     const VisibleSelection& selection = frame.selection().selection();
     131void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
     132{
    134133    if (frame.editor().hasComposition()) {
    135134        RefPtr<Range> compositionRange = frame.editor().compositionRange();
     
    146145        }
    147146    }
     147
     148    // We only set the remaining EditorState entries if the layout is done. To compute these
     149    // entries, we need the layout to be done and we don't want to trigger a synchronous
     150    // layout as this would be bad for performance. If we have a composition, we send everything
     151    // right away as the UIProcess needs the caretRects ASAP for marked text.
     152    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No && !frame.editor().hasComposition()) {
     153        result.isMissingPostLayoutData = true;
     154        return;
     155    }
     156
     157    auto& postLayoutData = result.postLayoutData();
    148158    FrameView* view = frame.view();
     159    const VisibleSelection& selection = frame.selection().selection();
    149160    if (selection.isCaret()) {
    150         result.caretRectAtStart = view->contentsToRootView(frame.selection().absoluteCaretBounds());
    151         result.caretRectAtEnd = result.caretRectAtStart;
     161        postLayoutData.caretRectAtStart = view->contentsToRootView(frame.selection().absoluteCaretBounds());
     162        postLayoutData.caretRectAtEnd = postLayoutData.caretRectAtStart;
    152163        // FIXME: The following check should take into account writing direction.
    153         result.isReplaceAllowed = result.isContentEditable && atBoundaryOfGranularity(selection.start(), WordGranularity, DirectionForward);
    154         result.wordAtSelection = plainTextReplacingNoBreakSpace(wordRangeFromPosition(selection.start()).get());
     164        postLayoutData.isReplaceAllowed = result.isContentEditable && atBoundaryOfGranularity(selection.start(), WordGranularity, DirectionForward);
     165        postLayoutData.wordAtSelection = plainTextReplacingNoBreakSpace(wordRangeFromPosition(selection.start()).get());
    155166        if (selection.isContentEditable()) {
    156             charactersAroundPosition(selection.start(), result.characterAfterSelection, result.characterBeforeSelection, result.twoCharacterBeforeSelection);
     167            charactersAroundPosition(selection.start(), postLayoutData.characterAfterSelection, postLayoutData.characterBeforeSelection, postLayoutData.twoCharacterBeforeSelection);
    157168            Node* root = selection.rootEditableElement();
    158             result.hasContent = root && root->hasChildNodes() && !isEndOfEditableOrNonEditableContent(firstPositionInNode(root));
     169            postLayoutData.hasContent = root && root->hasChildNodes() && !isEndOfEditableOrNonEditableContent(firstPositionInNode(root));
    159170        }
    160171    } else if (selection.isRange()) {
    161         result.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds());
    162         result.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds());
     172        postLayoutData.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds());
     173        postLayoutData.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds());
    163174        RefPtr<Range> selectedRange = selection.toNormalizedRange();
    164175        String selectedText;
    165176        if (selectedRange) {
    166             selectedRange->collectSelectionRects(result.selectionRects);
    167             convertSelectionRectsToRootView(view, result.selectionRects);
     177            selectedRange->collectSelectionRects(postLayoutData.selectionRects);
     178            convertSelectionRectsToRootView(view, postLayoutData.selectionRects);
    168179            selectedText = plainTextReplacingNoBreakSpace(selectedRange.get(), TextIteratorDefaultBehavior, true);
    169             result.selectedTextLength = selectedText.length();
     180            postLayoutData.selectedTextLength = selectedText.length();
    170181            const int maxSelectedTextLength = 200;
    171182            if (selectedText.length() <= maxSelectedTextLength)
    172                 result.wordAtSelection = selectedText;
     183                postLayoutData.wordAtSelection = selectedText;
    173184        }
    174185        // FIXME: We should disallow replace when the string contains only CJ characters.
    175         result.isReplaceAllowed = result.isContentEditable && !result.isInPasswordField && !selectedText.containsOnlyWhitespace();
     186        postLayoutData.isReplaceAllowed = result.isContentEditable && !result.isInPasswordField && !selectedText.containsOnlyWhitespace();
    176187    }
    177188    if (!selection.isNone()) {
     
    182193           
    183194            if (traits & kCTFontTraitBold)
    184                 result.typingAttributes |= AttributeBold;
     195                postLayoutData.typingAttributes |= AttributeBold;
    185196            if (traits & kCTFontTraitItalic)
    186                 result.typingAttributes |= AttributeItalics;
     197                postLayoutData.typingAttributes |= AttributeItalics;
    187198           
    188199            RefPtr<EditingStyle> typingStyle = frame.selection().typingStyle();
     
    190201                String value = typingStyle->style()->getPropertyValue(CSSPropertyWebkitTextDecorationsInEffect);
    191202                if (value.contains("underline"))
    192                     result.typingAttributes |= AttributeUnderline;
     203                    postLayoutData.typingAttributes |= AttributeUnderline;
    193204            } else {
    194205                if (style->textDecorationsInEffect() & TextDecorationUnderline)
    195                     result.typingAttributes |= AttributeUnderline;
     206                    postLayoutData.typingAttributes |= AttributeUnderline;
    196207            }
    197208           
  • trunk/Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm

    r182573 r182603  
    123123}
    124124
    125 void WebPage::platformEditorState(Frame& frame, EditorState& result) const
     125void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint) const
    126126{
    127127}
Note: See TracChangeset for help on using the changeset viewer.