Title: [219070] trunk
Revision
219070
Author
[email protected]
Date
2017-07-03 04:26:36 -0700 (Mon, 03 Jul 2017)

Log Message

Pasting single words copied to UIPasteboard inserts URLs in editable areas
https://bugs.webkit.org/show_bug.cgi?id=174082
<rdar://problem/33046992>

Reviewed by Tim Horton.

Source/WebCore:

Currently, our heuristics for coercing plain text to URLs when reading URLs off of the UIPasteboard allows URLs
to be created as long as -[UIPasteboard valuesForPasteboardType:inItemSet:] returns a non-null NSURL. However,
UIPasteboard automatically coerces any NSString into an NSURL if it initializes an NSURL via +URLWithString:.
Thus, single-word strings such as "hello" that are written to the pasteboard as "public.utf8-plain-text" can
be read back as NSURLs for "public.url". This currently causes bugs in shipping software: e.g. copying and
pasting a single word from an editable input or textarea and pasting into a rich contenteditable area using
WebKit1 inserts a link. However, when combined with another change in WebKit that attempts to read "public.url"
before "public.text" when reading plain text from the pasteboard, this now also affects pasting in plain text
areas, where pasted plain-text strings that are not URLs will paste as URL-encoded strings anyways (for
instance, replacing "[hello]" with "%5Bhello%5D").

To fix this, and existing issues with pasting single words in contenteditables, we make
PlatformPasteboard::readString and PlatformPasteboard::readURL only accept a coerced NSURL as an URL if it also
parses as a valid URL in WebKit (otherwise, we return an empty string).

Tests:
    UIPasteboardTests.DoNotPastePlainTextAsURL
    UIPasteboardTests.PastePlainTextAsURL
    UIPasteboardTests.PasteURLWithPlainTextAsURL

* platform/PlatformPasteboard.h:
* platform/ios/AbstractPasteboard.h:
* platform/ios/PlatformPasteboardIOS.mm:
(WebCore::PlatformPasteboard::allowReadingURLAtIndex):

Allow an URL to be read if either (1) an URL was explicitly specified in the UIPasteboard, or (2) the "proposed"
URL returned from -valuesForPasteboardType: is valid.

(WebCore::PlatformPasteboard::readString):
(WebCore::PlatformPasteboard::readURL):

Consult allowReadingURLAtIndex here (in the case of ::readString, only if the given pasteboard type is
"public.url").

* platform/ios/WebItemProviderPasteboard.h:
* platform/ios/WebItemProviderPasteboard.mm:
(-[WebItemProviderPasteboard itemProviders]):
(-[WebItemProviderPasteboard setItemProviders:]):

Source/WebKit2:

Add a hook to WKPreferences to allow programatic pasting.

* UIProcess/API/Cocoa/WKPreferences.mm:
(-[WKPreferences _setDOMPasteAllowed:]):
(-[WKPreferences _domPasteAllowed]):
* UIProcess/API/Cocoa/WKPreferencesPrivate.h:

Tools:

Adds 3 new unit tests to UIPasteboardTests to test cases of pasting plain text and URLs.

* TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
(TestWebKitAPI::setUpWebViewForPasteboardTests):
(TestWebKitAPI::TEST):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (219069 => 219070)


