[Cocoa] Add SPI to expose typing attributes at the current selection on WKWebView
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Oct 2018 00:47:05 +0000 (00:47 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 5 Oct 2018 00:47:05 +0000 (00:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190119
<rdar://problem/44767062>

Reviewed by Tim Horton.

Source/WebKit:

Add support for new WebKit2 SPI to notify the UI delegate about font attributes when the editor state changes
(e.g. due to selection changes, or executing an edit command). See below for more detail.

Test: FontAttributes.FontAttributesAfterChangingSelection

* Shared/EditorState.cpp:
(WebKit::EditorState::PostLayoutData::encode const):
(WebKit::EditorState::PostLayoutData::decode):
* Shared/EditorState.h:

Add a new optional `FontAttributes` member to EditorState's post-layout data. FontAttributes are computed and
sent over IPC only if the UI delegate implements the new delegate hook which requires information about font
attributes.

* Shared/WebPageCreationParameters.cpp:
(WebKit::WebPageCreationParameters::encode const):
(WebKit::WebPageCreationParameters::decode):
* Shared/WebPageCreationParameters.h:

Add a new flag for the UI process to let a new web page know whether it should additionally compute font
attributes when computing editor state.

* UIProcess/API/APIUIClient.h:
(API::UIClient::needsFontAttributes const):
(API::UIClient::didChangeFontAttributes):
* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didChangeEditorState]):
* UIProcess/Cocoa/UIDelegate.h:
* UIProcess/Cocoa/UIDelegate.mm:
(WebKit::UIDelegate::setDelegate):
(WebKit::UIDelegate::UIClient::didChangeFontAttributes):

Call out to the UI delegate with a font attribute dictionary, created via FontAttributes on EditorState's
post-layout data.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::setUIClient):

Update whether or not the UI process needs to know about font attributes. The UI process only requires font
attribute information if the UI delegate implements `-_webView:didChangeFontAttributes:`.

(WebKit::WebPageProxy::setNeedsFontAttributes):
(WebKit::WebPageProxy::creationParameters):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::editorStateChanged):
* UIProcess/mac/WebPageProxyMac.mm:
(WebKit::WebPageProxy::editorStateChanged):

Update the cached font attributes in the UI process when receiving a new editor state update.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::m_cpuLimit):
(WebKit::WebPage::editorState const):
(WebKit::WebPage::setNeedsFontAttributes):

Add a new IPC hook to update whether or not the page should additionally compute font attributes. In the case
where the UI delegate changes from something that does not require font attributes to one that does, we
additionally schedule an editor state update.

(WebKit::WebPage::updateFontAttributesAfterEditorStateChange):

Private helper function to plumb FontAttributes to the UI client after an editor state change.

* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Tools:

Add a new API test that loads a document containing various rich text styles. This test moves the selection
around the document and checks the last set of font attributes received via the new UI delegate hook.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/FontAttributes.mm: Added.
(-[FontAttributesListener _webView:didChangeFontAttributes:]):
(-[FontAttributesListener lastFontAttributes]):
(-[TestWKWebView selectElementWithIdentifier:]):
(-[TestWKWebView fontAttributesAfterNextPresentationUpdate]):
(ColorExpectation::ColorExpectation):
(ShadowExpectation::ShadowExpectation):
(checkColor):
(checkShadow):
(checkFont):
(webViewForTestingFontAttributes):
* TestWebKitAPI/Tests/WebKitCocoa/rich-text-attributes.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@236865 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
Source/WebKit/ChangeLog
Source/WebKit/Shared/EditorState.cpp
Source/WebKit/Shared/EditorState.h
Source/WebKit/Shared/WebPageCreationParameters.cpp
Source/WebKit/Shared/WebPageCreationParameters.h
Source/WebKit/UIProcess/API/APIUIClient.h
Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/Cocoa/UIDelegate.h
Source/WebKit/UIProcess/Cocoa/UIDelegate.mm
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit/UIProcess/mac/WebPageProxyMac.mm
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/FontAttributes.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitCocoa/rich-text-attributes.html [new file with mode: 0644]

