Changeset 220393 in webkit


Ignore:
Timestamp:
Aug 8, 2017 1:10:23 AM (7 years ago)
Author:
Wenson Hsieh
Message:

[iOS WK2] WKWebView schedules nonstop layout after pressing cmb+b,i,u inside a contenteditable div
https://bugs.webkit.org/show_bug.cgi?id=175116
<rdar://problem/28279301>

Reviewed by Darin Adler and Ryosuke Niwa.

Source/WebCore:

WebCore support for WebPage::editorState refactoring. See WebKit ChangeLogs for more detail.

Tests: EditorStateTests.TypingAttributesBold

EditorStateTests.TypingAttributesItalic
EditorStateTests.TypingAttributesUnderline
EditorStateTests.TypingAttributesTextAlignmentAbsoluteAlignmentOptions
EditorStateTests.TypingAttributesTextAlignmentStartEnd
EditorStateTests.TypingAttributesTextAlignmentDirectionalText
EditorStateTests.TypingAttributesTextColor
EditorStateTests.TypingAttributesMixedStyles
EditorStateTests.TypingAttributesLinkColor

  • css/StyleProperties.cpp:

(WebCore::StyleProperties::propertyAsColor const):
(WebCore::StyleProperties::propertyAsValueID const):

Introduces some helper functions in StyleProperties to convert CSS property values to Color or a CSSValueID.

  • css/StyleProperties.h:
  • editing/EditingStyle.cpp:

(WebCore::EditingStyle::hasStyle):

Pull out logic in selectionStartHasStyle that asks for a style TriState into EditingStyle::hasStyle. This is
because WebPage::editorState will now query for multiple styles at the selection start, but
selectionStartHasStyle currently recomputes styleAtSelectionStart every time it is called. To prevent extra work
from being done, we can just call selectionStartHasStyle once and use ask for EditingStyle::hasStyle on the
computed EditingStyle at selection start.

  • editing/EditingStyle.h:
  • editing/Editor.cpp:

(WebCore::Editor::selectionStartHasStyle const):

Source/WebKit:

Refactors WebPage::editorState to only use the StyleProperties derived from EditingStyle, instead of inserting
and removing a temporary node to figure out the style. Also adds hooks to notify the UI delegate of EditorState
changes.

  • UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
  • UIProcess/API/Cocoa/WKWebView.mm:

(nsTextAlignment):
(dictionaryRepresentationForEditorState):
(-[WKWebView _didChangeEditorState]):

Alerts the private UI delegate of UI-side EditorState updates.

(-[WKWebView _web_editorStateDidChange]):
(-[WKWebView _executeEditCommand:argument:completion:]):

  • UIProcess/API/Cocoa/WKWebViewInternal.h:
  • UIProcess/API/Cocoa/WKWebViewPrivate.h:
  • UIProcess/API/mac/WKView.mm:

(-[WKView _web_editorStateDidChange]):

  • UIProcess/Cocoa/WebViewImpl.h:
  • UIProcess/Cocoa/WebViewImpl.mm:

(WebKit::WebViewImpl::selectionDidChange):

  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::executeEditCommand):

Change executeEditCommand(name, callback) to executeEditCommand(name, argument, callback) and lift out of iOS
platform code and into WebPage.cpp.

  • UIProcess/WebPageProxy.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView executeEditCommandWithCallback:]):
(-[WKContentView _selectionChanged]):

  • UIProcess/ios/WebPageProxyIOS.mm:

(WebKit::WebPageProxy::executeEditCommand): Deleted.

Move the iOS-specific implementation of executeEditCommand that invokes a callback when the web process responds
out of WebPageProxyIOS, and into cross-platform WebPageProxy code. Additionally, add a parameter for the edit
command's argument.

  • WebProcess/WebPage/WebPage.cpp:

(WebKit::WebPage::editorState const):

Use EditingStyle::styleAtSelectionStart instead of Editor::styleForSelectionStart when computing an EditorState.
Tweak bold, italic and underline to use EditingStyle TriStates.

(WebKit::shouldEnsureEditorStateUpdateAfterExecutingCommand):
(WebKit::WebPage::executeEditCommandWithCallback):

  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/WebPage.messages.in:
  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::executeEditCommandWithCallback): Deleted.

Tools:

Introduces new testing infrastructure and API tests to test EditorState updates in the UI process. The new
EditorStateTests run on both iOS and Mac.

  • TestWebKitAPI/EditingTestHarness.h: Added.
  • TestWebKitAPI/EditingTestHarness.mm: Added.

EditingTestHarness is a helper object that API tests may use to apply editing commands and store EditorState
history. This test harness adds sugaring around various editing commands, and simplifies the process of checking
the state of the latest observed EditorState.