--- trunk/Source/WebCore/ChangeLog	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/ChangeLog	2017-07-03 11:26:36 UTC (rev 219070)
@@ -1,3 +1,50 @@
+2017-07-03  Wenson Hsieh  <[email protected]>
+
+        Pasting single words copied to UIPasteboard inserts URLs in editable areas
+        https://bugs.webkit.org/show_bug.cgi?id=174082
+        <rdar://problem/33046992>
+
+        Reviewed by Tim Horton.
+
+        Currently, our heuristics for coercing plain text to URLs when reading URLs off of the UIPasteboard allows URLs
+        to be created as long as -[UIPasteboard valuesForPasteboardType:inItemSet:] returns a non-null NSURL. However,
+        UIPasteboard automatically coerces any NSString into an NSURL if it initializes an NSURL via +URLWithString:.
+        Thus, single-word strings such as "hello" that are written to the pasteboard as "public.utf8-plain-text" can
+        be read back as NSURLs for "public.url". This currently causes bugs in shipping software: e.g. copying and
+        pasting a single word from an editable input or textarea and pasting into a rich contenteditable area using
+        WebKit1 inserts a link. However, when combined with another change in WebKit that attempts to read "public.url"
+        before "public.text" when reading plain text from the pasteboard, this now also affects pasting in plain text
+        areas, where pasted plain-text strings that are not URLs will paste as URL-encoded strings anyways (for
+        instance, replacing "[hello]" with "%5Bhello%5D").
+
+        To fix this, and existing issues with pasting single words in contenteditables, we make
+        PlatformPasteboard::readString and PlatformPasteboard::readURL only accept a coerced NSURL as an URL if it also
+        parses as a valid URL in WebKit (otherwise, we return an empty string).
+
+        Tests:
+            UIPasteboardTests.DoNotPastePlainTextAsURL
+            UIPasteboardTests.PastePlainTextAsURL
+            UIPasteboardTests.PasteURLWithPlainTextAsURL
+
+        * platform/PlatformPasteboard.h:
+        * platform/ios/AbstractPasteboard.h:
+        * platform/ios/PlatformPasteboardIOS.mm:
+        (WebCore::PlatformPasteboard::allowReadingURLAtIndex):
+
+        Allow an URL to be read if either (1) an URL was explicitly specified in the UIPasteboard, or (2) the "proposed"
+        URL returned from -valuesForPasteboardType: is valid.
+
+        (WebCore::PlatformPasteboard::readString):
+        (WebCore::PlatformPasteboard::readURL):
+
+        Consult allowReadingURLAtIndex here (in the case of ::readString, only if the given pasteboard type is
+        "public.url").
+
+        * platform/ios/WebItemProviderPasteboard.h:
+        * platform/ios/WebItemProviderPasteboard.mm:
+        (-[WebItemProviderPasteboard itemProviders]):
+        (-[WebItemProviderPasteboard setItemProviders:]):
+
 2017-07-03  Zan Dobersek  <[email protected]>
 
         [GCrypt] Implement CryptoKeyEC SPKI exports

Modified: trunk/Source/WebCore/platform/PlatformPasteboard.h (219069 => 219070)


--- trunk/Source/WebCore/platform/PlatformPasteboard.h	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/platform/PlatformPasteboard.h	2017-07-03 11:26:36 UTC (rev 219070)
@@ -102,6 +102,7 @@
     WEBCORE_EXPORT void writeObjectRepresentations(const PasteboardImage&);
     WEBCORE_EXPORT void writeObjectRepresentations(const String& pasteboardType, const String& text);
     WEBCORE_EXPORT void writeObjectRepresentations(const PasteboardURL&);
+    bool allowReadingURLAtIndex(const URL&, int index) const;
 #endif
 
 #if PLATFORM(MAC)

Modified: trunk/Source/WebCore/platform/ios/AbstractPasteboard.h (219069 => 219070)


--- trunk/Source/WebCore/platform/ios/AbstractPasteboard.h	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/platform/ios/AbstractPasteboard.h	2017-07-03 11:26:36 UTC (rev 219070)
@@ -34,6 +34,10 @@
 
 @property (readonly, nonatomic) NSInteger numberOfItems;
 
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
+@property (nonatomic, copy, nullable) NSArray<__kindof NSItemProvider *> *itemProviders;
+#endif
+
 - (NSArray<NSString *> *)pasteboardTypes;
 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet;
 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet;

Modified: trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm (219069 => 219070)


--- trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm	2017-07-03 11:26:36 UTC (rev 219070)
@@ -215,6 +215,24 @@
     [itemsToRegister addData:[(NSString *)plainText dataUsingEncoding:NSUTF8StringEncoding] forType:(NSString *)kUTTypeUTF8PlainText];
 }
 
+bool PlatformPasteboard::allowReadingURLAtIndex(const URL& url, int index) const
+{
+    NSItemProvider *itemProvider = (NSUInteger)index < [m_pasteboard itemProviders].count ? [[m_pasteboard itemProviders] objectAtIndex:index] : nil;
+    for (NSString *type in itemProvider.registeredTypeIdentifiers) {
+        if (UTTypeConformsTo((CFStringRef)type, kUTTypeURL))
+            return true;
+    }
+
+    return url.isValid();
+}
+
+#else
+
+bool PlatformPasteboard::allowReadingURLAtIndex(const URL&, int) const
+{
+    return true;
+}
+
 #endif
 
 void PlatformPasteboard::writeObjectRepresentations(const PasteboardWebContent& content)
@@ -426,7 +444,7 @@
             return [(NSAttributedString *)value string];
     } else if (type == String(kUTTypeURL)) {
         ASSERT([value isKindOfClass:[NSURL class]]);
-        if ([value isKindOfClass:[NSURL class]])
+        if ([value isKindOfClass:[NSURL class]] && allowReadingURLAtIndex((NSURL *)value, index))
             return [(NSURL *)value absoluteString];
     }
 