index 854b8cf..3d0d18f 100644 (file)
@@ -1,3 +1,79 @@
+2018-10-04  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Cocoa] Add SPI to expose typing attributes at the current selection on WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=190119
+        <rdar://problem/44767062>
+
+        Reviewed by Tim Horton.
+
+        Add support for new WebKit2 SPI to notify the UI delegate about font attributes when the editor state changes
+        (e.g. due to selection changes, or executing an edit command). See below for more detail.
+
+        Test: FontAttributes.FontAttributesAfterChangingSelection
+
+        * Shared/EditorState.cpp:
+        (WebKit::EditorState::PostLayoutData::encode const):
+        (WebKit::EditorState::PostLayoutData::decode):
+        * Shared/EditorState.h:
+
+        Add a new optional `FontAttributes` member to EditorState's post-layout data. FontAttributes are computed and
+        sent over IPC only if the UI delegate implements the new delegate hook which requires information about font
+        attributes.
+
+        * Shared/WebPageCreationParameters.cpp:
+        (WebKit::WebPageCreationParameters::encode const):
+        (WebKit::WebPageCreationParameters::decode):
+        * Shared/WebPageCreationParameters.h:
+
+        Add a new flag for the UI process to let a new web page know whether it should additionally compute font
+        attributes when computing editor state.
+
+        * UIProcess/API/APIUIClient.h:
+        (API::UIClient::needsFontAttributes const):
+        (API::UIClient::didChangeFontAttributes):
+        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didChangeEditorState]):
+        * UIProcess/Cocoa/UIDelegate.h:
+        * UIProcess/Cocoa/UIDelegate.mm:
+        (WebKit::UIDelegate::setDelegate):
+        (WebKit::UIDelegate::UIClient::didChangeFontAttributes):
+
+        Call out to the UI delegate with a font attribute dictionary, created via FontAttributes on EditorState's
+        post-layout data.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::setUIClient):
+
+        Update whether or not the UI process needs to know about font attributes. The UI process only requires font
+        attribute information if the UI delegate implements `-_webView:didChangeFontAttributes:`.
+
+        (WebKit::WebPageProxy::setNeedsFontAttributes):
+        (WebKit::WebPageProxy::creationParameters):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::editorStateChanged):
+        * UIProcess/mac/WebPageProxyMac.mm:
+        (WebKit::WebPageProxy::editorStateChanged):
+
+        Update the cached font attributes in the UI process when receiving a new editor state update.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::m_cpuLimit):
+        (WebKit::WebPage::editorState const):
+        (WebKit::WebPage::setNeedsFontAttributes):
+
+        Add a new IPC hook to update whether or not the page should additionally compute font attributes. In the case
+        where the UI delegate changes from something that does not require font attributes to one that does, we
+        additionally schedule an editor state update.
+
+        (WebKit::WebPage::updateFontAttributesAfterEditorStateChange):
+
+        Private helper function to plumb FontAttributes to the UI client after an editor state change.
+
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2018-10-04  Jeremy Jones  <jeremyj@apple.com>
 
         Unify implementation in VideoFullscreenInterfaceAVKit
index 2dfa518..dde0ed4 100644 (file)
@@ -138,6 +138,7 @@ void EditorState::PostLayoutData::encode(IPC::Encoder& encoder) const
     encoder << paragraphContextForCandidateRequest;
     encoder << stringForCandidateRequest;
 #endif
+    encoder << fontAttributes;
     encoder << canCut;
     encoder << canCopy;
     encoder << canPaste;
