Diff
Modified: trunk/LayoutTests/ChangeLog (220392 => 220393)
--- trunk/LayoutTests/ChangeLog 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/LayoutTests/ChangeLog 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1,3 +1,21 @@
+2017-08-08 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [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.
+
+ 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:
+
2017-08-07 Matt Lewis <jlew...@apple.com>
Marked media/modern-media-controls/fullscreen-support/fullscreen-support-press.html as flaky.
Modified: trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-024-expected.txt (220392 => 220393)
--- trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-024-expected.txt 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-024-expected.txt 2017-08-08 08:10:23 UTC (rev 220393)
@@ -64,7 +64,6 @@
RenderBlock {P} at (0,238) size 784x58 [border: (2px solid #0000FF)]
RenderText {#text} at (14,15) size 36x28
text run at (14,15) width 36: "xxx"
- RenderBlock (anonymous) at (0,320) size 784x0
RenderBlock {P} at (0,320) size 784x58 [border: (2px solid #0000FF)]
RenderBR {BR} at (14,15) size 0x28 [bgcolor=#008000]
RenderBlock {P} at (0,402) size 784x58 [border: (2px solid #0000FF)]
Modified: trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-026-expected.txt (220392 => 220393)
--- trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-026-expected.txt 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/LayoutTests/platform/ios-wk2/editing/inserting/insert-div-026-expected.txt 2017-08-08 08:10:23 UTC (rev 220393)
@@ -54,5 +54,4 @@
text run at (2,3) width 20: "fo"
RenderText {#text} at (21,3) size 13x28
text run at (21,3) width 13: "x"
- RenderBlock (anonymous) at (0,34) size 784x0
caret: position 3 of child 0 {#text} of child 0 {B} of child 1 {DIV} of child 3 {DIV} of body
Modified: trunk/LayoutTests/platform/ios-wk2/editing/style/5084241-expected.txt (220392 => 220393)
--- trunk/LayoutTests/platform/ios-wk2/editing/style/5084241-expected.txt 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/LayoutTests/platform/ios-wk2/editing/style/5084241-expected.txt 2017-08-08 08:10:23 UTC (rev 220393)
@@ -14,5 +14,4 @@
RenderInline {FONT} at (0,0) size 159x19 [color=#0000FF]
RenderText {#text} at (150,0) size 159x19
text run at (150,0) width 159: "This text should be blue."
- RenderBlock (anonymous) at (0,76) size 784x0
caret: position 25 of child 0 {#text} of child 1 {FONT} of child 2 {DIV} of body
Modified: trunk/LayoutTests/platform/ios-wk2/editing/style/unbold-in-bold-expected.txt (220392 => 220393)
--- trunk/LayoutTests/platform/ios-wk2/editing/style/unbold-in-bold-expected.txt 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/LayoutTests/platform/ios-wk2/editing/style/unbold-in-bold-expected.txt 2017-08-08 08:10:23 UTC (rev 220393)
@@ -86,6 +86,5 @@
RenderText {#text} at (170,15) size 72x28
text run at (170,15) width 72: "xxxxxx"
RenderInline {SPAN} at (0,0) size 0x28
- RenderBlock (anonymous) at (0,58) size 784x0
selection start: position 0 of child 1 {#text} of child 1 {DIV} of body
selection end: position 7 of child 1 {#text} of child 1 {DIV} of body
Modified: trunk/Source/WebCore/ChangeLog (220392 => 220393)
--- trunk/Source/WebCore/ChangeLog 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/ChangeLog 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1,3 +1,43 @@
+2017-08-08 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [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.
+
+ 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):
+
2017-08-08 Zan Dobersek <zdober...@igalia.com>
[TexMap] Add TextureMapperContextAttributes
Modified: trunk/Source/WebCore/css/StyleProperties.cpp (220392 => 220393)
--- trunk/Source/WebCore/css/StyleProperties.cpp 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/css/StyleProperties.cpp 2017-08-08 08:10:23 UTC (rev 220393)
@@ -31,6 +31,7 @@
#include "CSSValueKeywords.h"
#include "CSSValueList.h"
#include "CSSValuePool.h"
+#include "Color.h"
#include "Document.h"
#include "PropertySetCSSStyleDeclaration.h"
#include "StylePropertyShorthand.h"
@@ -239,6 +240,22 @@
}
}
+std::optional<Color> StyleProperties::propertyAsColor(CSSPropertyID property) const
+{
+ auto colorValue = getPropertyCSSValue(property);
+ if (!is<CSSPrimitiveValue>(colorValue.get()))
+ return std::nullopt;
+
+ auto& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
+ return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColor(colorValue->cssText());
+}
+
+CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const
+{
+ auto cssValue = getPropertyCSSValue(property);
+ return is<CSSPrimitiveValue>(cssValue.get()) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid;
+}
+
String StyleProperties::getCustomPropertyValue(const String& propertyName) const
{
RefPtr<CSSValue> value = getCustomPropertyCSSValue(propertyName);
Modified: trunk/Source/WebCore/css/StyleProperties.h (220392 => 220393)
--- trunk/Source/WebCore/css/StyleProperties.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/css/StyleProperties.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -36,6 +36,7 @@
class CSSDeferredParser;
class CSSStyleDeclaration;
class CachedResource;
+class Color;
class ImmutableStyleProperties;
class URL;
class MutableStyleProperties;
@@ -112,6 +113,10 @@
WEBCORE_EXPORT RefPtr<CSSValue> getPropertyCSSValue(CSSPropertyID) const;
WEBCORE_EXPORT String getPropertyValue(CSSPropertyID) const;
+
+ WEBCORE_EXPORT std::optional<Color> propertyAsColor(CSSPropertyID) const;
+ WEBCORE_EXPORT CSSValueID propertyAsValueID(CSSPropertyID) const;
+
bool propertyIsImportant(CSSPropertyID) const;
String getPropertyShorthand(CSSPropertyID) const;
bool isPropertyImplicit(CSSPropertyID) const;
Modified: trunk/Source/WebCore/editing/EditingStyle.cpp (220392 => 220393)
--- trunk/Source/WebCore/editing/EditingStyle.cpp 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/editing/EditingStyle.cpp 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1413,6 +1413,11 @@
m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
}
+bool EditingStyle::hasStyle(CSSPropertyID propertyID, const String& value)
+{
+ return EditingStyle::create(propertyID, value)->triStateOfStyle(this) != FalseTriState;
+}
+
RefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
{
if (selection.isNone())
Modified: trunk/Source/WebCore/editing/EditingStyle.h (220392 => 220393)
--- trunk/Source/WebCore/editing/EditingStyle.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/editing/EditingStyle.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -165,6 +165,7 @@
void setStrikeThroughChange(TextDecorationChange change) { m_strikeThroughChange = static_cast<unsigned>(change); }
TextDecorationChange strikeThroughChange() const { return static_cast<TextDecorationChange>(m_strikeThroughChange); }
+ WEBCORE_EXPORT bool hasStyle(CSSPropertyID, const String& value);
WEBCORE_EXPORT static RefPtr<EditingStyle> styleAtSelectionStart(const VisibleSelection&, bool shouldUseBackgroundColorInEffect = false);
static WritingDirection textDirectionForSelection(const VisibleSelection&, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings);
Modified: trunk/Source/WebCore/editing/Editor.cpp (220392 => 220393)
--- trunk/Source/WebCore/editing/Editor.cpp 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebCore/editing/Editor.cpp 2017-08-08 08:10:23 UTC (rev 220393)
@@ -916,8 +916,9 @@
bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const
{
- return EditingStyle::create(propertyID, value)->triStateOfStyle(
- EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor).get());
+ if (auto editingStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection(), propertyID == CSSPropertyBackgroundColor))
+ return editingStyle->hasStyle(propertyID, value);
+ return false;
}
TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const
Modified: trunk/Source/WebKit/ChangeLog (220392 => 220393)
--- trunk/Source/WebKit/ChangeLog 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/ChangeLog 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1,3 +1,62 @@
+2017-08-08 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [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.
+
+ 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.
+
2017-08-08 Zan Dobersek <zdober...@igalia.com>
[TexMap] Don't expose GraphicsContext3D object
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -78,6 +78,7 @@
- (void)_webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures completionHandler:(void (^)(WKWebView *webView))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
- (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));
+- (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
#if TARGET_OS_IPHONE
- (BOOL)_webView:(WKWebView *)webView shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element WK_API_AVAILABLE(ios(9.0));
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1085,6 +1085,47 @@
_page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
}
+static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
+{
+ switch (alignment) {
+ case WebKit::NoAlignment:
+ return NSTextAlignmentNatural;
+ case WebKit::LeftAlignment:
+ return NSTextAlignmentLeft;
+ case WebKit::RightAlignment:
+ return NSTextAlignmentRight;
+ case WebKit::CenterAlignment:
+ return NSTextAlignmentCenter;
+ case WebKit::JustifiedAlignment:
+ return NSTextAlignmentJustified;
+ }
+ ASSERT_NOT_REACHED();
+ return NSTextAlignmentNatural;
+}
+
+static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
+{
+ if (state.isMissingPostLayoutData)
+ return @{ @"post-layout-data" : @NO };
+
+ auto& postLayoutData = state.postLayoutData();
+ return @{
+ @"post-layout-data" : @YES,
+ @"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
+ @"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
+ @"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
+ @"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
+ @"text-color": (NSString *)postLayoutData.textColor.cssText()
+ };
+}
+
+- (void)_didChangeEditorState
+{
+ id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
+ if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
+ [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
+}
+
#pragma mark iOS-specific methods
#if PLATFORM(IOS)
@@ -3639,6 +3680,11 @@
_impl->dismissContentRelativeChildWindowsWithAnimationFromViewOnly(withAnimation);
}
+- (void)_web_editorStateDidChange
+{
+ [self _didChangeEditorState];
+}
+
- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
{
_impl->gestureEventWasNotHandledByWebCoreFromViewOnly(event);
@@ -5655,6 +5701,13 @@
WebKit::ViewSnapshotStore::singleton().setDisableSnapshotVolatilityForTesting(true);
}
+- (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion
+{
+ _page->executeEditCommand(command, argument, [capturedCompletionBlock = makeBlockPtr(completion)](WebKit::CallbackBase::Error error) {
+ capturedCompletionBlock(error == WebKit::CallbackBase::Error::None);
+ });
+}
+
#if PLATFORM(IOS)
- (void)_simulateDataInteractionEntered:(id)info
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -119,6 +119,8 @@
- (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler;
- (void)_hidePasswordView;
+- (void)_didChangeEditorState;
+
- (void)_addShortcut:(id)sender;
- (void)_arrowKey:(id)sender;
- (void)_define:(id)sender;
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -407,6 +407,7 @@
- (void)_doAfterNextVisibleContentRectUpdate:(void (^)(void))updateBlock WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
- (void)_disableBackForwardSnapshotVolatilityForTesting WK_API_AVAILABLE(macosx(10.12.3), ios(10.3));
+- (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
@end
Modified: trunk/Source/WebKit/UIProcess/API/mac/WKView.mm (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/API/mac/WKView.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/API/mac/WKView.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1029,6 +1029,10 @@
[self _dismissContentRelativeChildWindowsWithAnimation:withAnimation];
}
+- (void)_web_editorStateDidChange
+{
+}
+
- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event
{
[self _gestureEventWasNotHandledByWebCore:event];
Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -85,6 +85,7 @@
- (void)_web_dismissContentRelativeChildWindows;
- (void)_web_dismissContentRelativeChildWindowsWithAnimation:(BOOL)animate;
+- (void)_web_editorStateDidChange;
- (void)_web_gestureEventWasNotHandledByWebCore:(NSEvent *)event;
Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -2610,6 +2610,8 @@
if (!m_page->editorState().isMissingPostLayoutData)
requestCandidatesForSelectionIfNeeded();
#endif
+
+ [m_view _web_editorStateDidChange];
}
void WebViewImpl::didBecomeEditable()
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1642,6 +1642,17 @@
{
m_maintainsInactiveSelection = newValue;
}
+
+void WebPageProxy::executeEditCommand(const String& commandName, const String& argument, WTF::Function<void(CallbackBase::Error)>&& callbackFunction)
+{
+ if (!isValid()) {
+ callbackFunction(CallbackBase::Error::Unknown);
+ return;
+ }
+
+ auto callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken());
+ m_process->send(Messages::WebPage::ExecuteEditCommandWithCallback(commandName, argument, callbackID), m_pageID);
+}
void WebPageProxy::executeEditCommand(const String& commandName, const String& argument)
{
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -479,9 +479,9 @@
void activateMediaStreamCaptureInPage();
bool isMediaStreamCaptureMuted() const { return m_mutedState & WebCore::MediaProducer::CaptureDevicesAreMuted; }
void setMediaStreamCaptureMuted(bool);
+ void executeEditCommand(const String& commandName, const String& argument, WTF::Function<void(CallbackBase::Error)>&&);
#if PLATFORM(IOS)
- void executeEditCommand(const String& commandName, WTF::Function<void (CallbackBase::Error)>&&);
double displayedContentScale() const { return m_lastVisibleContentRectUpdate.scale(); }
const WebCore::FloatRect& exposedContentRect() const { return m_lastVisibleContentRectUpdate.exposedContentRect(); }
const WebCore::FloatRect& unobscuredContentRect() const { return m_lastVisibleContentRectUpdate.unobscuredContentRect(); }
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -3571,7 +3571,7 @@
{
[self beginSelectionChange];
RetainPtr<WKContentView> view = self;
- _page->executeEditCommand(commandName, [view](WebKit::CallbackBase::Error) {
+ _page->executeEditCommand(commandName, { }, [view](WebKit::CallbackBase::Error) {
[view endSelectionChange];
});
}
@@ -3923,6 +3923,8 @@
// to wait to paint the selection.
if (_usingGestureForSelection)
[self _updateChangedSelection];
+
+ [_webView _didChangeEditorState];
}
- (void)selectWordForReplacement
Modified: trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm (220392 => 220393)
--- trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -476,17 +476,6 @@
m_process->send(Messages::WebPage::ApplyAutocorrection(correction, originalText, callbackID), m_pageID);
}
-void WebPageProxy::executeEditCommand(const String& commandName, WTF::Function<void (CallbackBase::Error)>&& callbackFunction)
-{
- if (!isValid()) {
- callbackFunction(CallbackBase::Error::Unknown);
- return;
- }
-
- auto callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken());
- m_process->send(Messages::WebPage::ExecuteEditCommandWithCallback(commandName, callbackID), m_pageID);
-}
-
bool WebPageProxy::applyAutocorrection(const String& correction, const String& originalText)
{
bool autocorrectionApplied = false;
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (220392 => 220393)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2017-08-08 08:10:23 UTC (rev 220393)
@@ -861,63 +861,55 @@
if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::Yes && result.isContentEditable) {
auto& postLayoutData = result.postLayoutData();
if (!selection.isNone()) {
- Node* nodeToRemove;
- if (auto* style = Editor::styleForSelectionStart(&frame, nodeToRemove)) {
- if (isFontWeightBold(style->fontCascade().weight()))
+ if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection)) {
+ if (editingStyle->hasStyle(CSSPropertyFontWeight, "bold"))
postLayoutData.typingAttributes |= AttributeBold;
- if (isItalic(style->fontCascade().italic()))
+
+ if (editingStyle->hasStyle(CSSPropertyFontStyle, "italic") || editingStyle->hasStyle(CSSPropertyFontStyle, "oblique"))
postLayoutData.typingAttributes |= AttributeItalics;
- RefPtr<EditingStyle> typingStyle = frame.selection().typingStyle();
- if (typingStyle && typingStyle->style()) {
- String value = typingStyle->style()->getPropertyValue(CSSPropertyWebkitTextDecorationsInEffect);
- if (value.contains("underline"))
+ if (editingStyle->hasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline"))
postLayoutData.typingAttributes |= AttributeUnderline;
- } else {
- if (style->textDecorationsInEffect() & TextDecorationUnderline)
- postLayoutData.typingAttributes |= AttributeUnderline;
- }
- if (style->visitedDependentColor(CSSPropertyColor).isValid())
- postLayoutData.textColor = style->visitedDependentColor(CSSPropertyColor);
-
- switch (style->textAlign()) {
- case RIGHT:
- case WEBKIT_RIGHT:
- postLayoutData.textAlignment = RightAlignment;
- break;
- case LEFT:
- case WEBKIT_LEFT:
- postLayoutData.textAlignment = LeftAlignment;
- break;
- case CENTER:
- case WEBKIT_CENTER:
- postLayoutData.textAlignment = CenterAlignment;
- break;
- case JUSTIFY:
- postLayoutData.textAlignment = JustifiedAlignment;
- break;
- case TASTART:
- postLayoutData.textAlignment = style->isLeftToRightDirection() ? LeftAlignment : RightAlignment;
- break;
- case TAEND:
- postLayoutData.textAlignment = style->isLeftToRightDirection() ? RightAlignment : LeftAlignment;
- break;
+ if (auto* styleProperties = editingStyle->style()) {
+ bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
+ switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign)) {
+ case CSSValueRight:
+ case CSSValueWebkitRight:
+ postLayoutData.textAlignment = RightAlignment;
+ break;
+ case CSSValueLeft:
+ case CSSValueWebkitLeft:
+ postLayoutData.textAlignment = LeftAlignment;
+ break;
+ case CSSValueCenter:
+ case CSSValueWebkitCenter:
+ postLayoutData.textAlignment = CenterAlignment;
+ break;
+ case CSSValueJustify:
+ postLayoutData.textAlignment = JustifiedAlignment;
+ break;
+ case CSSValueStart:
+ postLayoutData.textAlignment = isLeftToRight ? LeftAlignment : RightAlignment;
+ break;
+ case CSSValueEnd:
+ postLayoutData.textAlignment = isLeftToRight ? RightAlignment : LeftAlignment;
+ break;
+ default:
+ break;
+ }
+ if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
+ postLayoutData.textColor = *textColor;
}
-
- HTMLElement* enclosingListElement = enclosingList(selection.start().deprecatedNode());
- if (enclosingListElement) {
- if (is<HTMLUListElement>(*enclosingListElement))
- postLayoutData.enclosingListType = UnorderedList;
- else if (is<HTMLOListElement>(*enclosingListElement))
- postLayoutData.enclosingListType = OrderedList;
- else
- ASSERT_NOT_REACHED();
- } else
- postLayoutData.enclosingListType = NoList;
+ }
- if (nodeToRemove)
- nodeToRemove->remove();
+ if (auto* enclosingListElement = enclosingList(selection.start().containerNode())) {
+ if (is<HTMLUListElement>(*enclosingListElement))
+ postLayoutData.enclosingListType = UnorderedList;
+ else if (is<HTMLOListElement>(*enclosingListElement))
+ postLayoutData.enclosingListType = OrderedList;
+ else
+ ASSERT_NOT_REACHED();
}
}
}
@@ -930,6 +922,21 @@
return result;
}
+static bool shouldEnsureEditorStateUpdateAfterExecutingCommand(const String& commandName)
+{
+ // These commands will always ensure an EditorState update in the UI process.
+ // FIXME: This logic was moved here from iOS platform-specific code; we should investigate whether this makes sense for all platforms.
+ return commandName == "toggleBold" || commandName == "toggleItalic" || commandName == "toggleUnderline";
+}
+
+void WebPage::executeEditCommandWithCallback(const String& commandName, const String& argument, CallbackID callbackID)
+{
+ executeEditCommand(commandName, argument);
+ if (shouldEnsureEditorStateUpdateAfterExecutingCommand(commandName))
+ send(Messages::WebPageProxy::EditorStateChanged(editorState()));
+ send(Messages::WebPageProxy::VoidCallback(callbackID));
+}
+
void WebPage::updateEditorStateAfterLayoutIfEditabilityChanged()
{
// FIXME: We should update EditorStateIsContentEditable to track whether the state is richly
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (220392 => 220393)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -516,6 +516,7 @@
void resetAssistedNodeForFrame(WebFrame*);
void viewportPropertiesDidChange(const WebCore::ViewportArguments&);
+ void executeEditCommandWithCallback(const String&, const String& argument, CallbackID);
#if PLATFORM(IOS)
WebCore::FloatSize screenSize() const;
@@ -587,7 +588,6 @@
#endif
void contentSizeCategoryDidChange(const String&);
- void executeEditCommandWithCallback(const String&, CallbackID);
Seconds eventThrottlingDelay() const;
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (220392 => 220393)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2017-08-08 08:10:23 UTC (rev 220393)
@@ -37,6 +37,7 @@
ViewWillStartLiveResize()
ViewWillEndLiveResize()
+ ExecuteEditCommandWithCallback(String name, String argument, WebKit::CallbackID callbackID)
KeyEvent(WebKit::WebKeyboardEvent event)
MouseEvent(WebKit::WebMouseEvent event)
#if PLATFORM(IOS)
@@ -91,7 +92,6 @@
ApplicationWillEnterForeground(bool isSuspendedUnderLock)
ApplicationDidBecomeActive()
ContentSizeCategoryDidChange(String contentSizeCategory)
- ExecuteEditCommandWithCallback(String name, WebKit::CallbackID callbackID)
GetSelectionContext(WebKit::CallbackID callbackID)
SetAllowsMediaDocumentInlinePlayback(bool allows)
HandleTwoFingerTapAtPoint(WebCore::IntPoint point, uint64_t requestID)
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (220392 => 220393)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -2245,14 +2245,6 @@
send(Messages::WebPageProxy::StringCallback(correctionApplied ? correction : String(), callbackID));
}
-void WebPage::executeEditCommandWithCallback(const String& commandName, CallbackID callbackID)
-{
- executeEditCommand(commandName, String());
- if (commandName == "toggleBold" || commandName == "toggleItalic" || commandName == "toggleUnderline")
- send(Messages::WebPageProxy::EditorStateChanged(editorState()));
- send(Messages::WebPageProxy::VoidCallback(callbackID));
-}
-
Seconds WebPage::eventThrottlingDelay() const
{
auto behaviorOverride = m_page->eventThrottlingBehaviorOverride();
Modified: trunk/Tools/ChangeLog (220392 => 220393)
--- trunk/Tools/ChangeLog 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Tools/ChangeLog 2017-08-08 08:10:23 UTC (rev 220393)
@@ -1,3 +1,55 @@
+2017-08-08 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [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.
+
+ 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.
+
2017-08-04 Brent Fulgham <bfulg...@apple.com>
Prevent domain from being set to a TLD
Added: trunk/Tools/TestWebKitAPI/EditingTestHarness.h (0 => 220393)
--- trunk/Tools/TestWebKitAPI/EditingTestHarness.h (rev 0)
+++ trunk/Tools/TestWebKitAPI/EditingTestHarness.h 2017-08-08 08:10:23 UTC (rev 220393)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if WK_API_ENABLED
+
+#import "TestWKWebView.h"
+#import <WebKit/WKUIDelegatePrivate.h>
+
+@interface EditingTestHarness : NSObject<WKUIDelegatePrivate> {
+ RetainPtr<NSMutableArray<NSDictionary *> *> _editorStateHistory;
+ RetainPtr<TestWKWebView *> _webView;
+}
+
+- (instancetype)initWithWebView:(TestWKWebView *)webView;
+
+@property (nonatomic, readonly) TestWKWebView *webView;
+@property (nonatomic, readonly) NSDictionary *latestEditorState;
+@property (nonatomic, readonly) NSArray<NSDictionary *> *editorStateHistory;
+
+- (void)insertParagraphAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)insertText:(NSString *)text andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)insertHTML:(NSString *)html andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)selectAllAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)moveBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)moveWordBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)deleteBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)toggleBold;
+- (void)toggleItalic;
+- (void)toggleUnderline;
+- (void)setForegroundColor:(NSString *)colorAsString;
+- (void)alignJustifiedAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)alignLeftAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)alignCenterAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+- (void)alignRightAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
+
+- (BOOL)latestEditorStateContains:(NSDictionary<NSString *, id> *)entries;
+
+@end
+
+#endif // WK_API_ENABLED
Added: trunk/Tools/TestWebKitAPI/EditingTestHarness.mm (0 => 220393)
--- trunk/Tools/TestWebKitAPI/EditingTestHarness.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/EditingTestHarness.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "EditingTestHarness.h"
+
+#if WK_API_ENABLED
+
+#import "PlatformUtilities.h"
+#import <WebKit/WKWebViewPrivate.h>
+
+@implementation EditingTestHarness
+
+- (instancetype)initWithWebView:(TestWKWebView *)webView
+{
+ if (self = [super init]) {
+ _webView = webView;
+ [_webView setUIDelegate:self];
+ _editorStateHistory = adoptNS([[NSMutableArray alloc] init]);
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ if ([_webView UIDelegate] == self)
+ [_webView setUIDelegate:nil];
+
+ [super dealloc];
+}
+
+- (TestWKWebView *)webView
+{
+ return _webView.get();
+}
+
+- (NSDictionary *)latestEditorState
+{
+ return self.editorStateHistory.lastObject;
+}
+
+- (NSArray<NSDictionary *> *)editorStateHistory
+{
+ return _editorStateHistory.get();
+}
+
+- (void)insertText:(NSString *)text andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"InsertText" argument:text expectEntries:entries];
+}
+
+- (void)insertHTML:(NSString *)html andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"InsertHTML" argument:html expectEntries:entries];
+}
+
+- (void)selectAllAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"SelectAll" argument:nil expectEntries:entries];
+}
+
+- (void)moveBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"MoveBackward" argument:nil expectEntries:entries];
+}
+
+- (void)moveWordBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"MoveWordBackward" argument:nil expectEntries:entries];
+}
+
+- (void)toggleBold
+{
+ [self _execCommand:@"ToggleBold" argument:nil expectEntries:nil];
+}
+
+- (void)toggleItalic
+{
+ [self _execCommand:@"ToggleItalic" argument:nil expectEntries:nil];
+}
+
+- (void)toggleUnderline
+{
+ [self _execCommand:@"ToggleUnderline" argument:nil expectEntries:nil];
+}
+
+- (void)setForegroundColor:(NSString *)colorAsString
+{
+ [self _execCommand:@"ForeColor" argument:colorAsString expectEntries:nil];
+}
+
+- (void)alignJustifiedAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"AlignJustified" argument:nil expectEntries:entries];
+}
+
+- (void)alignLeftAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"AlignLeft" argument:nil expectEntries:entries];
+}
+
+- (void)alignCenterAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"AlignCenter" argument:nil expectEntries:entries];
+}
+
+- (void)alignRightAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"AlignRight" argument:nil expectEntries:entries];
+}
+
+- (void)insertParagraphAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"InsertParagraph" argument:nil expectEntries:entries];
+}
+
+- (void)deleteBackwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"DeleteBackward" argument:nil expectEntries:entries];
+}
+
+- (void)_execCommand:(NSString *)command argument:(NSString *)argument expectEntries:(NSDictionary<NSString *, id> *)entries
+{
+ __block BOOL result = false;
+ __block bool done = false;
+ [_webView _executeEditCommand:command argument:argument completion:^(BOOL success) {
+ result = success;
+ done = true;
+ }];
+ TestWebKitAPI::Util::run(&done);
+
+ EXPECT_TRUE(result);
+ if (!result)
+ NSLog(@"Failed to execute editing command: ('%@', '%@')", command, argument ?: @"");
+
+ BOOL containsEntries = [self latestEditorStateContains:entries];
+ EXPECT_TRUE(containsEntries);
+ if (!containsEntries)
+ NSLog(@"Expected %@ to contain %@", self.latestEditorState, entries);
+}
+
+- (BOOL)latestEditorStateContains:(NSDictionary<NSString *, id> *)entries
+{
+ NSDictionary *latestEditorState = self.latestEditorState;
+ for (NSString *key in entries) {
+ if (![latestEditorState[key] isEqual:entries[key]])
+ return NO;
+ }
+ return latestEditorState.count || !entries.count;
+}
+
+#pragma mark - WKUIDelegatePrivate
+
+- (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState
+{
+ if (![editorState[@"post-layout-data"] boolValue])
+ return;
+
+ if (![self.latestEditorState isEqualToDictionary:editorState])
+ [_editorStateHistory addObject:editorState];
+}
+
+@end
+
+#endif // WK_API_ENABLED
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (220392 => 220393)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-08-08 07:46:42 UTC (rev 220392)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-08-08 08:10:23 UTC (rev 220393)
@@ -662,6 +662,9 @@
F41AB9AA1EF4696B0083FA08 /* textarea-to-input.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F41AB9951EF4692C0083FA08 /* textarea-to-input.html */; };
F42DA5161D8CEFE400336F40 /* large-input-field-focus-onload.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F42DA5151D8CEFDB00336F40 /* large-input-field-focus-onload.html */; };
F4451C761EB8FD890020C5DA /* two-paragraph-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4451C751EB8FD7C0020C5DA /* two-paragraph-contenteditable.html */; };
+ F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */; };
+ F44D06471F39627A001A0E29 /* EditorStateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44D06461F395C4D001A0E29 /* EditorStateTests.mm */; };
+ F44D064A1F3962F2001A0E29 /* EditingTestHarness.mm in Sources */ = {isa = PBXBuildFile; fileRef = F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */; };
F4538EF71E8473E600B5C953 /* large-red-square.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4538EF01E846B4100B5C953 /* large-red-square.png */; };
F45B63FB1F197F4A009D38B9 /* image-map.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F45B63FA1F197F33009D38B9 /* image-map.html */; };
F45B63FE1F19D410009D38B9 /* ActionSheetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F45B63FC1F19D410009D38B9 /* ActionSheetTests.mm */; };
@@ -814,6 +817,7 @@
F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.html in Copy Resources */,
A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */,
9984FACE1CFFB090008D198C /* editable-body.html in Copy Resources */,
+ F44D06451F395C26001A0E29 /* editor-state-test-harness.html in Copy Resources */,
51C8E1A91F27F49600BF731B /* EmptyGrandfatheredResourceLoadStatistics.plist in Copy Resources */,
A14AAB651E78DC5400C1ADC2 /* encrypted.pdf in Copy Resources */,
F4C2AB221DD6D95E00E06D5B /* enormous-video-with-sound.html in Copy Resources */,
@@ -1672,6 +1676,10 @@
F41AB99E1EF4692C0083FA08 /* div-and-large-image.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "div-and-large-image.html"; sourceTree = "<group>"; };
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; };
F4451C751EB8FD7C0020C5DA /* two-paragraph-contenteditable.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "two-paragraph-contenteditable.html"; sourceTree = "<group>"; };
+ F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "editor-state-test-harness.html"; sourceTree = "<group>"; };
+ F44D06461F395C4D001A0E29 /* EditorStateTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditorStateTests.mm; sourceTree = "<group>"; };
+ F44D06481F3962E3001A0E29 /* EditingTestHarness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditingTestHarness.h; sourceTree = "<group>"; };
+ F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditingTestHarness.mm; sourceTree = "<group>"; };
F4538EF01E846B4100B5C953 /* large-red-square.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "large-red-square.png"; sourceTree = "<group>"; };
F45B63FA1F197F33009D38B9 /* image-map.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "image-map.html"; sourceTree = "<group>"; };
F45B63FC1F19D410009D38B9 /* ActionSheetTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ActionSheetTests.mm; sourceTree = "<group>"; };
@@ -1811,6 +1819,8 @@
isa = PBXGroup;
children = (
A13EBB441B87332B00097110 /* WebProcessPlugIn */,
+ F44D06481F3962E3001A0E29 /* EditingTestHarness.h */,
+ F44D06491F3962E3001A0E29 /* EditingTestHarness.mm */,
5C726D6D1D3EE06800C5E1A1 /* InstanceMethodSwizzler.h */,
5C726D6E1D3EE06800C5E1A1 /* InstanceMethodSwizzler.mm */,
0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */,
@@ -1880,6 +1890,7 @@
2DC60E221E79F88C00FA6C7D /* DoAfterNextPresentationUpdateAfterCrash.mm */,
A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */,
+ F44D06461F395C4D001A0E29 /* EditorStateTests.mm */,
2D8104CB1BEC13E70020DA46 /* FindInPage.mm */,
2D1FE0AF1AD465C1006CD9E6 /* FixedLayoutSize.mm */,
CD78E11A1DB7EA360014A2DE /* FullscreenDelegate.mm */,
@@ -2113,6 +2124,7 @@
F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */,
A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,
9984FACD1CFFB038008D198C /* editable-body.html */,
+ F44D06441F395C0D001A0E29 /* editor-state-test-harness.html */,
51C8E1A81F27F47300BF731B /* EmptyGrandfatheredResourceLoadStatistics.plist */,
F4C2AB211DD6D94100E06D5B /* enormous-video-with-sound.html */,
F407FE381F1D0DE60017CF25 /* enormous.svg */,
@@ -3098,7 +3110,9 @@
A155022A1E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm in Sources */,
7CCE7EBE1A411A7E00447C4C /* DynamicDeviceScaleFactor.mm in Sources */,
5C0BF8921DD599B600B00328 /* EarlyKVOCrash.mm in Sources */,
+ F44D064A1F3962F2001A0E29 /* EditingTestHarness.mm in Sources */,
7CCE7EE01A411A9A00447C4C /* EditorCommands.mm in Sources */,
+ F44D06471F39627A001A0E29 /* EditorStateTests.mm in Sources */,
7CCE7EBF1A411A7E00447C4C /* ElementAtPointInWebFrame.mm in Sources */,
07492B3B1DF8B14C00633DE1 /* EnumerateMediaDevices.cpp in Sources */,
448D7E471EA6C55500ECC756 /* EnvironmentUtilitiesTest.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/EditorStateTests.mm (0 => 220393)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/EditorStateTests.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/EditorStateTests.mm 2017-08-08 08:10:23 UTC (rev 220393)
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_API_ENABLED
+
+#import "EditingTestHarness.h"
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKWebViewPrivate.h>
+
+namespace TestWebKitAPI {
+
+static RetainPtr<EditingTestHarness> setUpEditorStateTestHarness()
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+ auto testHarness = adoptNS([[EditingTestHarness alloc] initWithWebView:webView.get()]);
+ [webView synchronouslyLoadTestPageNamed:@"editor-state-test-harness"];
+ return testHarness;
+}
+
+TEST(EditorStateTests, TypingAttributesBold)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+
+ [testHarness insertHTML:@"<b>first</b>" andExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness toggleBold];
+ [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness insertHTML:@"<span style='font-weight: 700'> third</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness insertHTML:@"<span style='font-weight: 300'> fourth</span>" andExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness insertHTML:@"<span style='font-weight: 800'> fifth</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness insertHTML:@"<span style='font-weight: 400'> sixth</span>" andExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness insertHTML:@"<span style='font-weight: 900'> seventh</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness toggleBold];
+ [testHarness insertText:@" eighth" andExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness insertHTML:@"<strong> ninth</strong>" andExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @NO }];
+ [testHarness selectAllAndExpectEditorStateWith:@{ @"bold": @YES }];
+ EXPECT_WK_STREQ("first second third fourth fifth sixth seventh eighth ninth", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+}
+
+TEST(EditorStateTests, TypingAttributesItalic)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+
+ [testHarness insertHTML:@"<i>first</i>" andExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness toggleItalic];
+ [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"italic": @NO }];
+ [testHarness insertHTML:@"<span style='font-style: italic'> third</span>" andExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness toggleItalic];
+ [testHarness insertText:@" fourth" andExpectEditorStateWith:@{ @"italic": @NO }];
+ [testHarness toggleItalic];
+ [testHarness insertText:@" fifth" andExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness insertHTML:@"<span style='font-style: normal'> sixth</span>" andExpectEditorStateWith:@{ @"italic": @NO }];
+ [testHarness insertHTML:@"<span style='font-style: oblique'> seventh</span>" andExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @NO }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @NO }];
+
+ [testHarness selectAllAndExpectEditorStateWith:@{ @"italic": @YES }];
+ EXPECT_WK_STREQ("first second third fourth fifth sixth seventh", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+}
+
+TEST(EditorStateTests, TypingAttributesUnderline)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+
+ [testHarness insertHTML:@"<u>first</u>" andExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness toggleUnderline];
+ [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"underline": @NO }];
+ [testHarness insertHTML:@"<span style='text-decoration: underline'> third</span>" andExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness insertHTML:@"<span style='text-decoration: line-through'> fourth</span>" andExpectEditorStateWith:@{ @"underline": @NO }];
+ [testHarness insertHTML:@"<span style='text-decoration: underline overline line-through'> fifth</span>" andExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness insertHTML:@"<span style='text-decoration: none'> sixth</span>" andExpectEditorStateWith:@{ @"underline": @NO }];
+ [testHarness toggleUnderline];
+ [testHarness insertText:@" seventh" andExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @NO }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
+ [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @NO }];
+
+ [testHarness selectAllAndExpectEditorStateWith:@{ @"underline": @YES }];
+ EXPECT_WK_STREQ("first second third fourth fifth sixth seventh", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+}
+
+TEST(EditorStateTests, TypingAttributesTextAlignmentAbsoluteAlignmentOptions)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+ TestWKWebView *webView = [testHarness webView];
+
+ [webView stringByEvaluatingJavaScript:@"document.body.style.direction = 'ltr'"];
+
+ [testHarness insertHTML:@"<div style='text-align: right;'>right</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+
+ [testHarness insertText:@"justified" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness alignJustifiedAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentJustified) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentJustified) }];
+
+ [testHarness alignCenterAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
+ [testHarness insertText:@"center" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
+
+ [testHarness insertHTML:@"<span id='left'>left</span>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
+ [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(left.childNodes[0], 0, left.childNodes[0], 6)"];
+ [testHarness alignLeftAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+
+ [testHarness selectAllAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ EXPECT_WK_STREQ("right\njustified\ncenter\nleft", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+}
+
+TEST(EditorStateTests, TypingAttributesTextAlignmentStartEnd)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+ TestWKWebView *webView = [testHarness webView];
+
+ [webView stringByEvaluatingJavaScript:@"document.styleSheets[0].insertRule('.start { text-align: start; }')"];
+ [webView stringByEvaluatingJavaScript:@"document.styleSheets[0].insertRule('.end { text-align: end; }')"];
+ [webView stringByEvaluatingJavaScript:@"document.body.style.direction = 'rtl'"];
+
+ [testHarness insertHTML:@"<div class='start'>rtl start</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+
+ [testHarness insertHTML:@"<div class='end'>rtl end</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+
+ [[testHarness webView] stringByEvaluatingJavaScript:@"document.body.style.direction = 'ltr'"];
+ [testHarness insertHTML:@"<div class='start'>ltr start</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+
+ [testHarness insertHTML:@"<div class='end'>ltr end</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+}
+
+TEST(EditorStateTests, TypingAttributesTextAlignmentDirectionalText)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+ [[testHarness webView] stringByEvaluatingJavaScript:@"document.body.setAttribute('dir', 'auto')"];
+
+ [testHarness insertHTML:@"<div>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertHTML:@"<div dir='ltr'>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertHTML:@"<div dir='rtl'>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+
+ [testHarness insertHTML:@"<div dir='auto'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertHTML:@"<div dir='rtl'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
+ [testHarness insertHTML:@"<div dir='ltr'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
+}
+
+TEST(EditorStateTests, TypingAttributesTextColor)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+
+ [testHarness setForegroundColor:@"rgb(255, 0, 0)"];
+ [testHarness insertText:@"red" andExpectEditorStateWith:@{ @"text-color": @"rgb(255, 0, 0)" }];
+
+ [testHarness insertHTML:@"<span style='color: rgb(0, 255, 0)'>green</span>" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 255, 0)" }];
+ [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-color": @"rgb(0, 255, 0)" }];
+
+ [testHarness setForegroundColor:@"rgb(0, 0, 255)"];
+ [testHarness insertText:@"blue" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 255)" }];
+}
+
+TEST(EditorStateTests, TypingAttributesMixedStyles)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+
+ [testHarness setForegroundColor:@"rgb(128, 128, 128)"];
+ [testHarness toggleBold];
+ [testHarness toggleItalic];
+ [testHarness toggleUnderline];
+ [testHarness alignCenterAndExpectEditorStateWith:@{
+ @"bold": @YES,
+ @"italic": @YES,
+ @"underline": @YES,
+ @"text-color": @"rgb(128, 128, 128)",
+ @"text-alignment": @(NSTextAlignmentCenter)
+ }];
+}
+
+TEST(EditorStateTests, TypingAttributeLinkColor)
+{
+ auto testHarness = setUpEditorStateTestHarness();
+ [testHarness insertHTML:@"<a href=''>This is a link</a>" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 238)" }];
+ [testHarness selectAllAndExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 238)" }];
+ EXPECT_WK_STREQ("https://www.apple.com/", [[testHarness webView] stringByEvaluatingJavaScript:@"document.querySelector('a').href"]);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // WK_API_ENABLED
Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/editor-state-test-harness.html (0 => 220393)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/editor-state-test-harness.html (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/editor-state-test-harness.html 2017-08-08 08:10:23 UTC (rev 220393)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<style>
+body, html {
+ font-family: -apple-system;
+ font-size: 1em;
+ width: 100%;
+ height: 100%;
+}
+</style>
+<body contenteditable></body>
+<script>
+document.body.focus();
+</script>