@@ -447,6 +465,9 @@
     if (![value isKindOfClass:[NSURL class]])
         return URL();
 
+    if (!allowReadingURLAtIndex((NSURL *)value, index))
+        return { };
+
 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
     title = [value _title];
 #else

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h (219069 => 219070)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h	2017-07-03 11:26:36 UTC (rev 219070)
@@ -79,7 +79,7 @@
 - (WebItemProviderRegistrationInfoList *)registrationInfoAtIndex:(NSUInteger)index;
 - (UIItemProvider *)itemProviderAtIndex:(NSUInteger)index;
 
-@property (copy, nonatomic, nullable) NSArray<UIItemProvider *> *itemProviders;
+@property (copy, nonatomic, nullable) NSArray<__kindof NSItemProvider *> *itemProviders;
 @property (readonly, nonatomic) NSInteger numberOfItems;
 @property (readonly, nonatomic) NSInteger changeCount;
 

Modified: trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm (219069 => 219070)


--- trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm	2017-07-03 11:26:36 UTC (rev 219070)
@@ -222,12 +222,12 @@
     return _cachedTypeIdentifiers.get();
 }
 
-- (NSArray<UIItemProvider *> *)itemProviders
+- (NSArray<__kindof NSItemProvider *> *)itemProviders
 {
     return _itemProviders.get();
 }
 
-- (void)setItemProviders:(NSArray<UIItemProvider *> *)itemProviders
+- (void)setItemProviders:(NSArray<__kindof NSItemProvider *> *)itemProviders
 {
     itemProviders = itemProviders ?: [NSArray array];
     if (_itemProviders == itemProviders || [_itemProviders isEqualToArray:itemProviders])

Modified: trunk/Source/WebKit2/ChangeLog (219069 => 219070)


--- trunk/Source/WebKit2/ChangeLog	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebKit2/ChangeLog	2017-07-03 11:26:36 UTC (rev 219070)
@@ -1,3 +1,18 @@
+2017-07-03  Wenson Hsieh  <[email protected]>
+
+        Pasting single words copied to UIPasteboard inserts URLs in editable areas
+        https://bugs.webkit.org/show_bug.cgi?id=174082
+        <rdar://problem/33046992>
+
+        Reviewed by Tim Horton.
+
+        Add a hook to WKPreferences to allow programatic pasting.
+
+        * UIProcess/API/Cocoa/WKPreferences.mm:
+        (-[WKPreferences _setDOMPasteAllowed:]):
+        (-[WKPreferences _domPasteAllowed]):
+        * UIProcess/API/Cocoa/WKPreferencesPrivate.h:
+
 2017-07-03  Zan Dobersek  <[email protected]>
 
         Unreviewed GTK+ and WPE build fix when building with GCC 4.9.

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferences.mm (219069 => 219070)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferences.mm	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferences.mm	2017-07-03 11:26:36 UTC (rev 219070)
@@ -627,6 +627,16 @@
     return _preferences->_javascript_CanAccessClipboard();
 }
 
+- (void)_setDOMPasteAllowed:(BOOL)domPasteAllowed
+{
+    _preferences->setDOMPasteAllowed(domPasteAllowed);
+}
+
+- (BOOL)_domPasteAllowed
+{
+    return _preferences->domPasteAllowed();
+}
+
 - (void)_setMediaDocumentEntersFullscreenAutomatically:(BOOL)mediaDocumentEntersFullscreenAutomatically
 {
     _preferences->setMediaDocumentEntersFullscreenAutomatically(mediaDocumentEntersFullscreenAutomatically);

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferencesPrivate.h (219069 => 219070)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferencesPrivate.h	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKPreferencesPrivate.h	2017-07-03 11:26:36 UTC (rev 219070)
@@ -107,6 +107,7 @@
 @property (nonatomic, setter=_setWebRTCLegacyAPIEnabled:) BOOL _webRTCLegacyAPIEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @property (nonatomic, setter=_setJavaScriptCanAccessClipboard:) BOOL _javaScriptCanAccessClipboard WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic, setter=_setDOMPasteAllowed:) BOOL _domPasteAllowed WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @property (nonatomic, setter=_setMediaDocumentEntersFullscreenAutomatically:) BOOL _mediaDocumentEntersFullscreenAutomatically WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 