@@ -200,6 +201,13 @@ bool EditorState::PostLayoutData::decode(IPC::Decoder& decoder, PostLayoutData&
         return false;
 #endif
 
+    std::optional<std::optional<FontAttributes>> optionalFontAttributes;
+    decoder >> optionalFontAttributes;
+    if (!optionalFontAttributes)
+        return false;
+
+    result.fontAttributes = optionalFontAttributes.value();
+
     if (!decoder.decode(result.canCut))
         return false;
     if (!decoder.decode(result.canCopy))
index 2085c15..08151a2 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "ArgumentCoders.h"
 #include <WebCore/Color.h>
+#include <WebCore/FontAttributes.h>
 #include <WebCore/IntRect.h>
 #include <wtf/text/WTFString.h>
 
@@ -114,6 +115,8 @@ struct EditorState {
         String stringForCandidateRequest;
 #endif
 
+        std::optional<WebCore::FontAttributes> fontAttributes;
+
         bool canCut { false };
         bool canCopy { false };
         bool canPaste { false };
index 5c01b7f..b1fb4e5 100644 (file)
@@ -110,6 +110,7 @@ void WebPageCreationParameters::encode(IPC::Encoder& encoder) const
 #if ENABLE(SERVICE_WORKER)
     encoder << hasRegisteredServiceWorkers;
 #endif
+    encoder << needsFontAttributes;
     encoder << iceCandidateFilteringEnabled;
     encoder << enumeratingAllNetworkInterfacesEnabled;
     encoder << userContentWorlds;
@@ -305,6 +306,9 @@ std::optional<WebPageCreationParameters> WebPageCreationParameters::decode(IPC::
         return std::nullopt;
 #endif
 
+    if (!decoder.decode(parameters.needsFontAttributes))
+        return std::nullopt;
+
     if (!decoder.decode(parameters.iceCandidateFilteringEnabled))
         return std::nullopt;
 
index e35a5f4..db4c07f 100644 (file)
@@ -173,6 +173,8 @@ struct WebPageCreationParameters {
     bool hasRegisteredServiceWorkers { true };
 #endif
 
+    bool needsFontAttributes { false };
+
     // WebRTC members.
     bool iceCandidateFilteringEnabled { true };
     bool enumeratingAllNetworkInterfacesEnabled { false };
index ba290e0..a10fa71 100644 (file)
@@ -40,6 +40,7 @@ OBJC_CLASS UIViewController;
 
 namespace WebCore {
 class ResourceRequest;
+struct FontAttributes;
 struct SecurityOriginData;
 struct WindowFeatures;
 }
@@ -123,6 +124,9 @@ public:
         completionHandler(currentQuota);
     }
 
+    virtual bool needsFontAttributes() const { return false; }
+    virtual void didChangeFontAttributes(const WebCore::FontAttributes&) { }
+
     virtual bool runOpenPanel(WebKit::WebPageProxy*, WebKit::WebFrameProxy*, const WebCore::SecurityOriginData&, OpenPanelParameters*, WebKit::WebOpenPanelResultListenerProxy*) { return false; }
     virtual void decidePolicyForGeolocationPermissionRequest(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, SecurityOrigin&, Function<void(bool)>&) { }
     virtual bool decidePolicyForUserMediaPermissionRequest(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, SecurityOrigin&, SecurityOrigin&, WebKit::UserMediaPermissionRequestProxy&) { return false; }
index f122b73..18a9fc9 100644 (file)
@@ -121,6 +121,8 @@ struct UIEdgeInsets;
 
 - (void)_webView:(WKWebView *)webView requestStorageAccessPanelForDomain:(NSString *)requestingDomain underCurrentDomain:(NSString *)currentDomain completionHandler:(void (^)(BOOL result))completionHandler WK_API_AVAILABLE(macosx(10.14), ios(12.0));
 
+- (void)_webView:(WKWebView *)webView didChangeFontAttributes:(NSDictionary<NSString *, id> *)fontAttributes 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));
 - (NSArray *)_webView:(WKWebView *)webView actionsForElement:(_WKActivatedElementInfo *)element defaultActions:(NSArray<_WKElementAction *> *)defaultActions;
index 092c654..0e7dccb 100644 (file)
@@ -1250,6 +1250,10 @@ static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::Editor
 - (void)_didChangeEditorState
 {
     id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
+
+    // FIXME: We should either rename -_webView:editorStateDidChange: to clarify that it's only intended for use when testing,
+    // or remove it entirely and use -_webView:didChangeFontAttributes: instead once text alignment is supported in the set of
+    // font attributes.
     if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
         [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
 }
index 33f0a9c..9b40e6a 100644 (file)
@@ -121,6 +121,8 @@ private:
         void didExceedBackgroundResourceLimitWhileInForeground(WebPageProxy&, WKResourceLimit) final;
         void saveDataToFileInDownloadsFolder(WebPageProxy*, const WTF::String&, const WTF::String&, const WebCore::URL&, API::Data&) final;
 #endif
+        bool needsFontAttributes() const final { return m_uiDelegate.m_delegateMethods.webViewDidChangeFontAttributes; }
+        void didChangeFontAttributes(const WebCore::FontAttributes&) final;
         bool decidePolicyForUserMediaPermissionRequest(WebPageProxy&, WebFrameProxy&, API::SecurityOrigin&, API::SecurityOrigin&, UserMediaPermissionRequestProxy&) final;
         bool checkUserMediaPermissionForOrigin(WebPageProxy&, WebFrameProxy&, API::SecurityOrigin&, API::SecurityOrigin&, UserMediaPermissionCheckProxy&) final;
         void mediaCaptureStateDidChange(WebCore::MediaProducer::MediaStateFlags) final;
@@ -198,6 +200,7 @@ private:
         bool webViewRequestUserMediaAuthorizationForDevicesURLMainFrameURLDecisionHandler : 1;
         bool webViewCheckUserMediaPermissionForURLMainFrameURLFrameIdentifierDecisionHandler : 1;
         bool webViewMediaCaptureStateDidChange : 1;
+        bool webViewDidChangeFontAttributes : 1;
 #if PLATFORM(IOS)
 #if HAVE(APP_LINKS)
         bool webViewShouldIncludeAppLinkActionsForElement : 1;
index 3ece820..122fc7e 100644 (file)
@@ -50,6 +50,7 @@
 #import "_WKContextMenuElementInfo.h"
 #import "_WKFrameHandleInternal.h"
 #import "_WKHitTestResultInternal.h"
+#import <WebCore/FontAttributes.h>
 #import <WebCore/SecurityOriginData.h>
 #import <WebCore/URL.h>
 #import <wtf/BlockPtr.h>
@@ -152,6 +153,7 @@ void UIDelegate::setDelegate(id <WKUIDelegate> delegate)
     m_delegateMethods.webViewRequestUserMediaAuthorizationForDevicesURLMainFrameURLDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:)];
     m_delegateMethods.webViewCheckUserMediaPermissionForURLMainFrameURLFrameIdentifierDecisionHandler = [delegate respondsToSelector:@selector(_webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:)];
     m_delegateMethods.webViewMediaCaptureStateDidChange = [delegate respondsToSelector:@selector(_webView:mediaCaptureStateDidChange:)];
