Title: [250863] trunk
Revision
250863
Author
timo...@apple.com
Date
2019-10-08 14:56:28 -0700 (Tue, 08 Oct 2019)

Log Message

Copying white text from dark mode WebKit apps and pasting in a light mode app results in white (invisible) text.
https://bugs.webkit.org/show_bug.cgi?id=202662
rdar://problem/48677354

Reviewed by Megan Gardner.

Source/WebCore:

Covered by new API tests.

HTMLConverter needs to strip white text colors when the document is in dark mode, like it does for black in light mode.

* editing/cocoa/HTMLConverter.mm:
(normalizedColor): Handle dark mode and ignore white.
(HTMLConverterCaches::colorPropertyValueForNode): Pass element to normalizedColor.

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm: Added.
(readRTFDataFromPasteboard):
(copyAttributedStringFromHTML):
(checkColor):
(TEST):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (250862 => 250863)


--- trunk/Source/WebCore/ChangeLog	2019-10-08 21:40:52 UTC (rev 250862)
+++ trunk/Source/WebCore/ChangeLog	2019-10-08 21:56:28 UTC (rev 250863)
@@ -1,3 +1,19 @@
+2019-10-08  Timothy Hatcher  <timo...@apple.com>
+
+        Copying white text from dark mode WebKit apps and pasting in a light mode app results in white (invisible) text.
+        https://bugs.webkit.org/show_bug.cgi?id=202662
+        rdar://problem/48677354
+
+        Reviewed by Megan Gardner.
+
+        Covered by new API tests.
+
+        HTMLConverter needs to strip white text colors when the document is in dark mode, like it does for black in light mode.
+
+        * editing/cocoa/HTMLConverter.mm:
+        (normalizedColor): Handle dark mode and ignore white.
+        (HTMLConverterCaches::colorPropertyValueForNode): Pass element to normalizedColor.
+
 2019-10-08  Antti Koivisto  <an...@apple.com>
 
         [CSS Shadow Parts] Fix style invalidation with class selector and ::before and ::after

Modified: trunk/Source/WebCore/editing/cocoa/HTMLConverter.mm (250862 => 250863)


--- trunk/Source/WebCore/editing/cocoa/HTMLConverter.mm	2019-10-08 21:40:52 UTC (rev 250862)
+++ trunk/Source/WebCore/editing/cocoa/HTMLConverter.mm	2019-10-08 21:56:28 UTC (rev 250863)
@@ -855,15 +855,18 @@
     return element;
 }
 
-static Color normalizedColor(Color color, bool ignoreBlack)
+static Color normalizedColor(Color color, bool ignoreDefaultColor, Element& element)
 {
-    const double ColorEpsilon = 1 / (2 * (double)255.0);
-    
-    double red, green, blue, alpha;
-    color.getRGBA(red, green, blue, alpha);
-    if (red < ColorEpsilon && green < ColorEpsilon && blue < ColorEpsilon && (ignoreBlack || alpha < ColorEpsilon))
+    if (!ignoreDefaultColor)
+        return color;
+
+    bool useDarkAppearance = element.document().useDarkAppearance(element.existingComputedStyle());
+    if (useDarkAppearance && Color::isWhiteColor(color))
         return Color();
-    
+
+    if (!useDarkAppearance && Color::isBlackColor(color))
+        return Color();
+
     return color;
 }
 
@@ -875,16 +878,18 @@
         return Color();
     }
 
+    bool ignoreDefaultColor = propertyId == CSSPropertyColor;
+
     Element& element = downcast<Element>(node);
     if (RefPtr<CSSValue> value = computedStylePropertyForElement(element, propertyId)) {
         if (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isRGBColor())
-            return normalizedColor(Color(downcast<CSSPrimitiveValue>(*value).color().rgb()), propertyId == CSSPropertyColor);
+            return normalizedColor(Color(downcast<CSSPrimitiveValue>(*value).color().rgb()), ignoreDefaultColor, element);
     }
 
     bool inherit = false;
     if (RefPtr<CSSValue> value = inlineStylePropertyForElement(element, propertyId)) {
         if (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isRGBColor())
-            return normalizedColor(Color(downcast<CSSPrimitiveValue>(*value).color().rgb()), propertyId == CSSPropertyColor);
+            return normalizedColor(Color(downcast<CSSPrimitiveValue>(*value).color().rgb()), ignoreDefaultColor, element);
         if (value->isInheritedValue())
             inherit = true;
     }

