Diff
Modified: trunk/Source/WebCore/ChangeLog (236853 => 236854)
--- trunk/Source/WebCore/ChangeLog 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Source/WebCore/ChangeLog 2018-10-04 22:08:51 UTC (rev 236854)
@@ -1,3 +1,33 @@
+2018-10-04 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [macOS] Fix some font attribute conversion bugs in preparation for "Font > Styles…" support in WebKit2
+ https://bugs.webkit.org/show_bug.cgi?id=190289
+ <rdar://problem/45020806>
+
+ Reviewed by Ryosuke Niwa.
+
+ Makes some small adjustments to fix two bugs in font attribute conversion logic. See below for more detail.
+
+ Tests: FontManagerTests.AddFontShadowUsingFontOptions
+ FontManagerTests.AddAndRemoveColorsUsingFontOptions
+
+ * editing/FontAttributeChanges.cpp:
+ (WebCore::cssValueListForShadow):
+ * editing/cocoa/FontAttributesCocoa.mm:
+
+ Currently, we bail from adding a font shadow if the shadow's offset is empty. However, valid shadow offsets may
+ have negative dimensions, so a check for `isZero()` should be used instead.
+
+ (WebCore::FontAttributes::createDictionary const):
+ * platform/mac/WebCoreNSFontManagerExtras.mm:
+
+ Fall back to a transparent background color; this allows senders to remove the current background color by just
+ removing NSBackgroundColorAttributeName from the attribute dictionary, rather than explicitly setting it to the
+ transparent color (this scenario is exercised when using "Font > Styles…" to specify a font style without a
+ background color).
+
+ (WebCore::computedFontAttributeChanges):
+
2018-10-03 Ryosuke Niwa <rn...@webkit.org>
MutationRecord doesn't keep JS wrappers of target, addedNodes, and removedNodes alive
Modified: trunk/Source/WebCore/editing/FontAttributeChanges.cpp (236853 => 236854)
--- trunk/Source/WebCore/editing/FontAttributeChanges.cpp 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Source/WebCore/editing/FontAttributeChanges.cpp 2018-10-04 22:08:51 UTC (rev 236854)
@@ -81,7 +81,7 @@
static RefPtr<CSSValueList> cssValueListForShadow(const FontShadow& shadow)
{
- if (shadow.offset.isEmpty() && !shadow.blurRadius)
+ if (shadow.offset.isZero() && !shadow.blurRadius)
return nullptr;
auto list = CSSValueList::createCommaSeparated();
Modified: trunk/Source/WebCore/editing/cocoa/FontAttributesCocoa.mm (236853 => 236854)
--- trunk/Source/WebCore/editing/cocoa/FontAttributesCocoa.mm 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Source/WebCore/editing/cocoa/FontAttributesCocoa.mm 2018-10-04 22:08:51 UTC (rev 236854)
@@ -42,10 +42,8 @@
if (backgroundColor.isValid())
attributes[NSBackgroundColorAttributeName] = platformColor(backgroundColor);
- if (fontShadow.color.isValid() && (!fontShadow.offset.isEmpty() || fontShadow.blurRadius)) {
- auto shadow = fontShadow.createShadow();
- attributes[NSShadowAttributeName] = shadow.get();
- }
+ if (fontShadow.color.isValid() && (!fontShadow.offset.isZero() || fontShadow.blurRadius))
+ attributes[NSShadowAttributeName] = fontShadow.createShadow().get();
if (subscriptOrSuperscript == SubscriptOrSuperscript::Subscript)
attributes[NSSuperscriptAttributeName] = @(-1);
Modified: trunk/Source/WebCore/platform/mac/WebCoreNSFontManagerExtras.mm (236853 => 236854)
--- trunk/Source/WebCore/platform/mac/WebCoreNSFontManagerExtras.mm 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Source/WebCore/platform/mac/WebCoreNSFontManagerExtras.mm 2018-10-04 22:08:51 UTC (rev 236854)
@@ -126,7 +126,7 @@
NSColor *convertedBackgroundColorA = [convertedAttributesA objectForKey:NSBackgroundColorAttributeName];
if (convertedBackgroundColorA == [convertedAttributesB objectForKey:NSBackgroundColorAttributeName])
- changes.setBackgroundColor(colorFromNSColor(convertedBackgroundColorA));
+ changes.setBackgroundColor(colorFromNSColor(convertedBackgroundColorA ?: NSColor.clearColor));
changes.setFontChanges(computedFontChanges(fontManager, originalFontA, [convertedAttributesA objectForKey:NSFontAttributeName], [convertedAttributesB objectForKey:NSFontAttributeName]));
Modified: trunk/Tools/ChangeLog (236853 => 236854)
--- trunk/Tools/ChangeLog 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Tools/ChangeLog 2018-10-04 22:08:51 UTC (rev 236854)
@@ -1,3 +1,46 @@
+2018-10-04 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [macOS] Fix some font attribute conversion bugs in preparation for "Font > Styles…" support in WebKit2
+ https://bugs.webkit.org/show_bug.cgi?id=190289
+ <rdar://problem/45020806>
+
+ Reviewed by Ryosuke Niwa.
+
+ Add new API tests to exercise two corner cases when using NSFontOptions ("Font > Styles…") to change font
+ attributes at the current selection.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/TestWebKitAPI/mac/AppKitSPI.h:
+ * TestWebKitAPI/Tests/mac/FontManagerTests.mm:
+ (webViewForFontManagerTesting):
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/mac/TestFontOptions.h: Copied from Source/WebCore/editing/cocoa/FontAttributesCocoa.mm.
+ * TestWebKitAPI/mac/TestFontOptions.mm: Added.
+
+ Introduce TestFontOptions, which wraps the shared NSFontOptions and swizzles `-sharedFontOptions` to return a
+ global instance of itself. TestFontOptions supports several testing helpers to add or remove font shadows,
+ foreground colors, and background colors.
+
+ (sharedFontOptionsForTesting):
+ (+[TestFontOptions sharedInstance]):
+ (-[TestFontOptions initWithFontOptions:]):
+ (-[TestFontOptions selectedAttributes]):
+ (-[TestFontOptions fontOptions]):
+ (-[TestFontOptions shadowWidth]):
+ (-[TestFontOptions setShadowWidth:]):
+ (-[TestFontOptions shadowHeight]):
+ (-[TestFontOptions setShadowHeight:]):
+ (-[TestFontOptions setShadowBlurRadius:]):
+ (-[TestFontOptions setHasShadow:]):
+ (-[TestFontOptions foregroundColor]):
+ (-[TestFontOptions setForegroundColor:]):
+ (-[TestFontOptions backgroundColor]):
+ (-[TestFontOptions setBackgroundColor:]):
+ (-[TestFontOptions _dispatchFontAttributeChanges]):
+ (-[TestFontOptions convertAttributes:]):
+ (-[TestFontOptions setSelectedAttributes:isMultiple:]):
+ (-[TestFontOptions forwardInvocation:]):
+
2018-10-04 Jiewen Tan <jiewen_...@apple.com>
[WebAuthN] Move time out control from WebProcess to UIProcess
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (236853 => 236854)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-10-04 22:08:51 UTC (rev 236854)
@@ -856,6 +856,7 @@
F4F137921D9B683E002BEC57 /* large-video-test-now-playing.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4F137911D9B6832002BEC57 /* large-video-test-now-playing.html */; };
F4F405BC1D4C0D1C007A9707 /* full-size-autoplaying-video-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4F405BA1D4C0CF8007A9707 /* full-size-autoplaying-video-with-audio.html */; };
F4F405BD1D4C0D1C007A9707 /* skinny-autoplaying-video-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4F405BB1D4C0CF8007A9707 /* skinny-autoplaying-video-with-audio.html */; };
+ F4F5BB5221667BAA002D06B9 /* TestFontOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4F5BB5121667BAA002D06B9 /* TestFontOptions.mm */; };
F4FA91811E61849B007B8C1D /* WKWebViewMacEditingTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4FA917F1E61849B007B8C1D /* WKWebViewMacEditingTests.mm */; };
F4FA91831E61857B007B8C1D /* double-click-does-not-select-trailing-space.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4FA91821E618566007B8C1D /* double-click-does-not-select-trailing-space.html */; };
F4FC077720F013B600CA043C /* significant-text-milestone.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4FC077620F0108100CA043C /* significant-text-milestone.html */; };
@@ -2114,6 +2115,8 @@
F4F137911D9B6832002BEC57 /* large-video-test-now-playing.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-test-now-playing.html"; sourceTree = "<group>"; };
F4F405BA1D4C0CF8007A9707 /* full-size-autoplaying-video-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "full-size-autoplaying-video-with-audio.html"; sourceTree = "<group>"; };
F4F405BB1D4C0CF8007A9707 /* skinny-autoplaying-video-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "skinny-autoplaying-video-with-audio.html"; sourceTree = "<group>"; };
+ F4F5BB5021667BAA002D06B9 /* TestFontOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestFontOptions.h; sourceTree = "<group>"; };
+ F4F5BB5121667BAA002D06B9 /* TestFontOptions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestFontOptions.mm; sourceTree = "<group>"; };
F4FA917F1E61849B007B8C1D /* WKWebViewMacEditingTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewMacEditingTests.mm; sourceTree = "<group>"; };
F4FA91821E618566007B8C1D /* double-click-does-not-select-trailing-space.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = "double-click-does-not-select-trailing-space.html"; path = "Tests/WebKitCocoa/double-click-does-not-select-trailing-space.html"; sourceTree = SOURCE_ROOT; };
F4FC077620F0108100CA043C /* significant-text-milestone.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "significant-text-milestone.html"; sourceTree = "<group>"; };
@@ -3174,6 +3177,8 @@
F46128CA211D475100D9FADB /* TestDraggingInfo.mm */,
F4E0A2B62122847400AF7C7F /* TestFilePromiseReceiver.h */,
F4E0A2B72122847400AF7C7F /* TestFilePromiseReceiver.mm */,
+ F4F5BB5021667BAA002D06B9 /* TestFontOptions.h */,
+ F4F5BB5121667BAA002D06B9 /* TestFontOptions.mm */,
F45D388F215A7B4B002A2979 /* TestInspectorBar.h */,
F45D3890215A7B4B002A2979 /* TestInspectorBar.mm */,
C08587BE13FE956C001EF4E5 /* WebKitAgnosticTest.h */,
@@ -3995,6 +4000,7 @@
7CCE7EA91A411A1D00447C4C /* TestBrowsingContextLoadDelegate.mm in Sources */,
F46128CB211D475100D9FADB /* TestDraggingInfo.mm in Sources */,
F4E0A2B82122847400AF7C7F /* TestFilePromiseReceiver.mm in Sources */,
+ F4F5BB5221667BAA002D06B9 /* TestFontOptions.mm in Sources */,
F45E15762112CE6200307E82 /* TestInputDelegate.mm in Sources */,
F45D3891215A7B4B002A2979 /* TestInspectorBar.mm in Sources */,
2D1C04A71D76298B000A6816 /* TestNavigationDelegate.mm in Sources */,
Modified: trunk/Tools/TestWebKitAPI/Tests/TestWebKitAPI/mac/AppKitSPI.h (236853 => 236854)
--- trunk/Tools/TestWebKitAPI/Tests/TestWebKitAPI/mac/AppKitSPI.h 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Tools/TestWebKitAPI/Tests/TestWebKitAPI/mac/AppKitSPI.h 2018-10-04 22:08:51 UTC (rev 236854)
@@ -103,4 +103,8 @@
- (void)menuNeedsUpdate:(NSMenu *)menu;
@end
+@interface NSFontOptions : NSObject
++ (instancetype)sharedFontOptions;
+@end
+
#endif // PLATFORM(MAC)
Modified: trunk/Tools/TestWebKitAPI/Tests/mac/FontManagerTests.mm (236853 => 236854)
--- trunk/Tools/TestWebKitAPI/Tests/mac/FontManagerTests.mm 2018-10-04 21:54:14 UTC (rev 236853)
+++ trunk/Tools/TestWebKitAPI/Tests/mac/FontManagerTests.mm 2018-10-04 22:08:51 UTC (rev 236854)
@@ -31,6 +31,7 @@
#import "AppKitSPI.h"
#import "NSFontPanelTesting.h"
#import "PlatformUtilities.h"
+#import "TestFontOptions.h"
#import "TestInspectorBar.h"
#import "TestWKWebView.h"
#import <WebKit/WKWebViewPrivate.h>
@@ -113,12 +114,10 @@
@end
-static RetainPtr<FontManagerTestWKWebView> webViewForFontManagerTesting(NSFontManager *fontManager)
+static RetainPtr<FontManagerTestWKWebView> webViewForFontManagerTesting(NSFontManager *fontManager, NSString *markup)
{
auto webView = adoptNS([[FontManagerTestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
- [webView synchronouslyLoadHTMLString:@"<body contenteditable>"
- "<span id='foo'>foo</span> <span id='bar'>bar</span> <span id='baz'>baz</span>"
- "</body><script>document.body.addEventListener('input', event => lastInputEvent = event)</script>"];
+ [webView synchronouslyLoadHTMLString:markup];
[webView stringByEvaluatingJavaScript:@"document.body.focus()"];
[webView _setEditable:YES];
fontManager.target = webView.get();
@@ -125,6 +124,13 @@
return webView;
}
+static RetainPtr<FontManagerTestWKWebView> webViewForFontManagerTesting(NSFontManager *fontManager)
+{
+ return webViewForFontManagerTesting(fontManager, @"<body contenteditable>"
+ "<span id='foo'>foo</span> <span id='bar'>bar</span> <span id='baz'>baz</span>"
+ "</body><script>document.body.addEventListener('input', event => lastInputEvent = event)</script>");
+}
+
static RetainPtr<NSMenuItemCell> menuItemCellForFontAction(NSUInteger tag)
{
auto menuItem = adoptNS([[NSMenuItem alloc] init]);
@@ -474,6 +480,48 @@
EXPECT_EQ(0, [[webView typingAttributes][NSSuperscriptAttributeName] integerValue]);
}
+TEST(FontManagerTests, AddFontShadowUsingFontOptions)
+{
+ TestFontOptions *options = TestFontOptions.sharedInstance;
+ auto webView = webViewForFontManagerTesting(NSFontManager.sharedFontManager);
+
+ [webView selectWord:nil];
+ options.shadowWidth = 3;
+ options.shadowHeight = -3;
+ options.hasShadow = YES;
+
+ EXPECT_WK_STREQ("rgba(0, 0, 0, 0.333333) 3px -3px 0px", [webView stylePropertyAtSelectionStart:@"text-shadow"]);
+ [webView waitForNextPresentationUpdate];
+ NSShadow *shadow = [webView typingAttributes][NSShadowAttributeName];
+ EXPECT_EQ(shadow.shadowOffset.width, 3);
+ EXPECT_EQ(shadow.shadowOffset.height, -3);
+}
+
+TEST(FontManagerTests, AddAndRemoveColorsUsingFontOptions)
+{
+ TestFontOptions *options = TestFontOptions.sharedInstance;
+ auto webView = webViewForFontManagerTesting(NSFontManager.sharedFontManager, @"<body contenteditable>hello</body>");
+ [webView selectWord:nil];
+
+ options.backgroundColor = [NSColor colorWithRed:1 green:0 blue:0 alpha:0.2];
+ options.foregroundColor = [NSColor colorWithRed:0 green:0 blue:1 alpha:1];
+
+ EXPECT_WK_STREQ("rgb(0, 0, 255)", [webView stylePropertyAtSelectionStart:@"color"]);
+ EXPECT_WK_STREQ("rgba(255, 0, 0, 0.2)", [webView stylePropertyAtSelectionStart:@"background-color"]);
+ NSDictionary *attributes = [webView typingAttributes];
+ EXPECT_TRUE([[NSColor colorWithRed:0 green:0 blue:1 alpha:1] isEqual:attributes[NSForegroundColorAttributeName]]);
+ EXPECT_TRUE([[NSColor colorWithRed:1 green:0 blue:0 alpha:0.2] isEqual:attributes[NSBackgroundColorAttributeName]]);
+
+ options.backgroundColor = nil;
+ options.foregroundColor = nil;
+
+ EXPECT_WK_STREQ("rgb(0, 0, 0)", [webView stylePropertyAtSelectionStart:@"color"]);
+ EXPECT_WK_STREQ("rgba(0, 0, 0, 0)", [webView stylePropertyAtSelectionStart:@"background-color"]);
+ attributes = [webView typingAttributes];
+ EXPECT_NULL(attributes[NSForegroundColorAttributeName]);
+ EXPECT_NULL(attributes[NSBackgroundColorAttributeName]);
+}
+
} // namespace TestWebKitAPI
#endif // PLATFORM(MAC) && WK_API_ENABLED
Copied: trunk/Tools/TestWebKitAPI/mac/TestFontOptions.h (from rev 236853, trunk/Source/WebCore/editing/cocoa/FontAttributesCocoa.mm) (0 => 236854)
--- trunk/Tools/TestWebKitAPI/mac/TestFontOptions.h (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/TestFontOptions.h 2018-10-04 22:08:51 UTC (rev 236854)
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#if PLATFORM(MAC) && WK_API_ENABLED
+
+#import <AppKit/AppKit.h>
+
+@interface TestFontOptions : NSObject
+
+@property (class, readonly) TestFontOptions *sharedInstance;
+@property (nonatomic, readonly) NSDictionary *selectedAttributes;
+@property (nonatomic, readonly) BOOL hasMultipleFonts;
+
+// Font shadow manipulation.
+@property (nonatomic) BOOL hasShadow;
+@property (nonatomic) CGFloat shadowWidth;
+@property (nonatomic) CGFloat shadowHeight;
+@property (nonatomic) CGFloat shadowBlurRadius;
+
+// Font colors.
+@property (nonatomic, copy) NSColor *foregroundColor;
+@property (nonatomic, copy) NSColor *backgroundColor;
+
+@end
+
+#endif
Added: trunk/Tools/TestWebKitAPI/mac/TestFontOptions.mm (0 => 236854)
--- trunk/Tools/TestWebKitAPI/mac/TestFontOptions.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/TestFontOptions.mm 2018-10-04 22:08:51 UTC (rev 236854)
@@ -0,0 +1,232 @@
+/*
+ * 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"
+#import "TestFontOptions.h"
+
+#if PLATFORM(MAC) && WK_API_ENABLED
+
+#import "AppKitSPI.h"
+#import "ClassMethodSwizzler.h"
+#import <objc/runtime.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/SetForScope.h>
+
+using namespace TestWebKitAPI;
+
+static TestFontOptions *sharedFontOptionsForTesting()
+{
+ return TestFontOptions.sharedInstance;
+}
+
+@interface TestFontOptions ()
+- (instancetype)initWithFontOptions:(NSFontOptions *)fontOptions;
+@end
+
+@implementation TestFontOptions {
+ RetainPtr<NSFontOptions> _fontOptions;
+ CGSize _shadowOffset;
+ CGFloat _shadowBlurRadius;
+ BOOL _hasShadow;
+ BOOL _hasPendingShadowChanges;
+
+ RetainPtr<NSColor> _foregroundColor;
+ RetainPtr<NSColor> _backgroundColor;
+ BOOL _hasPendingColorChanges;
+
+ std::unique_ptr<ClassMethodSwizzler> _replaceFontOptionsSwizzler;
+ RetainPtr<NSDictionary> _selectedAttributes;
+ BOOL _hasMultipleFonts;
+}
+
+@synthesize hasShadow=_hasShadow;
+@synthesize shadowBlurRadius=_shadowBlurRadius;
+@synthesize hasMultipleFonts=_hasMultipleFonts;
+
++ (instancetype)sharedInstance
+{
+ static TestFontOptions *sharedInstance;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSFontOptions *sharedFontOptions = [NSClassFromString(@"NSFontOptions") sharedFontOptions];
+ sharedInstance = [[TestFontOptions alloc] initWithFontOptions:sharedFontOptions];
+ });
+ return sharedInstance;
+}
+
+- (instancetype)initWithFontOptions:(NSFontOptions *)fontOptions
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _fontOptions = fontOptions;
+ ASSERT(_fontOptions);
+
+ _shadowOffset = CGSizeZero;
+ _shadowBlurRadius = 0;
+ _replaceFontOptionsSwizzler = std::make_unique<ClassMethodSwizzler>(NSClassFromString(@"NSFontOptions"), @selector(sharedFontOptions), reinterpret_cast<IMP>(sharedFontOptionsForTesting));
+ _hasPendingShadowChanges = NO;
+ _hasMultipleFonts = NO;
+
+ return self;
+}
+
+- (NSDictionary *)selectedAttributes
+{
+ return _selectedAttributes.get();
+}
+
+- (NSFontOptions *)fontOptions
+{
+ return _fontOptions.get();
+}
+
+- (CGFloat)shadowWidth
+{
+ return _shadowOffset.width;
+}
+
+- (void)setShadowWidth:(CGFloat)shadowWidth
+{
+ if (_shadowOffset.width == shadowWidth)
+ return;
+
+ SetForScope<BOOL> hasPendingFontShadowChanges(_hasPendingShadowChanges, YES);
+ _shadowOffset.width = shadowWidth;
+ [self _dispatchFontAttributeChanges];
+}
+
+- (CGFloat)shadowHeight
+{
+ return _shadowOffset.height;
+}
+
+- (void)setShadowHeight:(CGFloat)shadowHeight
+{
+ if (_shadowOffset.height == shadowHeight)
+ return;
+
+ SetForScope<BOOL> hasPendingFontShadowChanges(_hasPendingShadowChanges, YES);
+ _shadowOffset.height = shadowHeight;
+ [self _dispatchFontAttributeChanges];
+}
+
+- (void)setShadowBlurRadius:(CGFloat)shadowBlurRadius
+{
+ if (_shadowBlurRadius == shadowBlurRadius)
+ return;
+
+ SetForScope<BOOL> hasPendingFontShadowChanges(_hasPendingShadowChanges, YES);
+ _shadowBlurRadius = shadowBlurRadius;
+ [self _dispatchFontAttributeChanges];
+}
+
+- (void)setHasShadow:(BOOL)hasShadow
+{
+ if (_hasShadow == hasShadow)
+ return;
+
+ SetForScope<BOOL> hasPendingFontShadowChanges(_hasPendingShadowChanges, YES);
+ _hasShadow = hasShadow;
+ [self _dispatchFontAttributeChanges];
+}
+
+- (NSColor *)foregroundColor
+{
+ return _foregroundColor.get();
+}
+
+- (void)setForegroundColor:(NSColor *)color
+{
+ SetForScope<BOOL> hasPendingColorChanges(_hasPendingColorChanges, YES);
+ _foregroundColor = adoptNS([color copy]);
+ [self _dispatchFontAttributeChanges];
+}
+
+- (NSColor *)backgroundColor
+{
+ return _backgroundColor.get();
+}
+
+- (void)setBackgroundColor:(NSColor *)color
+{
+ SetForScope<BOOL> hasPendingColorChanges(_hasPendingColorChanges, YES);
+ _backgroundColor = adoptNS([color copy]);
+ [self _dispatchFontAttributeChanges];
+}
+
+- (void)_dispatchFontAttributeChanges
+{
+ [NSFontManager.sharedFontManager.target performSelector:@selector(changeAttributes:) withObject:self];
+}
+
+- (NSDictionary *)convertAttributes:(NSDictionary *)attributes
+{
+ auto convertedAttributes = adoptNS([attributes mutableCopy]);
+
+ if (_hasPendingShadowChanges) {
+ if (_hasShadow) {
+ auto shadow = adoptNS([[NSShadow alloc] init]);
+ [shadow setShadowBlurRadius:_shadowBlurRadius];
+ [shadow setShadowOffset:_shadowOffset];
+ [convertedAttributes setObject:shadow.get() forKey:NSShadowAttributeName];
+ } else
+ [convertedAttributes removeObjectForKey:NSShadowAttributeName];
+ }
+
+ if (_hasPendingColorChanges) {
+ if (_foregroundColor)
+ [convertedAttributes setObject:_foregroundColor.get() forKey:NSForegroundColorAttributeName];
+ else
+ [convertedAttributes removeObjectForKey:NSForegroundColorAttributeName];
+
+ if (_backgroundColor)
+ [convertedAttributes setObject:_backgroundColor.get() forKey:NSBackgroundColorAttributeName];
+ else
+ [convertedAttributes removeObjectForKey:NSBackgroundColorAttributeName];
+ }
+
+ return convertedAttributes.autorelease();
+}
+
+- (void)setSelectedAttributes:(NSDictionary *)attributes isMultiple:(BOOL)multiple
+{
+ _hasMultipleFonts = multiple;
+ _selectedAttributes = attributes;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ if ([_fontOptions respondsToSelector:invocation.selector]) {
+ [invocation invokeWithTarget:_fontOptions.get()];
+ return;
+ }
+
+ [super forwardInvocation:invocation];
+}
+
+@end
+
+#endif // PLATFORM(MAC) && WK_API_ENABLED