+    m_delegateMethods.webViewDidChangeFontAttributes = [delegate respondsToSelector:@selector(_webView:didChangeFontAttributes:)];
     m_delegateMethods.dataDetectionContextForWebView = [delegate respondsToSelector:@selector(_dataDetectionContextForWebView:)];
     m_delegateMethods.webViewImageOrMediaDocumentSizeChanged = [delegate respondsToSelector:@selector(_webView:imageOrMediaDocumentSizeChanged:)];
 
@@ -883,6 +885,15 @@ static void requestUserMediaAuthorizationForDevices(const WebFrameProxy& frame,
 }
 #endif
 
+void UIDelegate::UIClient::didChangeFontAttributes(const WebCore::FontAttributes& fontAttributes)
+{
+    if (!needsFontAttributes())
+        return;
+
+    auto privateUIDelegate = (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get();
+    [privateUIDelegate _webView:m_uiDelegate.m_webView didChangeFontAttributes:fontAttributes.createDictionary().get()];
+}
+
 bool UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest(WebPageProxy& page, WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, UserMediaPermissionRequestProxy& request)
 {
 #if ENABLE(MEDIA_STREAM)
index 422f26a..ea0f067 100644 (file)
@@ -606,6 +606,7 @@ void WebPageProxy::setUIClient(std::unique_ptr<API::UIClient>&& uiClient)
 
     m_process->send(Messages::WebPage::SetCanRunBeforeUnloadConfirmPanel(m_uiClient->canRunBeforeUnloadConfirmPanel()), m_pageID);
     setCanRunModal(m_uiClient->canRunModal());
+    setNeedsFontAttributes(m_uiClient->needsFontAttributes());
 }
 
 void WebPageProxy::setIconLoadingClient(std::unique_ptr<API::IconLoadingClient>&& iconLoadingClient)
@@ -1733,6 +1734,30 @@ void WebPageProxy::validateCommand(const String& commandName, WTF::Function<void
     m_process->send(Messages::WebPage::ValidateCommand(commandName, callbackID), m_pageID);
 }
 
+void WebPageProxy::updateFontAttributesAfterEditorStateChange()
+{
+    m_cachedFontAttributesAtSelectionStart.reset();
+
+    if (m_editorState.isMissingPostLayoutData)
+        return;
+
+    if (auto fontAttributes = m_editorState.postLayoutData().fontAttributes) {
+        m_uiClient->didChangeFontAttributes(*fontAttributes);
+        m_cachedFontAttributesAtSelectionStart = WTFMove(fontAttributes);
+    }
+}
+
+void WebPageProxy::setNeedsFontAttributes(bool needsFontAttributes)
+{
+    if (m_needsFontAttributes == needsFontAttributes)
+        return;
+
+    m_needsFontAttributes = needsFontAttributes;
+
+    if (isValid())
+        m_process->send(Messages::WebPage::SetNeedsFontAttributes(needsFontAttributes), m_pageID);
+}
+
 bool WebPageProxy::maintainsInactiveSelection() const
 {
     // Regardless of what the client wants to do, keep selections if a local Inspector is open.
@@ -6354,6 +6379,8 @@ WebPageCreationParameters WebPageProxy::creationParameters()
     parameters.applicationManifest = m_configuration->applicationManifest() ? std::optional<WebCore::ApplicationManifest>(m_configuration->applicationManifest()->applicationManifest()) : std::nullopt;
 #endif
 
+    parameters.needsFontAttributes = m_needsFontAttributes;
+
     m_process->addWebUserContentControllerProxy(m_userContentController, parameters);
 
     return parameters;
index e66c54a..5b12c58 100644 (file)
@@ -1847,6 +1847,9 @@ private:
 
     void continueNavigationInNewProcess(API::Navigation&, Ref<WebProcessProxy>&&);
 
+    void setNeedsFontAttributes(bool);
+    void updateFontAttributesAfterEditorStateChange();
+
     WeakPtr<PageClient> m_pageClient;
     Ref<API::PageConfiguration> m_configuration;
 
@@ -2259,6 +2262,8 @@ private:
 
     RunLoop::Timer<WebPageProxy> m_resetRecentCrashCountTimer;
     unsigned m_recentCrashCount { 0 };
+
+    bool m_needsFontAttributes { false };
 };
 
 } // namespace WebKit
index fcc1647..394a565 100644 (file)
@@ -1053,7 +1053,6 @@ void WebPageProxy::editorStateChanged(const EditorState& editorState)
     bool couldChangeSecureInputState = m_editorState.isInPasswordField != editorState.isInPasswordField || m_editorState.selectionIsNone;
     
     m_editorState = editorState;
-    m_cachedFontAttributesAtSelectionStart.reset();
     
     // Selection being none is a temporary state when editing. Flipping secure input state too quickly was causing trouble (not fully understood).
     if (couldChangeSecureInputState && !editorState.selectionIsNone)
@@ -1065,6 +1064,7 @@ void WebPageProxy::editorStateChanged(const EditorState& editorState)
     // We always need to notify the client on iOS to make sure the selection is redrawn,
     // even during composition to support phrase boundary gesture.
     pageClient().selectionDidChange();