(-[EditingTestHarness initWithWebView:]):
(-[EditingTestHarness dealloc]):
(-[EditingTestHarness webView]):
(-[EditingTestHarness latestEditorState]):
(-[EditingTestHarness editorStateHistory]):
(-[EditingTestHarness insertText:andExpectEditorStateWith:]):
(-[EditingTestHarness insertHTML:andExpectEditorStateWith:]):
(-[EditingTestHarness selectAllAndExpectEditorStateWith:]):
(-[EditingTestHarness moveBackwardAndExpectEditorStateWith:]):
(-[EditingTestHarness moveWordBackwardAndExpectEditorStateWith:]):
(-[EditingTestHarness toggleBold]):
(-[EditingTestHarness toggleItalic]):
(-[EditingTestHarness toggleUnderline]):
(-[EditingTestHarness setForegroundColor:]):
(-[EditingTestHarness alignJustifiedAndExpectEditorStateWith:]):
(-[EditingTestHarness alignLeftAndExpectEditorStateWith:]):
(-[EditingTestHarness alignCenterAndExpectEditorStateWith:]):
(-[EditingTestHarness alignRightAndExpectEditorStateWith:]):
(-[EditingTestHarness insertParagraphAndExpectEditorStateWith:]):
(-[EditingTestHarness deleteBackwardAndExpectEditorStateWith:]):
(-[EditingTestHarness _execCommand:argument:expectEntries:]):

Dispatches an editing command to the web process, and blocks until a response is received. If an expected
entries dictionary is given, this will additionally verify that the latest EditorState contains all the expected
keys and values.

(-[EditingTestHarness latestEditorStateContains:]):
(-[EditingTestHarness _webView:editorStateDidChange:]):

  • TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
  • TestWebKitAPI/Tests/WebKit2Cocoa/EditorStateTests.mm: Added.

(TestWebKitAPI::setUpEditorStateTestHarness):
(TestWebKitAPI::TEST):

  • TestWebKitAPI/Tests/WebKit2Cocoa/editor-state-test-harness.html: Added.

LayoutTests:

Rebaseline some iOS WK2 LayoutTest expectations. These tests currently expect an empty anonymous RenderBlock to
be inserted into the render tree, but this is only a result of us adding and removing a temporary <span> when
computing a RenderStyle in WebPage::editorState -- this patch removes these empty RenderBlocks, making these
expectations' RenderTrees consistent with WebKit1.

  • platform/ios-wk2/editing/inserting/insert-div-024-expected.txt:
  • platform/ios-wk2/editing/inserting/insert-div-026-expected.txt:
  • platform/ios-wk2/editing/style/5084241-expected.txt:
  • platform/ios-wk2/editing/style/unbold-in-bold-expected.txt:
Location:
trunk
Files:
4 added
29 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r220375 r220393  
     12017-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS WK2] WKWebView schedules nonstop layout after pressing cmb+b,i,u inside a contenteditable div
     4        https://bugs.webkit.org/show_bug.cgi?id=175116
     5        <rdar://problem/28279301>
     6
     7        Reviewed by Darin Adler and Ryosuke Niwa.
     8
     9        Rebaseline some iOS WK2 LayoutTest expectations. These tests currently expect an empty anonymous RenderBlock to
     10        be inserted into the render tree, but this is only a result of us adding and removing a temporary <span> when
     11        computing a RenderStyle in WebPage::editorState -- this patch removes these empty RenderBlocks, making these
     12        expectations' RenderTrees consistent with WebKit1.
     13
     14        * platform/ios-wk2/editing/inserting/insert-div-024-expected.txt:
     15        * platform/ios-wk2/editing/inserting/insert-div-026-expected.txt:
     16        * platform/ios-wk2/editing/style/5084241-expected.txt:
     17        * platform/ios-wk2/editing/style/unbold-in-bold-expected.txt:
     18
    1192017-08-07  Matt Lewis  <jlewis3@apple.com>
    220
  • trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-024-expected.txt

    r193911 r220393  
    6565        RenderText {#text} at (14,15) size 36x28
    6666          text run at (14,15) width 36: "xxx"
    67       RenderBlock (anonymous) at (0,320) size 784x0
    6867      RenderBlock {P} at (0,320) size 784x58 [border: (2px solid #0000FF)]
    6968        RenderBR {BR} at (14,15) size 0x28 [bgcolor=#008000]
  • trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-026-expected.txt

    r193911 r220393  
    5555          RenderText {#text} at (21,3) size 13x28
    5656            text run at (21,3) width 13: "x"
    57         RenderBlock (anonymous) at (0,34) size 784x0
    5857caret: position 3 of child 0 {#text} of child 0 {B} of child 1 {DIV} of child 3 {DIV} of body
  • trunk/LayoutTests/platform/ios-wk2/editing/style/5084241-expected.txt

    r177519 r220393  
    1515          RenderText {#text} at (150,0) size 159x19
    1616            text run at (150,0) width 159: "This text should be blue."
    17       RenderBlock (anonymous) at (0,76) size 784x0
    1817caret: position 25 of child 0 {#text} of child 1 {FONT} of child 2 {DIV} of body
  • trunk/LayoutTests/platform/ios-wk2/editing/style/unbold-in-bold-expected.txt

    r193911 r220393  
    8787            text run at (170,15) width 72: "xxxxxx"
    8888        RenderInline {SPAN} at (0,0) size 0x28
    89       RenderBlock (anonymous) at (0,58) size 784x0
    9089selection start: position 0 of child 1 {#text} of child 1 {DIV} of body
    9190selection end:   position 7 of child 1 {#text} of child 1 {DIV} of body
  • trunk/Source/WebCore/ChangeLog

    r220392 r220393  
     12017-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS WK2] WKWebView schedules nonstop layout after pressing cmb+b,i,u inside a contenteditable div
     4        https://bugs.webkit.org/show_bug.cgi?id=175116
     5        <rdar://problem/28279301>
     6
     7        Reviewed by Darin Adler and Ryosuke Niwa.
     8
     9        WebCore support for WebPage::editorState refactoring. See WebKit ChangeLogs for more detail.
     10
     11        Tests: EditorStateTests.TypingAttributesBold
     12               EditorStateTests.TypingAttributesItalic
     13               EditorStateTests.TypingAttributesUnderline
     14               EditorStateTests.TypingAttributesTextAlignmentAbsoluteAlignmentOptions
     15               EditorStateTests.TypingAttributesTextAlignmentStartEnd
     16               EditorStateTests.TypingAttributesTextAlignmentDirectionalText
     17               EditorStateTests.TypingAttributesTextColor
     18               EditorStateTests.TypingAttributesMixedStyles
     19               EditorStateTests.TypingAttributesLinkColor
     20
     21        * css/StyleProperties.cpp:
     22        (WebCore::StyleProperties::propertyAsColor const):
     23        (WebCore::StyleProperties::propertyAsValueID const):
     24
     25        Introduces some helper functions in StyleProperties to convert CSS property values to Color or a CSSValueID.
     26
     27        * css/StyleProperties.h:
     28        * editing/EditingStyle.cpp:
     29        (WebCore::EditingStyle::hasStyle):
     30
     31        Pull out logic in selectionStartHasStyle that asks for a style TriState into EditingStyle::hasStyle. This is
     32        because WebPage::editorState will now query for multiple styles at the selection start, but
     33        selectionStartHasStyle currently recomputes styleAtSelectionStart every time it is called. To prevent extra work
     34        from being done, we can just call selectionStartHasStyle once and use ask for EditingStyle::hasStyle on the
     35        computed EditingStyle at selection start.
     36
     37        * editing/EditingStyle.h:
     38        * editing/Editor.cpp:
     39        (WebCore::Editor::selectionStartHasStyle const):
     40
    1412017-08-08  Zan Dobersek  <zdobersek@igalia.com>
    242
  • trunk/Source/WebCore/css/StyleProperties.cpp

    r218890 r220393  
    3232#include "CSSValueList.h"
    3333#include "CSSValuePool.h"
     34#include "Color.h"
    3435#include "Document.h"
    3536#include "PropertySetCSSStyleDeclaration.h"
     
    240241}
    241242
     243std::optional<Color> StyleProperties::propertyAsColor(CSSPropertyID property) const
     244{
     245    auto colorValue = getPropertyCSSValue(property);
     246    if (!is<CSSPrimitiveValue>(colorValue.get()))
     247        return std::nullopt;
     248
     249    auto& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
     250    return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColor(colorValue->cssText());
     251}
     252
     253CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const
     254{
     255    auto cssValue = getPropertyCSSValue(property);
     256    return is<CSSPrimitiveValue>(cssValue.get()) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid;
     257}
     258
    242259String StyleProperties::getCustomPropertyValue(const String& propertyName) const
    243260{
  • trunk/Source/WebCore/css/StyleProperties.h

    r218588 r220393  
    3737class CSSStyleDeclaration;
    3838class CachedResource;
     39class Color;
    3940class ImmutableStyleProperties;
    4041class URL;
     
    113114    WEBCORE_EXPORT RefPtr<CSSValue> getPropertyCSSValue(CSSPropertyID) const;
    114115    WEBCORE_EXPORT String getPropertyValue(CSSPropertyID) const;
     116
     117    WEBCORE_EXPORT std::optional<Color> propertyAsColor(CSSPropertyID) const;
     118    WEBCORE_EXPORT CSSValueID propertyAsValueID(CSSPropertyID) const;
     119
    115120    bool propertyIsImportant(CSSPropertyID) const;
    116121    String getPropertyShorthand(CSSPropertyID) const;
  • trunk/Source/WebCore/editing/EditingStyle.cpp

    r219595 r220393  
    14141414}
    14151415
     1416bool EditingStyle::hasStyle(CSSPropertyID propertyID, const String& value)
     1417{
     1418    return EditingStyle::create(propertyID, value)->triStateOfStyle(this) != FalseTriState;
     1419}
     1420
    14161421RefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
    14171422{
  • trunk/Source/WebCore/editing/EditingStyle.h

    r216128 r220393  
    166166    TextDecorationChange strikeThroughChange() const { return static_cast<TextDecorationChange>(m_strikeThroughChange); }
    167167
     168    WEBCORE_EXPORT bool hasStyle(CSSPropertyID, const String& value);
    168169    WEBCORE_EXPORT static RefPtr<EditingStyle> styleAtSelectionStart(const VisibleSelection&, bool shouldUseBackgroundColorInEffect = false);
    169170    static WritingDirection textDirectionForSelection(const VisibleSelection&, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings);
  • trunk/Source/WebCore/editing/Editor.cpp

    r219685 r220393  
    917917bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const
    918918{
    919     return EditingStyle::create(propertyID, value)->triStateOfStyle(
    920         EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor).get());
     919    if (auto editingStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor))
     920        return editingStyle->hasStyle(propertyID, value);
     921    return false;
    921922}
    922923
  • trunk/Source/WebKit/ChangeLog

    r220391 r220393  
     12017-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS WK2] WKWebView schedules nonstop layout after pressing cmb+b,i,u inside a contenteditable div
     4        https://bugs.webkit.org/show_bug.cgi?id=175116
     5        <rdar://problem/28279301>
     6
     7        Reviewed by Darin Adler and Ryosuke Niwa.
     8
     9        Refactors WebPage::editorState to only use the StyleProperties derived from EditingStyle, instead of inserting
     10        and removing a temporary node to figure out the style. Also adds hooks to notify the UI delegate of EditorState
     11        changes.
     12
     13        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
     14        * UIProcess/API/Cocoa/WKWebView.mm:
     15        (nsTextAlignment):
     16        (dictionaryRepresentationForEditorState):
     17        (-[WKWebView _didChangeEditorState]):
     18
     19        Alerts the private UI delegate of UI-side EditorState updates.
     20
     21        (-[WKWebView _web_editorStateDidChange]):
     22        (-[WKWebView _executeEditCommand:argument:completion:]):
     23        * UIProcess/API/Cocoa/WKWebViewInternal.h:
     24        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
     25        * UIProcess/API/mac/WKView.mm:
     26        (-[WKView _web_editorStateDidChange]):
     27        * UIProcess/Cocoa/WebViewImpl.h:
     28        * UIProcess/Cocoa/WebViewImpl.mm:
     29        (WebKit::WebViewImpl::selectionDidChange):
     30        * UIProcess/WebPageProxy.cpp:
     31        (WebKit::WebPageProxy::executeEditCommand):
     32
     33        Change executeEditCommand(name, callback) to executeEditCommand(name, argument, callback) and lift out of iOS
     34        platform code and into WebPage.cpp.
     35
     36        * UIProcess/WebPageProxy.h:
     37        * UIProcess/ios/WKContentViewInteraction.mm:
     38        (-[WKContentView executeEditCommandWithCallback:]):
     39        (-[WKContentView _selectionChanged]):
     40        * UIProcess/ios/WebPageProxyIOS.mm:
     41        (WebKit::WebPageProxy::executeEditCommand): Deleted.
     42
     43        Move the iOS-specific implementation of executeEditCommand that invokes a callback when the web process responds
     44        out of WebPageProxyIOS, and into cross-platform WebPageProxy code. Additionally, add a parameter for the edit
     45        command's argument.
     46
     47        * WebProcess/WebPage/WebPage.cpp:
     48        (WebKit::WebPage::editorState const):
     49
     50        Use EditingStyle::styleAtSelectionStart instead of Editor::styleForSelectionStart when computing an EditorState.
     51        Tweak bold, italic and underline to use EditingStyle TriStates.
     52
     53        (WebKit::shouldEnsureEditorStateUpdateAfterExecutingCommand):
     54        (WebKit::WebPage::executeEditCommandWithCallback):
     55        * WebProcess/WebPage/WebPage.h:
     56        * WebProcess/WebPage/WebPage.messages.in:
     57        * WebProcess/WebPage/ios/WebPageIOS.mm:
     58        (WebKit::WebPage::executeEditCommandWithCallback): Deleted.
     59
    1602017-08-08  Zan Dobersek  <zdobersek@igalia.com>
    261
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h

    r219224 r220393  
    7979
    8080- (void)_webView:(WKWebView *)webView runBeforeUnloadConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
     81- (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
    8182
    8283#if TARGET_OS_IPHONE
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm

    r220286 r220393  
    10841084
    10851085    _page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
     1086}
     1087
     1088static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
     1089{
     1090    switch (alignment) {
     1091    case WebKit::NoAlignment:
     1092        return NSTextAlignmentNatural;
     1093    case WebKit::LeftAlignment:
     1094        return NSTextAlignmentLeft;
     1095    case WebKit::RightAlignment:
     1096        return NSTextAlignmentRight;
     1097    case WebKit::CenterAlignment:
     1098        return NSTextAlignmentCenter;
     1099    case WebKit::JustifiedAlignment:
     1100        return NSTextAlignmentJustified;
     1101    }
     1102    ASSERT_NOT_REACHED();
     1103    return NSTextAlignmentNatural;
     1104}
     1105
     1106static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
     1107{
     1108    if (state.isMissingPostLayoutData)
     1109        return @{ @"post-layout-data" : @NO };
     1110
     1111    auto& postLayoutData = state.postLayoutData();
     1112    return @{
     1113        @"post-layout-data" : @YES,
     1114        @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
     1115        @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
     1116        @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
     1117        @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
     1118        @"text-color": (NSString *)postLayoutData.textColor.cssText()
     1119    };
     1120}
     1121
     1122- (void)_didChangeEditorState
     1123{
     1124    id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
     1125    if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
     1126        [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
    10861127}
    10871128
     
    36403681}
    36413682
     3683- (void)_web_editorStateDidChange
     3684{
     3685    [self _didChangeEditorState];
     3686}
     3687
    36423688- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
    36433689{
     
    56565702}
    56575703
     5704- (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion
     5705{
     5706    _page->executeEditCommand(command, argument, [capturedCompletionBlock = makeBlockPtr(completion)](WebKit::CallbackBase::Error error) {
     5707        capturedCompletionBlock(error == WebKit::CallbackBase::Error::None);
     5708    });
     5709}
     5710
    56585711#if PLATFORM(IOS)
    56595712
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h

    r220286 r220393  
    120120- (void)_hidePasswordView;
    121121
     122- (void)_didChangeEditorState;
     123
    122124- (void)_addShortcut:(id)sender;
    123125- (void)_arrowKey:(id)sender;
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h

    r219768 r220393  
    408408
    409409- (void)_disableBackForwardSnapshotVolatilityForTesting WK_API_AVAILABLE(macosx(10.12.3), ios(10.3));
     410- (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
    410411
    411412@end
  • trunk/Source/WebKit/UIProcess/API/mac/WKView.mm

    r218457 r220393  
    10301030}
    10311031
     1032- (void)_web_editorStateDidChange
     1033{
     1034}
     1035
    10321036- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
    10331037{
  • trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h

    r219050 r220393  
    8686- (void)_web_dismissContentRelativeChildWindows;
    8787- (void)_web_dismissContentRelativeChildWindowsWithAnimation:(BOOL)animate;
     88- (void)_web_editorStateDidChange;
    8889
    8990- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event;
  • trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm

    r219996 r220393  
    26112611        requestCandidatesForSelectionIfNeeded();
    26122612#endif
     2613
     2614    [m_view _web_editorStateDidChange];
    26132615}
    26142616
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r220317 r220393  
    16431643    m_maintainsInactiveSelection = newValue;
    16441644}
     1645
     1646void WebPageProxy::executeEditCommand(const String& commandName, const String& argument, WTF::Function<void(CallbackBase::Error)>&& callbackFunction)
     1647{
     1648    if (!isValid()) {
     1649        callbackFunction(CallbackBase::Error::Unknown);
     1650        return;
     1651    }
     1652
     1653    auto callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken());
     1654    m_process->send(Messages::WebPage::ExecuteEditCommandWithCallback(commandName, argument, callbackID), m_pageID);
     1655}
    16451656   
    16461657void WebPageProxy::executeEditCommand(const String& commandName, const String& argument)
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r220286 r220393  
    480480    bool isMediaStreamCaptureMuted() const { return m_mutedState & WebCore::MediaProducer::CaptureDevicesAreMuted; }
    481481    void setMediaStreamCaptureMuted(bool);
     482    void executeEditCommand(const String& commandName, const String& argument, WTF::Function<void(CallbackBase::Error)>&&);
    482483       
    483484#if PLATFORM(IOS)
    484     void executeEditCommand(const String& commandName, WTF::Function<void (CallbackBase::Error)>&&);
    485485    double displayedContentScale() const { return m_lastVisibleContentRectUpdate.scale(); }
    486486    const WebCore::FloatRect& exposedContentRect() const { return m_lastVisibleContentRectUpdate.exposedContentRect(); }
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r220293 r220393  
    35723572    [self beginSelectionChange];
    35733573    RetainPtr<WKContentView> view = self;
    3574     _page->executeEditCommand(commandName, [view](WebKit::CallbackBase::Error) {
     3574    _page->executeEditCommand(commandName, { }, [view](WebKit::CallbackBase::Error) {
    35753575        [view endSelectionChange];
    35763576    });
     
    39243924    if (_usingGestureForSelection)
    39253925        [self _updateChangedSelection];
     3926
     3927    [_webView _didChangeEditorState];
    39263928}
    39273929
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r220286 r220393  
    477477}
    478478
    479 void WebPageProxy::executeEditCommand(const String& commandName, WTF::Function<void (CallbackBase::Error)>&& callbackFunction)
    480 {
    481     if (!isValid()) {
    482         callbackFunction(CallbackBase::Error::Unknown);
    483         return;
    484     }
    485    
    486     auto callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken());
    487     m_process->send(Messages::WebPage::ExecuteEditCommandWithCallback(commandName, callbackID), m_pageID);
    488 }
    489 
    490479bool WebPageProxy::applyAutocorrection(const String& correction, const String& originalText)
    491480{
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r220370 r220393  
    862862        auto& postLayoutData = result.postLayoutData();
    863863        if (!selection.isNone()) {
    864             Node* nodeToRemove;
    865             if (auto* style = Editor::styleForSelectionStart(&frame, nodeToRemove)) {
    866                 if (isFontWeightBold(style->fontCascade().weight()))
     864            if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection)) {
     865                if (editingStyle->hasStyle(CSSPropertyFontWeight, "bold"))
    867866                    postLayoutData.typingAttributes |= AttributeBold;
    868                 if (isItalic(style->fontCascade().italic()))
     867
     868                if (editingStyle->hasStyle(CSSPropertyFontStyle, "italic") || editingStyle->hasStyle(CSSPropertyFontStyle, "oblique"))
    869869                    postLayoutData.typingAttributes |= AttributeItalics;
    870870
    871                 RefPtr<EditingStyle> typingStyle = frame.selection().typingStyle();
    872                 if (typingStyle && typingStyle->style()) {
    873                     String value = typingStyle->style()->getPropertyValue(CSSPropertyWebkitTextDecorationsInEffect);
    874                 if (value.contains("underline"))
     871                if (editingStyle->hasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline"))
    875872                    postLayoutData.typingAttributes |= AttributeUnderline;
    876                 } else {
    877                     if (style->textDecorationsInEffect() & TextDecorationUnderline)
    878                         postLayoutData.typingAttributes |= AttributeUnderline;
     873
     874                if (auto* styleProperties = editingStyle->style()) {
     875                    bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
     876                    switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign)) {
     877                    case CSSValueRight:
     878                    case CSSValueWebkitRight:
     879                        postLayoutData.textAlignment = RightAlignment;
     880                        break;
     881                    case CSSValueLeft:
     882                    case CSSValueWebkitLeft:
     883                        postLayoutData.textAlignment = LeftAlignment;
     884                        break;
     885                    case CSSValueCenter:
     886                    case CSSValueWebkitCenter:
     887                        postLayoutData.textAlignment = CenterAlignment;
     888                        break;
     889                    case CSSValueJustify:
     890                        postLayoutData.textAlignment = JustifiedAlignment;
     891                        break;
     892                    case CSSValueStart:
     893                        postLayoutData.textAlignment = isLeftToRight ? LeftAlignment : RightAlignment;
     894                        break;
     895                    case CSSValueEnd:
     896                        postLayoutData.textAlignment = isLeftToRight ? RightAlignment : LeftAlignment;
     897                        break;
     898                    default:
     899                        break;
     900                    }
     901                    if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
     902                        postLayoutData.textColor = *textColor;
    879903                }
    880 
    881                 if (style->visitedDependentColor(CSSPropertyColor).isValid())
    882                     postLayoutData.textColor = style->visitedDependentColor(CSSPropertyColor);
    883 
    884                 switch (style->textAlign()) {
    885                 case RIGHT:
    886                 case WEBKIT_RIGHT:
    887                     postLayoutData.textAlignment = RightAlignment;
    888                     break;
    889                 case LEFT:
    890                 case WEBKIT_LEFT:
    891                     postLayoutData.textAlignment = LeftAlignment;
    892                     break;
    893                 case CENTER:
    894                 case WEBKIT_CENTER:
    895                     postLayoutData.textAlignment = CenterAlignment;
    896                     break;
    897                 case JUSTIFY:
    898                     postLayoutData.textAlignment = JustifiedAlignment;
    899                     break;
    900                 case TASTART:
    901                     postLayoutData.textAlignment = style->isLeftToRightDirection() ? LeftAlignment : RightAlignment;
    902                     break;
    903                 case TAEND:
    904                     postLayoutData.textAlignment = style->isLeftToRightDirection() ? RightAlignment : LeftAlignment;
    905                     break;
    906                 }
    907                
    908                 HTMLElement* enclosingListElement = enclosingList(selection.start().deprecatedNode());
    909                 if (enclosingListElement) {
    910                     if (is<HTMLUListElement>(*enclosingListElement))
    911                         postLayoutData.enclosingListType = UnorderedList;
    912                     else if (is<HTMLOListElement>(*enclosingListElement))
    913                         postLayoutData.enclosingListType = OrderedList;
    914                     else
    915                         ASSERT_NOT_REACHED();
    916                 } else
    917                     postLayoutData.enclosingListType = NoList;
    918 
    919                 if (nodeToRemove)
    920                     nodeToRemove->remove();
     904            }
     905
     906            if (auto* enclosingListElement = enclosingList(selection.start().containerNode())) {
     907                if (is<HTMLUListElement>(*enclosingListElement))
     908                    postLayoutData.enclosingListType = UnorderedList;
     909                else if (is<HTMLOListElement>(*enclosingListElement))
     910                    postLayoutData.enclosingListType = OrderedList;
     911                else
     912                    ASSERT_NOT_REACHED();
    921913            }
    922914        }
     
    929921
    930922    return result;
     923}
     924
     925static bool shouldEnsureEditorStateUpdateAfterExecutingCommand(const String& commandName)
     926{
     927    // These commands will always ensure an EditorState update in the UI process.
     928    // FIXME: This logic was moved here from iOS platform-specific code; we should investigate whether this makes sense for all platforms.
     929    return commandName == "toggleBold" || commandName == "toggleItalic" || commandName == "toggleUnderline";
     930}
     931
     932void WebPage::executeEditCommandWithCallback(const String& commandName, const String& argument, CallbackID callbackID)
     933{
     934    executeEditCommand(commandName, argument);
     935    if (shouldEnsureEditorStateUpdateAfterExecutingCommand(commandName))
     936        send(Messages::WebPageProxy::EditorStateChanged(editorState()));
     937    send(Messages::WebPageProxy::VoidCallback(callbackID));
    931938}
    932939
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r220011 r220393  
    517517
    518518    void viewportPropertiesDidChange(const WebCore::ViewportArguments&);
     519    void executeEditCommandWithCallback(const String&, const String& argument, CallbackID);
    519520
    520521#if PLATFORM(IOS)
     
    588589
    589590    void contentSizeCategoryDidChange(const String&);
    590     void executeEditCommandWithCallback(const String&, CallbackID);
    591591
    592592    Seconds eventThrottlingDelay() const;
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r219887 r220393  
    3838    ViewWillEndLiveResize()
    3939
     40    ExecuteEditCommandWithCallback(String name, String argument, WebKit::CallbackID callbackID)
    4041    KeyEvent(WebKit::WebKeyboardEvent event)
    4142    MouseEvent(WebKit::WebMouseEvent event)
     
    9293    ApplicationDidBecomeActive()
    9394    ContentSizeCategoryDidChange(String contentSizeCategory)
    94     ExecuteEditCommandWithCallback(String name, WebKit::CallbackID callbackID)
    9595    GetSelectionContext(WebKit::CallbackID callbackID)
    9696    SetAllowsMediaDocumentInlinePlayback(bool allows)
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r219702 r220393  
    22462246}
    22472247
    2248 void WebPage::executeEditCommandWithCallback(const String& commandName, CallbackID callbackID)
    2249 {
    2250     executeEditCommand(commandName, String());
    2251     if (commandName == "toggleBold" || commandName == "toggleItalic" || commandName == "toggleUnderline")
    2252         send(Messages::WebPageProxy::EditorStateChanged(editorState()));
    2253     send(Messages::WebPageProxy::VoidCallback(callbackID));
    2254 }
    2255 
    22562248Seconds WebPage::eventThrottlingDelay() const
    22572249{
  • trunk/Tools/ChangeLog

    r220376 r220393  
     12017-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS WK2] WKWebView schedules nonstop layout after pressing cmb+b,i,u inside a contenteditable div
     4        https://bugs.webkit.org/show_bug.cgi?id=175116
     5        <rdar://problem/28279301>
     6
     7        Reviewed by Darin Adler and Ryosuke Niwa.
     8
     9        Introduces new testing infrastructure and API tests to test EditorState updates in the UI process. The new
     10        EditorStateTests run on both iOS and Mac.
     11
     12        * TestWebKitAPI/EditingTestHarness.h: Added.
     13        * TestWebKitAPI/EditingTestHarness.mm: Added.
     14
     15        EditingTestHarness is a helper object that API tests may use to apply editing commands and store EditorState
     16        history. This test harness adds sugaring around various editing commands, and simplifies the process of checking
     17        the state of the latest observed EditorState.
     18
     19        (-[EditingTestHarness initWithWebView:]):
     20        (-[EditingTestHarness dealloc]):
     21        (-[EditingTestHarness webView]):
     22        (-[EditingTestHarness latestEditorState]):
     23        (-[EditingTestHarness editorStateHistory]):
     24        (-[EditingTestHarness insertText:andExpectEditorStateWith:]):
     25        (-[EditingTestHarness insertHTML:andExpectEditorStateWith:]):
     26        (-[EditingTestHarness selectAllAndExpectEditorStateWith:]):
     27        (-[EditingTestHarness moveBackwardAndExpectEditorStateWith:]):
     28        (-[EditingTestHarness moveWordBackwardAndExpectEditorStateWith:]):
     29        (-[EditingTestHarness toggleBold]):
     30        (-[EditingTestHarness toggleItalic]):
     31        (-[EditingTestHarness toggleUnderline]):
     32        (-[EditingTestHarness setForegroundColor:]):
     33        (-[EditingTestHarness alignJustifiedAndExpectEditorStateWith:]):
     34        (-[EditingTestHarness alignLeftAndExpectEditorStateWith:]):
     35        (-[EditingTestHarness alignCenterAndExpectEditorStateWith:]):
     36        (-[EditingTestHarness alignRightAndExpectEditorStateWith:]):
     37        (-[EditingTestHarness insertParagraphAndExpectEditorStateWith:]):
     38        (-[EditingTestHarness deleteBackwardAndExpectEditorStateWith:]):
     39        (-[EditingTestHarness _execCommand:argument:expectEntries:]):
     40
     41        Dispatches an editing command to the web process, and blocks until a response is received. If an expected
     42        entries dictionary is given, this will additionally verify that the latest EditorState contains all the expected
     43        keys and values.
     44
     45        (-[EditingTestHarness latestEditorStateContains:]):
     46        (-[EditingTestHarness _webView:editorStateDidChange:]):
     47        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     48        * TestWebKitAPI/Tests/WebKit2Cocoa/EditorStateTests.mm: Added.
     49        (TestWebKitAPI::setUpEditorStateTestHarness):
     50        (TestWebKitAPI::TEST):
     51        * TestWebKitAPI/Tests/WebKit2Cocoa/editor-state-test-harness.html: Added.
     52
    1532017-08-04  Brent Fulgham  <bfulgham@apple.com>
    254
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r220284 r220393  
    663663                F42DA5161D8CEFE400336F40 /* large-input-field-focus-onload.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F42DA5151D8CEFDB00336F40 /* large-input-field-focus-onload.html */; };
    664664                F4451C761EB8FD890020C5DA /* two-paragraph-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4451C751EB8FD7C0020C5DA /* two-paragraph-contenteditable.html */; };
     665                F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */; };
     666                F44D06471F39627A001A0E29 /* EditorStateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44D06461F395C4D001A0E29 /* EditorStateTests.mm */; };
     667                F44D064A1F3962F2001A0E29 /* EditingTestHarness.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */; };
    665668                F4538EF71E8473E600B5C953 /* large-red-square.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4538EF01E846B4100B5C953 /* large-red-square.png */; };
    666669                F45B63FB1F197F4A009D38B9 /* image-map.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F45B63FA1F197F33009D38B9 /* image-map.html */; };
     
    815818                                A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */,
    816819                                9984FACE1CFFB090008D198C /* editable-body.html in Copy Resources */,
     820                                F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */,
    817821                                51C8E1A91F27F49600BF731B /* EmptyGrandfatheredResourceLoadStatistics.plist in Copy Resources */,
    818822                                A14AAB651E78DC5400C1ADC2 /* encrypted.pdf in Copy Resources */,
     
    16731677                F42DA5151D8CEFDB00336F40 /* large-input-field-focus-onload.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = "large-input-field-focus-onload.html"; path = "Tests/WebKit2Cocoa/large-input-field-focus-onload.html"; sourceTree = SOURCE_ROOT; };
    16741678                F4451C751EB8FD7C0020C5DA /* two-paragraph-contenteditable.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "two-paragraph-contenteditable.html"; sourceTree = "<group>"; };
     1679                F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "editor-state-test-harness.html"; sourceTree = "<group>"; };
     1680                F44D06461F395C4D001A0E29 /* EditorStateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditorStateTests.mm; sourceTree = "<group>"; };
     1681                F44D06481F3962E3001A0E29 /* EditingTestHarness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditingTestHarness.h; sourceTree = "<group>"; };
     1682                F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditingTestHarness.mm; sourceTree = "<group>"; };
    16751683                F4538EF01E846B4100B5C953 /* large-red-square.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "large-red-square.png"; sourceTree = "<group>"; };
    16761684                F45B63FA1F197F33009D38B9 /* image-map.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "image-map.html"; sourceTree = "<group>"; };
     
    18121820                        children = (
    18131821                                A13EBB441B87332B00097110 /* WebProcessPlugIn */,
     1822                                F44D06481F3962E3001A0E29 /* EditingTestHarness.h */,
     1823                                F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */,
    18141824                                5C726D6D1D3EE06800C5E1A1 /* InstanceMethodSwizzler.h */,
    18151825                                5C726D6E1D3EE06800C5E1A1 /* InstanceMethodSwizzler.mm */,
     
    18811891                                A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
    18821892                                A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */,
     1893                                F44D06461F395C4D001A0E29 /* EditorStateTests.mm */,
    18831894                                2D8104CB1BEC13E70020DA46 /* FindInPage.mm */,
    18841895                                2D1FE0AF1AD465C1006CD9E6 /* FixedLayoutSize.mm */,
     
    21142125                                A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,
    21152126                                9984FACD1CFFB038008D198C /* editable-body.html */,
     2127                                F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */,
    21162128                                51C8E1A81F27F47300BF731B /* EmptyGrandfatheredResourceLoadStatistics.plist */,
    21172129                                F4C2AB211DD6D94100E06D5B /* enormous-video-with-sound.html */,
     
    30993111                                7CCE7EBE1A411A7E00447C4C /* DynamicDeviceScaleFactor.mm in Sources */,
    31003112                                5C0BF8921DD599B600B00328 /* EarlyKVOCrash.mm in Sources */,
     3113                                F44D064A1F3962F2001A0E29 /* EditingTestHarness.mm in Sources */,
    31013114                                7CCE7EE01A411A9A00447C4C /* EditorCommands.mm in Sources */,
     3115                                F44D06471F39627A001A0E29 /* EditorStateTests.mm in Sources */,
    31023116                                7CCE7EBF1A411A7E00447C4C /* ElementAtPointInWebFrame.mm in Sources */,
    31033117                                07492B3B1DF8B14C00633DE1 /* EnumerateMediaDevices.cpp in Sources */,
Note: See TracChangeset for help on using the changeset viewer.