Modified: trunk/Tools/ChangeLog (250862 => 250863)


--- trunk/Tools/ChangeLog	2019-10-08 21:40:52 UTC (rev 250862)
+++ trunk/Tools/ChangeLog	2019-10-08 21:56:28 UTC (rev 250863)
@@ -1,3 +1,18 @@
+2019-10-08  Timothy Hatcher  <timo...@apple.com>
+
+        Copying white text from dark mode WebKit apps and pasting in a light mode app results in white (invisible) text.
+        https://bugs.webkit.org/show_bug.cgi?id=202662
+        rdar://problem/48677354
+
+        Reviewed by Megan Gardner.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm: Added.
+        (readRTFDataFromPasteboard):
+        (copyAttributedStringFromHTML):
+        (checkColor):
+        (TEST):
+
 2019-10-08  Brady Eidson  <beid...@apple.com>
 
         Service Worker Fetch events should time out.

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (250862 => 250863)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2019-10-08 21:40:52 UTC (rev 250862)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2019-10-08 21:56:28 UTC (rev 250863)
@@ -103,6 +103,7 @@
 		1C2B81861C89259D00A5529F /* webfont.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1C2B81841C8924A200A5529F /* webfont.html */; };
 		1C2B81871C8925A000A5529F /* Ahem.ttf in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1C2B81851C89252300A5529F /* Ahem.ttf */; };
 		1C734B5320788C4800F430EA /* SystemColors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C734B5220788C4800F430EA /* SystemColors.mm */; };
+		1C79201C234BDD9B001EAF23 /* CopyRTF.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C79201B234BDD9B001EAF23 /* CopyRTF.mm */; };
 		1C7FEB20207C0F2E00D23278 /* BackgroundColor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C7FEB1F207C0F2D00D23278 /* BackgroundColor.mm */; };
 		1C90420C2326E03C00BEF91E /* SelectionByWord.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C90420B2326E03C00BEF91E /* SelectionByWord.mm */; };
 		1C9EB8411E380DA1005C6442 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C9EB8401E380DA1005C6442 /* ComplexTextController.cpp */; };
@@ -1535,6 +1536,7 @@
 		1C2B81841C8924A200A5529F /* webfont.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = webfont.html; sourceTree = "<group>"; };
 		1C2B81851C89252300A5529F /* Ahem.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Ahem.ttf; sourceTree = "<group>"; };
 		1C734B5220788C4800F430EA /* SystemColors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemColors.mm; sourceTree = "<group>"; };
+		1C79201B234BDD9B001EAF23 /* CopyRTF.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyRTF.mm; sourceTree = "<group>"; };
 		1C7FEB1F207C0F2D00D23278 /* BackgroundColor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BackgroundColor.mm; sourceTree = "<group>"; };
 		1C90420B2326E03C00BEF91E /* SelectionByWord.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SelectionByWord.mm; sourceTree = "<group>"; };
 		1C9EB8401E380DA1005C6442 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
@@ -2779,6 +2781,7 @@
 				5C19A5231FD0F32600EEA323 /* CookiePrivateBrowsing.mm */,
 				9B1056411F9045C700D5583F /* CopyHTML.mm */,
 				9999108A1F393C8B008AD455 /* Copying.mm */,
+				1C79201B234BDD9B001EAF23 /* CopyRTF.mm */,
 				9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */,
 				46A911582108E66B0078D40D /* CustomUserAgent.mm */,
 				2DC4CF761D2D9DD800ECCC94 /* DataDetection.mm */,
@@ -4463,6 +4466,7 @@
 				5C19A5241FD0F60100EEA323 /* CookiePrivateBrowsing.mm in Sources */,
 				9B1F6F781F90558400B55744 /* CopyHTML.mm in Sources */,
 				9999108B1F393C96008AD455 /* Copying.mm in Sources */,