+    updateFontAttributesAfterEditorStateChange();
 }
 
 void WebPageProxy::showValidationMessage(const IntRect& anchorClientRect, const String& message)
index 48418cc..f312348 100644 (file)
@@ -650,7 +650,6 @@ void WebPageProxy::editorStateChanged(const EditorState& editorState)
     bool couldChangeSecureInputState = m_editorState.isInPasswordField != editorState.isInPasswordField || m_editorState.selectionIsNone;
     
     m_editorState = editorState;
-    m_cachedFontAttributesAtSelectionStart.reset();
     
     // Selection being none is a temporary state when editing. Flipping secure input state too quickly was causing trouble (not fully understood).
     if (couldChangeSecureInputState && !editorState.selectionIsNone)
@@ -660,6 +659,7 @@ void WebPageProxy::editorStateChanged(const EditorState& editorState)
         return;
     
     pageClient().selectionDidChange();
+    updateFontAttributesAfterEditorStateChange();
 }
 
 void WebPageProxy::startWindowDrag()
index 22b8090..432dc89 100644 (file)
@@ -599,6 +599,8 @@ WebPage::WebPage(uint64_t pageID, WebPageCreationParameters&& parameters)
         ServiceWorkerProvider::singleton().setMayHaveRegisteredServiceWorkers();
 #endif
 
+    m_needsFontAttributes = parameters.needsFontAttributes;
+
 #if ENABLE(WEB_RTC)
     if (!parameters.iceCandidateFilteringEnabled)
         disableICECandidateFiltering();
@@ -935,6 +937,9 @@ EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayo
         postLayoutData.canCopy = editor.canCopy();
         postLayoutData.canPaste = editor.canPaste();
 
+        if (m_needsFontAttributes)
+            postLayoutData.fontAttributes = editor.fontAttributesAtSelectionStart();
+
 #if PLATFORM(COCOA)
         if (result.isContentEditable && !selection.isNone()) {
             if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection)) {
@@ -2491,6 +2496,17 @@ void WebPage::executeEditCommand(const String& commandName, const String& argume
     executeEditingCommand(commandName, argument);
 }
 
+void WebPage::setNeedsFontAttributes(bool needsFontAttributes)
+{
+    if (m_needsFontAttributes == needsFontAttributes)
+        return;
+
+    m_needsFontAttributes = needsFontAttributes;
+
+    if (m_needsFontAttributes)
+        sendPartialEditorStateAndSchedulePostLayoutUpdate();
+}
+
 void WebPage::restoreSessionInternal(const Vector<BackForwardListItemState>& itemStates, WasRestoredByAPIRequest restoredByAPIRequest, WebBackForwardListProxy::OverwriteExistingItem overwrite)
 {
     for (const auto& itemState : itemStates) {
index 150ab4f..b12b063 100644 (file)
@@ -1179,6 +1179,8 @@ private:
     void executeEditCommand(const String&, const String&);
     void setEditable(bool);
 
+    void setNeedsFontAttributes(bool);
+
     void mouseEvent(const WebMouseEvent&);
     void keyEvent(const WebKeyboardEvent&);
 
@@ -1740,6 +1742,7 @@ private:
 #endif
 
     bool m_isSuspended { false };
+    bool m_needsFontAttributes { false };
 };
 
 } // namespace WebKit
index d75acee..94cafe6 100644 (file)
@@ -209,6 +209,8 @@ messages -> WebPage LegacyReceiver {
     ValidateCommand(String name, WebKit::CallbackID callbackID)
     ExecuteEditCommand(String name, String argument)
 
+    SetNeedsFontAttributes(bool needsFontAttributes)
+
     RequestFontAttributesAtSelectionStart(WebKit::CallbackID callbackID)
 
     DidRemoveEditCommand(uint64_t commandID)
index 1e46e8b..c5faea6 100644 (file)
@@ -1,3 +1,28 @@
+2018-10-04  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Cocoa] Add SPI to expose typing attributes at the current selection on WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=190119
+        <rdar://problem/44767062>
+
+        Reviewed by Tim Horton.
+
+        Add a new API test that loads a document containing various rich text styles. This test moves the selection
+        around the document and checks the last set of font attributes received via the new UI delegate hook.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/FontAttributes.mm: Added.
+        (-[FontAttributesListener _webView:didChangeFontAttributes:]):
+        (-[FontAttributesListener lastFontAttributes]):
+        (-[TestWKWebView selectElementWithIdentifier:]):
+        (-[TestWKWebView fontAttributesAfterNextPresentationUpdate]):
+        (ColorExpectation::ColorExpectation):
+        (ShadowExpectation::ShadowExpectation):
+        (checkColor):
+        (checkShadow):
+        (checkFont):
+        (webViewForTestingFontAttributes):
+        * TestWebKitAPI/Tests/WebKitCocoa/rich-text-attributes.html: Added.
+
 2018-10-04  Alan Coon  <alancoon@apple.com>
 
         Unreviewed, add self as contributor.
index 04b5da5..a50f547 100644 (file)
                2E691AF31D79E75E00129407 /* large-video-playing-scroll-away.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E691AF21D79E75400129407 /* large-video-playing-scroll-away.html */; };
                2E7765CD16C4D80A00BA2BB1 /* mainIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */; };
                2E7765CF16C4D81100BA2BB1 /* mainMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CE16C4D81100BA2BB1 /* mainMac.mm */; };