Modified: trunk/Tools/ChangeLog (219069 => 219070)


--- trunk/Tools/ChangeLog	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Tools/ChangeLog	2017-07-03 11:26:36 UTC (rev 219070)
@@ -1,3 +1,17 @@
+2017-07-03  Wenson Hsieh  <[email protected]>
+
+        Pasting single words copied to UIPasteboard inserts URLs in editable areas
+        https://bugs.webkit.org/show_bug.cgi?id=174082
+        <rdar://problem/33046992>
+
+        Reviewed by Tim Horton.
+
+        Adds 3 new unit tests to UIPasteboardTests to test cases of pasting plain text and URLs.
+
+        * TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
+        (TestWebKitAPI::setUpWebViewForPasteboardTests):
+        (TestWebKitAPI::TEST):
+
 2017-07-02  Brady Eidson  <[email protected]>
 
         Add API test for all parts of WebKit1 API related to favicons.

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm (219069 => 219070)


--- trunk/Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm	2017-07-03 10:58:41 UTC (rev 219069)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm	2017-07-03 11:26:36 UTC (rev 219070)
@@ -31,9 +31,13 @@
 #import "TestWKWebView.h"
 #import <MobileCoreServices/MobileCoreServices.h>
 #import <UIKit/UIPasteboard.h>
+#import <WebCore/SoftLinking.h>
 #import <WebKit/WKPreferencesPrivate.h>
 #import <WebKit/WKWebViewPrivate.h>
 
+SOFT_LINK_FRAMEWORK(UIKit)
+SOFT_LINK(UIKit, UIApplicationInitialize, void, (void), ())
+
 namespace TestWebKitAPI {
 
 NSData *dataForPasteboardType(CFStringRef type)
@@ -43,12 +47,17 @@
 
 RetainPtr<TestWKWebView> setUpWebViewForPasteboardTests()
 {
+    // UIPasteboard's type coercion codepaths only take effect when the UIApplication has been initialized.
+    UIApplicationInitialize();
+
     [UIPasteboard generalPasteboard].items = @[];
     EXPECT_TRUE(!dataForPasteboardType(kUTTypeUTF8PlainText).length);
     EXPECT_TRUE(!dataForPasteboardType(kUTTypeUTF16PlainText).length);
 
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
-    [webView configuration].preferences._javaScriptCanAccessClipboard = YES;
+    WKPreferences *preferences = [webView configuration].preferences;
+    preferences._javaScriptCanAccessClipboard = YES;
+    preferences._domPasteAllowed = YES;
     [webView synchronouslyLoadTestPageNamed:@"rich-and-plain-text"];
     return webView;
 }
@@ -77,6 +86,57 @@
     EXPECT_WK_STREQ("Hello world", [utf16Result UTF8String]);
 }
 
+TEST(UIPasteboardTests, DoNotPastePlainTextAsURL)
+{
+    auto webView = setUpWebViewForPasteboardTests();
+
+    NSString *testString = @"[helloworld]";
+    [UIPasteboard generalPasteboard].string = testString;
+
+    [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"plain.value"]);
+
+    [webView stringByEvaluatingJavaScript:@"selectRichText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"rich.textContent"]);
+    EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"!!rich.querySelector('a')"].boolValue);
+}
+
+TEST(UIPasteboardTests, PastePlainTextAsURL)
+{
+    auto webView = setUpWebViewForPasteboardTests();
+
+    NSString *testString = @"https://www.apple.com/iphone";
+    [UIPasteboard generalPasteboard].string = testString;
+
+    [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"plain.value"]);
+
+    [webView stringByEvaluatingJavaScript:@"selectRichText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"rich.textContent"]);
+    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!rich.querySelector('a')"].boolValue);
+}
+
+TEST(UIPasteboardTests, PasteURLWithPlainTextAsURL)
+{
+    auto webView = setUpWebViewForPasteboardTests();
+
+    NSString *testString = @"thisisdefinitelyaurl";
+    [UIPasteboard generalPasteboard].URL = "" URLWithString:testString];
+
+    [webView stringByEvaluatingJavaScript:@"selectPlainText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"plain.value"]);
+
+    [webView stringByEvaluatingJavaScript:@"selectRichText()"];
+    [webView stringByEvaluatingJavaScript:@"document.execCommand('paste')"];
+    EXPECT_WK_STREQ(testString, [webView stringByEvaluatingJavaScript:@"rich.textContent"]);
+    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!rich.querySelector('a')"].boolValue);
+}
+
 } // namespace TestWebKitAPI
 
 #endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to