+				1C79201C234BDD9B001EAF23 /* CopyRTF.mm in Sources */,
 				9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */,
 				7CCE7EAC1A411A3400447C4C /* Counters.cpp in Sources */,
 				7AEAD47F1E20116C00416EFE /* CrossPartitionFileSchemeAccess.mm in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm (0 => 250863)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyRTF.mm	2019-10-08 21:56:28 UTC (rev 250863)
@@ -0,0 +1,143 @@
+
+/*
+ * Copyright (C) 2019 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 PLATFORM(COCOA)
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
+#import <wtf/RetainPtr.h>
+
+#if PLATFORM(IOS_FAMILY)
+#include <MobileCoreServices/MobileCoreServices.h>
+#endif
+
+#if USE(APPKIT)
+using PlatformColor = NSColor;
+#else
+using PlatformColor = UIColor;
+#endif
+
+#if USE(APPKIT)
+static NSData *readRTFDataFromPasteboard()
+{
+    return [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeRTF];
+}
+#else
+static NSData *readRTFDataFromPasteboard()
+{
+    id value = [[UIPasteboard generalPasteboard] valueForPasteboardType:(__bridge NSString *)kUTTypeRTF];
+    ASSERT([value isKindOfClass:[NSData class]]);
+    return value;
+}
+#endif
+
+static RetainPtr<NSAttributedString> copyAttributedStringFromHTML(NSString *htmlString, bool forceDarkMode)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+
+    if (forceDarkMode)
+        [webView forceDarkMode];
+
+    [webView synchronouslyLoadHTMLString:htmlString];
+
+    [webView selectAll:nil];
+    [webView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
+
+    NSData *rtfData = readRTFDataFromPasteboard();
+    EXPECT_NOT_NULL(rtfData);
+
+    auto attributedString = adoptNS([[NSAttributedString alloc] initWithData:rtfData options:@{ } documentAttributes:nil error:nullptr]);
+    EXPECT_NOT_NULL(attributedString.get());
+
+    return attributedString;
+}
+
+static void checkColor(PlatformColor *color, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)
+{
+    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(red, observedRed);
+    EXPECT_EQ(green, observedRed);
+    EXPECT_EQ(blue, observedBlue);
+    EXPECT_EQ(alpha, observedAlpha);
+}
+
+#if HAVE(OS_DARK_MODE_SUPPORT)
+TEST(CopyRTF, StripsDefaultTextColorOfDarkContent)
+{
+    auto attributedString = copyAttributedStringFromHTML(@"<style>:root { color-scheme: dark }</style> Default <span style=\"color: black\">Black</span> <span style=\"color: white\">White</span>", true);
+
+    __block size_t i = 0;
+    [attributedString enumerateAttribute:NSForegroundColorAttributeName inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange attributeRange, BOOL *stop) {
+        if (!i)
+            EXPECT_NULL(value);
+        else if (i == 1) {
+            EXPECT_NOT_NULL(value);
+            checkColor(value, 0, 0, 0, 1);
+        } else if (i == 2)
+            EXPECT_NULL(value);
+        else
+            ASSERT_NOT_REACHED();
+
+        ++i;
+    }];
+
+    EXPECT_EQ(i, 3UL);
+}
+#endif
+
+TEST(CopyRTF, StripsDefaultTextColorOfLightContent)
+{
+    auto attributedString = copyAttributedStringFromHTML(@"Default <span style=\"color: white\">White</span> <span style=\"color: black\">Black</span>", false);
+
+    __block size_t i = 0;
+    [attributedString enumerateAttribute:NSForegroundColorAttributeName inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange attributeRange, BOOL *stop) {
+        if (!i)
+            EXPECT_NULL(value);
+        else if (i == 1) {
+            EXPECT_NOT_NULL(value);
+            checkColor(value, 1, 1, 1, 1);
+        } else if (i == 2)
+            EXPECT_NULL(value);
+        else
+            ASSERT_NOT_REACHED();
+
+        ++i;
+    }];
+
+    EXPECT_EQ(i, 3UL);
+}
+
+#endif // PLATFORM(COCOA)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to