+               2E92B8F7216490D4005B64F0 /* rich-text-attributes.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E92B8F6216490C3005B64F0 /* rich-text-attributes.html */; };
+               2E92B8FA2164A0C1005B64F0 /* FontAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E92B8F8216490EA005B64F0 /* FontAttributes.mm */; };
                2E9896151D8F093800739892 /* text-and-password-inputs.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E9896141D8F092B00739892 /* text-and-password-inputs.html */; };
                2EB29D5E1F762DB90023A5F1 /* dump-datatransfer-types.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */; };
                2EBD9D0A2134730D002DA758 /* video.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 07CD32F72065B72A0064A4BE /* video.html */; };
                                F6FDDDD614241C6F004F1729 /* push-state.html in Copy Resources */,
                                A12DDC001E8373E700CF6CAE /* rendered-image-excluding-overflow.html in Copy Resources */,
                                F46849C01EEF5EF300B937FE /* rich-and-plain-text.html in Copy Resources */,
+                               2E92B8F7216490D4005B64F0 /* rich-text-attributes.html in Copy Resources */,
                                0F5651F91FCE513500310FBC /* scroll-to-anchor.html in Copy Resources */,
                                F4E0A296211FC5FB00AF7C7F /* selected-text-and-textarea.html in Copy Resources */,
                                F4D65DA81F5E4704009D8C27 /* selected-text-image-link-and-editable.html in Copy Resources */,
                2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainIOS.mm; sourceTree = "<group>"; };
                2E7765CE16C4D81100BA2BB1 /* mainMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainMac.mm; sourceTree = "<group>"; };
                2E7EF7AC1F266A8100DFB67C /* AppKitSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppKitSPI.h; sourceTree = "<group>"; };
+               2E92B8F6216490C3005B64F0 /* rich-text-attributes.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "rich-text-attributes.html"; sourceTree = "<group>"; };
+               2E92B8F8216490EA005B64F0 /* FontAttributes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FontAttributes.mm; sourceTree = "<group>"; };
                2E9896141D8F092B00739892 /* text-and-password-inputs.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "text-and-password-inputs.html"; sourceTree = "<group>"; };
                2EB29D5D1F762DA50023A5F1 /* dump-datatransfer-types.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "dump-datatransfer-types.html"; sourceTree = "<group>"; };
                2ECFF5541D9B12F800B55394 /* NowPlayingControlsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NowPlayingControlsTests.mm; sourceTree = "<group>"; };
                                CDA29B2820FD2A9900F15CED /* ExitFullscreenOnEnterPiP.mm */,
                                2D8104CB1BEC13E70020DA46 /* FindInPage.mm */,
                                2D1FE0AF1AD465C1006CD9E6 /* FixedLayoutSize.mm */,
+                               2E92B8F8216490EA005B64F0 /* FontAttributes.mm */,
                                5CB5B3BD1FFC517E00C27BB0 /* FrameHandleSerialization.mm */,
                                CD78E11A1DB7EA360014A2DE /* FullscreenDelegate.mm */,
                                3F1B52681D3D7129008D60C4 /* FullscreenLayoutConstraints.mm */,
                                F41AB99A1EF4692C0083FA08 /* prevent-start.html */,
                                A12DDBFF1E8373C100CF6CAE /* rendered-image-excluding-overflow.html */,
                                F46849BF1EEF5EDC00B937FE /* rich-and-plain-text.html */,
+                               2E92B8F6216490C3005B64F0 /* rich-text-attributes.html */,
                                F4E0A295211FC5A300AF7C7F /* selected-text-and-textarea.html */,
                                F4D65DA71F5E46C0009D8C27 /* selected-text-image-link-and-editable.html */,
                                F4E3D80720F708E4007B58C5 /* significant-text-milestone-article.html */,
                                7A909A7F1D877480007E10F8 /* FloatRect.cpp in Sources */,
                                7A909A801D877480007E10F8 /* FloatSize.cpp in Sources */,
                                F4BC0B142146C849002A0478 /* FocusPreservationTests.mm in Sources */,
+                               2E92B8FA2164A0C1005B64F0 /* FontAttributes.mm in Sources */,
                                1CAD1F861E5CE7DA00AF2C2C /* FontCache.cpp in Sources */,
                                F456AB1C213EDBA300CB2CEF /* FontManagerTests.mm in Sources */,
                                7CCE7EF51A411AE600447C4C /* ForceRepaint.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/FontAttributes.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/FontAttributes.mm
new file mode 100644 (file)
index 0000000..0b98b39
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#import "config.h"
+
+#if PLATFORM(COCOA) && WK_API_ENABLED
+
+#import "NSFontPanelTesting.h"
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKUIDelegatePrivate.h>
+#import <cmath>
+#import <wtf/Optional.h>
+
+@interface FontAttributesListener : NSObject <WKUIDelegatePrivate>
+@property (nonatomic, readonly) NSDictionary *lastFontAttributes;
+@end
+
+@implementation FontAttributesListener {
+    RetainPtr<NSDictionary> _lastFontAttributes;
+}
+
+- (void)_webView:(WKWebView *)webView didChangeFontAttributes:(NSDictionary<NSString *, id> *)fontAttributes
+{
+    _lastFontAttributes = fontAttributes;
+}
+
+- (NSDictionary *)lastFontAttributes
+{
+    return _lastFontAttributes.get();
+}
+
+@end
+
+@interface TestWKWebView (FontAttributesTesting)
+- (void)selectElementWithIdentifier:(NSString *)identifier;
+- (NSDictionary *)fontAttributesAfterNextPresentationUpdate;
+@end
+
+@implementation TestWKWebView (FontAttributesTesting)
+
+- (void)selectElementWithIdentifier:(NSString *)identifier
+{
+    [self objectByEvaluatingJavaScript:[NSString stringWithFormat:
+        @"element = document.getElementById('%@');"
+        "range = document.createRange();"
+        "range.selectNodeContents(element);"
+        "getSelection().removeAllRanges();"
+        "getSelection().addRange(range)", identifier]];
+}
+
+- (NSDictionary *)fontAttributesAfterNextPresentationUpdate
+{
+    [self waitForNextPresentationUpdate];
+    return [(FontAttributesListener *)self.UIDelegate lastFontAttributes];
+}
+
+@end
+
+#if PLATFORM(MAC)
+#define PlatformColor NSColor
+#define PlatformFont NSFont
+#else
+#define PlatformColor UIColor
+#define PlatformFont UIFont
+static NSString *const NSSuperscriptAttributeName = @"NSSuperscript";
+#endif
+
+namespace TestWebKitAPI {
+
+enum class Nullity : uint8_t { Null, NonNull };
+
+struct ColorExpectation {
+    ColorExpectation(CGFloat redValue, CGFloat greenValue, CGFloat blueValue, CGFloat alphaValue)
+        : red(redValue)
+        , green(greenValue)
+        , blue(blueValue)
+        , alpha(alphaValue)
+        , nullity(Nullity::NonNull)
+    {
+    }
+
+    ColorExpectation() = default;
+
+    CGFloat red { 0 };
+    CGFloat green { 0 };
+    CGFloat blue { 0 };
+    CGFloat alpha { 0 };
+    Nullity nullity { Nullity::Null };
+};
+
+struct ShadowExpectation {
+    ShadowExpectation(CGFloat opacityValue, CGFloat blurRadiusValue)
+        : opacity(opacityValue)
+        , blurRadius(blurRadiusValue)
+        , nullity(Nullity::NonNull)
+    {
+    }
+
+    ShadowExpectation() = default;
+
+    CGFloat opacity { 0 };
+    CGFloat blurRadius { 0 };
+    Nullity nullity { Nullity::Null };
+};
+
+struct FontExpectation {
+    const char* fontName;
+    CGFloat fontSize;
+};
+
+static void checkColor(PlatformColor *color, ColorExpectation&& expectation)
+{
+    if (expectation.nullity == Nullity::Null) {
+        EXPECT_NULL(color);
+        return;
+    }
+
+    EXPECT_NOT_NULL(color);
+
+    CGFloat observedRed = 0;
+    CGFloat observedGreen = 0;
+    CGFloat observedBlue = 0;
+    CGFloat observedAlpha = 0;
+    [color getRed:&observedRed green:&observedGreen blue:&observedBlue alpha:&observedAlpha];
+    EXPECT_EQ(expectation.red, std::round(observedRed * 255));
+    EXPECT_EQ(expectation.green, std::round(observedGreen * 255));
+    EXPECT_EQ(expectation.blue, std::round(observedBlue * 255));
+    EXPECT_LT(std::abs(expectation.alpha - observedAlpha), 0.0001);
+}
+
+static void checkShadow(NSShadow *shadow, ShadowExpectation&& expectation)
+{
+    if (expectation.nullity == Nullity::Null) {
+        EXPECT_NULL(shadow);
+        return;
+    }
+
+    EXPECT_NOT_NULL(shadow);
+
+    CGFloat observedAlpha = 0;
+    [shadow.shadowColor getRed:nullptr green:nullptr blue:nullptr alpha:&observedAlpha];
+    EXPECT_LT(std::abs(expectation.opacity - observedAlpha), 0.0001);
+    EXPECT_EQ(expectation.blurRadius, shadow.shadowBlurRadius);
+}
+
+static void checkFont(PlatformFont *font, FontExpectation&& expectation)
+{
+    EXPECT_WK_STREQ(expectation.fontName, font.fontName);
+    EXPECT_EQ(expectation.fontSize, font.pointSize);
+}
+
+static RetainPtr<TestWKWebView> webViewForTestingFontAttributes()
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"rich-text-attributes"];
+    [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+    return webView;
+}
+
+TEST(FontAttributes, FontAttributesAfterChangingSelection)
+{
+    auto delegate = adoptNS([FontAttributesListener new]);
+    auto webView = webViewForTestingFontAttributes();
+    [webView setUIDelegate:delegate.get()];
+
+    {
+        [webView selectElementWithIdentifier:@"one"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 227, 36, 0, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { 255, 199, 119, 1 });
+        checkFont(attributes[NSFontAttributeName], { "Helvetica-Bold", 48 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleSingle, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"two"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 102, 157, 52, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { 255, 197, 171, 1 });
+        checkFont(attributes[NSFontAttributeName], { "Helvetica-Bold", 48 });
+        checkShadow(attributes[NSShadowAttributeName], { 0.470588, 5 });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleSingle, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"three"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 255, 106, 0, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "Menlo-Italic", 18 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"four"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 255, 255, 255, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { 0, 0, 0, 1 });
+        checkFont(attributes[NSFontAttributeName], { "Avenir-Book", 24 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"five"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 131, 16, 0, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "TimesNewRomanPS-BoldMT", 24 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"six"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 255, 64, 19, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "Avenir-Black", 18 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"seven"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { 235, 235, 235, 1 });
+        checkColor(attributes[NSBackgroundColorAttributeName], { 78, 122, 39, 1 });
+        checkFont(attributes[NSFontAttributeName], { "Avenir-BookOblique", 12 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(-1, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"eight"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "Avenir-Book", 12 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(1, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"nine"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "Georgia", 36 });
+        checkShadow(attributes[NSShadowAttributeName], { 0.658824, 9 });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+    {
+        [webView selectElementWithIdentifier:@"ten"];
+        NSDictionary *attributes = [webView fontAttributesAfterNextPresentationUpdate];
+        checkColor(attributes[NSForegroundColorAttributeName], { });
+        checkColor(attributes[NSBackgroundColorAttributeName], { });
+        checkFont(attributes[NSFontAttributeName], { "Avenir-BookOblique", 18 });
+        checkShadow(attributes[NSShadowAttributeName], { });
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSStrikethroughStyleAttributeName] integerValue]);
+        EXPECT_EQ(NSUnderlineStyleNone, [attributes[NSUnderlineStyleAttributeName] integerValue]);
+        EXPECT_EQ(0, [attributes[NSSuperscriptAttributeName] integerValue]);
+    }
+}
+
+} // namespace TestWebKitAPI
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/rich-text-attributes.html b/Tools/TestWebKitAPI/Tests/WebKitCocoa/rich-text-attributes.html
new file mode 100644 (file)
index 0000000..5e8ce04
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf8">
+<html>
+<head>
+    <style>
+        body, html {
+            font-family: "Helvetica";
+        }
+    </style>
+</head>
+<body contenteditable>
+    <div>
+        <div style="text-align: left;">
+            <span id="one" style="font-size: 48px; font-weight: bold; color: rgb(227, 36, 0); background-color: rgb(255, 199, 119); text-decoration: line-through;">One</span><span id="two" style="caret-color: rgb(102, 157, 52); color: rgb(102, 157, 52); background-color: rgb(255, 197, 171); text-decoration: underline; font-weight: bold; font-size: 48px; text-shadow: rgba(0, 0, 0, 0.470588) 0px 0px 5px;">Two</span>
+        </div>
+        <div style="text-align: center;">
+            <span id="three" style="color: rgb(255, 106, 0); font-family: Menlo; font-size: 18px; font-style: italic;">Three</span><span style="font-size: 24px;"><span id="four" style="font-family: Avenir-Book; background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);">Four</span><span id="five" style="color: rgb(131, 16, 0); font-weight: bold; font-family: 'Times New Roman';">Five</span></span>
+        </div>
+    </div>
+    <div style="text-align: left;">
+        <ul>
+            <li><span id="six" style="color: rgb(255, 64, 19); font-family: Avenir-Book; font-size: 18px; font-weight: bold;">Six</span><br></li>
+            <li><span id="seven" style="font-family: Avenir-Book; font-style: italic; color: rgb(235, 235, 235); background-color: rgb(78, 122, 39); vertical-align: sub; font-size: 12px;">Seven</span></li>
+        </ul>
+        <ol>
+            <li><span id="eight" style="font-family: Avenir-Book; vertical-align: super; font-size: 12px;">Eight</span></li>
+            <li><span id="nine" style="font-family: Georgia; font-style: normal; font-size: 36px; text-shadow: rgba(0, 0, 0, 0.658824) 0px 0px 9px;">Nine</span></li>
+        </ol>
+        <div style="text-align: right;">
+            <span id="ten" style="font-family: Avenir-Book; font-size: 18px; font-style: oblique;">Ten</span>
+        </div>
+    </div>
+</body>
+</html>
\ No newline at end of file