Changeset 235011 in webkit


Ignore:
Timestamp:
Aug 18, 2018 3:59:20 PM (6 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Paste is missing from callout bar when pasteboard only contains custom data
https://bugs.webkit.org/show_bug.cgi?id=184271
<rdar://problem/39256708>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Export a couple of WebCore functions for use in WebKit.

  • dom/Document.h:
  • platform/Pasteboard.h:

Source/WebKit:

Currently, the "paste:" selector action cannot be performed during editing if the pasteboard only contains
custom pasteboard data. This is because logic in -[WKContentView canPerformActionForWebView:withSender:] only
checks against a list of pasteboard types which does not include the type identifier for custom pasteboard data.
To fix this, we allow pasting only in the case where none of the other type identifiers exist in the pasteboard,
as long as the custom pasteboard data type identifier is present, and the custom pasteboard data's origin
matches the origin of the focused frame's document.

Test: PasteMixedContent.CopyAndPasteWithCustomPasteboardDataOnly

  • Shared/EditorState.cpp:

(WebKit::EditorState::encode const):
(WebKit::EditorState::decode):

  • Shared/EditorState.h:

Add a originIdentifierForPasteboard field, and add support for encoding it when propagating EditorState via IPC.

  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView canPerformActionForWebView:withSender:]):

If none of the conventional pasteboard type identifiers for rich or plain text editing are present, check to see
if we have custom pasteboard data; if so, only allow pasting if the custom pasteboard data's origin matches that
of the focused frame's document origin.

Additionally refactor a bit of logic by pulling out _page->editorState() into a separate local variable, used
throughout the rest of the method.

  • WebProcess/WebPage/WebPage.cpp:

(WebKit::WebPage::editorState const):

Send the focused frame's document origin to the UI process via EditorState.

Tools:

Add a new API test to verify that on both iOS and macOS, copied custom pasteboard data can only be pasted in a
matching origin. Additionally verify that on iOS, the web view is capable of performing the "paste:" selector.

  • TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:

(TestWebKitAPI::TEST):
(imagePath): Deleted.
(writeTypesAndDataToPasteboard): Deleted.
(setUpWebView): Deleted.
(markupString): Deleted.

Make this test file no longer exclusive to macOS.

  • TestWebKitAPI/cocoa/TestWKWebView.h:
  • TestWebKitAPI/cocoa/TestWKWebView.mm:

(-[TestWKWebView synchronouslyLoadHTMLString:baseURL:]):

Tweak this helper to also take in a baseURL. Defaults to using the TestWebKitAPI bundle resource URL.

(-[TestWKWebView synchronouslyLoadHTMLString:]):

Location:
trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r235009 r235011  
     12018-08-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Paste is missing from callout bar when pasteboard only contains custom data
     4        https://bugs.webkit.org/show_bug.cgi?id=184271
     5        <rdar://problem/39256708>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        Export a couple of WebCore functions for use in WebKit.
     10
     11        * dom/Document.h:
     12        * platform/Pasteboard.h:
     13
    1142018-08-17  Ryosuke Niwa  <rniwa@webkit.org>
    215
  • trunk/Source/WebCore/dom/Document.h

    r234680 r235011  
    10071007    uint64_t domTreeVersion() const { return m_domTreeVersion; }
    10081008
    1009     String originIdentifierForPasteboard();
     1009    WEBCORE_EXPORT String originIdentifierForPasteboard();
    10101010
    10111011    // XPathEvaluator methods
  • trunk/Source/WebCore/platform/Pasteboard.h

    r234930 r235011  
    171171
    172172#if PLATFORM(COCOA)
    173     static const char* cocoaType();
     173    WEBCORE_EXPORT static const char* cocoaType();
    174174#endif
    175175};
  • trunk/Source/WebKit/ChangeLog

    r235006 r235011  
     12018-08-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Paste is missing from callout bar when pasteboard only contains custom data
     4        https://bugs.webkit.org/show_bug.cgi?id=184271
     5        <rdar://problem/39256708>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        Currently, the "paste:" selector action cannot be performed during editing if the pasteboard only contains
     10        custom pasteboard data. This is because logic in -[WKContentView canPerformActionForWebView:withSender:] only
     11        checks against a list of pasteboard types which does not include the type identifier for custom pasteboard data.
     12        To fix this, we allow pasting only in the case where none of the other type identifiers exist in the pasteboard,
     13        as long as the custom pasteboard data type identifier is present, and the custom pasteboard data's origin
     14        matches the origin of the focused frame's document.
     15
     16        Test: PasteMixedContent.CopyAndPasteWithCustomPasteboardDataOnly
     17
     18        * Shared/EditorState.cpp:
     19        (WebKit::EditorState::encode const):
     20        (WebKit::EditorState::decode):
     21        * Shared/EditorState.h:
     22
     23        Add a originIdentifierForPasteboard field, and add support for encoding it when propagating EditorState via IPC.
     24
     25        * UIProcess/ios/WKContentViewInteraction.mm:
     26        (-[WKContentView canPerformActionForWebView:withSender:]):
     27
     28        If none of the conventional pasteboard type identifiers for rich or plain text editing are present, check to see
     29        if we have custom pasteboard data; if so, only allow pasting if the custom pasteboard data's origin matches that
     30        of the focused frame's document origin.
     31
     32        Additionally refactor a bit of logic by pulling out `_page->editorState()` into a separate local variable, used
     33        throughout the rest of the method.
     34
     35        * WebProcess/WebPage/WebPage.cpp:
     36        (WebKit::WebPage::editorState const):
     37
     38        Send the focused frame's document origin to the UI process via EditorState.
     39
    1402018-08-17  Tim Horton  <timothy_horton@apple.com>
    241
  • trunk/Source/WebKit/Shared/EditorState.cpp

    r225651 r235011  
    5454    encoder << markedText;
    5555#endif
     56
     57    encoder << originIdentifierForPasteboard;
    5658}
    5759
     
    98100        return false;
    99101#endif
     102
     103    if (!decoder.decode(result.originIdentifierForPasteboard))
     104        return false;
    100105
    101106    return true;
  • trunk/Source/WebKit/Shared/EditorState.h

    r225651 r235011  
    8181#endif
    8282
     83    String originIdentifierForPasteboard;
     84
    8385    struct PostLayoutData {
    8486        uint32_t typingAttributes { AttributeNone };
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r234808 r235011  
    22772277        return [self isFirstResponder];
    22782278       
     2279    auto editorState = _page->editorState();
    22792280    if (action == @selector(_showTextStyleOptions:))
    2280         return _page->editorState().isContentRichlyEditable && _page->editorState().selectionIsRange && !_showingTextStyleOptions;
     2281        return editorState.isContentRichlyEditable && editorState.selectionIsRange && !_showingTextStyleOptions;
    22812282    if (_showingTextStyleOptions)
    22822283        return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
    22832284    if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:))
    2284         return _page->editorState().isContentRichlyEditable;
     2285        return editorState.isContentRichlyEditable;
    22852286    if (action == @selector(cut:))
    2286         return !_page->editorState().isInPasswordField && _page->editorState().isContentEditable && _page->editorState().selectionIsRange;
     2287        return !editorState.isInPasswordField && editorState.isContentEditable && editorState.selectionIsRange;
    22872288   
    22882289    if (action == @selector(paste:)) {
    2289         if (_page->editorState().selectionIsNone || !_page->editorState().isContentEditable)
     2290        if (editorState.selectionIsNone || !editorState.isContentEditable)
    22902291            return NO;
    22912292        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    22922293        NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
    22932294        NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
    2294         return [pasteboard containsPasteboardTypes:types inItemSet:indices];
     2295        if ([pasteboard containsPasteboardTypes:types inItemSet:indices])
     2296            return YES;
     2297
     2298        auto focusedDocumentOrigin = editorState.originIdentifierForPasteboard;
     2299        if (focusedDocumentOrigin.isEmpty())
     2300            return NO;
     2301
     2302        NSArray *allCustomPasteboardData = [pasteboard dataForPasteboardType:@(PasteboardCustomData::cocoaType()) inItemSet:indices];
     2303        for (NSData *data in allCustomPasteboardData) {
     2304            auto buffer = SharedBuffer::create(data);
     2305            if (PasteboardCustomData::fromSharedBuffer(buffer.get()).origin == focusedDocumentOrigin)
     2306                return YES;
     2307        }
     2308        return NO;
    22952309    }
    22962310
    22972311    if (action == @selector(copy:)) {
    2298         if (_page->editorState().isInPasswordField)
     2312        if (editorState.isInPasswordField)
    22992313            return NO;
    2300         return _page->editorState().selectionIsRange;
     2314        return editorState.selectionIsRange;
    23012315    }
    23022316
    23032317    if (action == @selector(_define:)) {
    2304         if (_page->editorState().isInPasswordField || !_page->editorState().selectionIsRange)
     2318        if (editorState.isInPasswordField || !editorState.selectionIsRange)
    23052319            return NO;
    23062320
    2307         NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
     2321        NSUInteger textLength = editorState.postLayoutData().selectedTextLength;
    23082322        // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
    23092323        // acceptable, but the interface takes a string.
     
    23212335
    23222336    if (action == @selector(_lookup:)) {
    2323         if (_page->editorState().isInPasswordField)
     2337        if (editorState.isInPasswordField)
    23242338            return NO;
    23252339
     
    23292343#endif
    23302344
    2331         return _page->editorState().selectionIsRange;
     2345        return editorState.selectionIsRange;
    23322346    }
    23332347
    23342348    if (action == @selector(_share:)) {
    2335         if (_page->editorState().isInPasswordField || !_page->editorState().selectionIsRange)
     2349        if (editorState.isInPasswordField || !editorState.selectionIsRange)
    23362350            return NO;
    23372351
    2338         return _page->editorState().postLayoutData().selectedTextLength > 0;
     2352        return editorState.postLayoutData().selectedTextLength > 0;
    23392353    }
    23402354
    23412355    if (action == @selector(_addShortcut:)) {
    2342         if (_page->editorState().isInPasswordField || !_page->editorState().selectionIsRange)
     2356        if (editorState.isInPasswordField || !editorState.selectionIsRange)
    23432357            return NO;
    23442358
     
    23552369
    23562370    if (action == @selector(_promptForReplace:)) {
    2357         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
     2371        if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
    23582372            return NO;
    23592373        if ([[self selectedText] _containsCJScriptsOnly])
     
    23632377
    23642378    if (action == @selector(_transliterateChinese:)) {
    2365         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
     2379        if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
    23662380            return NO;
    23672381        return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
     
    23702384    if (action == @selector(select:)) {
    23712385        // Disable select in password fields so that you can't see word boundaries.
    2372         return !_page->editorState().isInPasswordField && [self hasContent] && !_page->editorState().selectionIsNone && !_page->editorState().selectionIsRange;
     2386        return !editorState.isInPasswordField && [self hasContent] && !editorState.selectionIsNone && !editorState.selectionIsRange;
    23732387    }
    23742388
    23752389    if (action == @selector(selectAll:)) {
    2376         if (!_page->editorState().selectionIsNone && !_page->editorState().selectionIsRange)
     2390        if (!editorState.selectionIsNone && !editorState.selectionIsRange)
    23772391            return YES;
    23782392        return NO;
     
    23802394
    23812395    if (action == @selector(replace:))
    2382         return _page->editorState().isContentEditable && !_page->editorState().isInPasswordField;
     2396        return editorState.isContentEditable && !editorState.isInPasswordField;
    23832397
    23842398    return [super canPerformAction:action withSender:sender];
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r234995 r235011  
    922922    result.shouldIgnoreSelectionChanges = editor.ignoreSelectionChanges();
    923923
     924    if (auto* document = frame.document())
     925        result.originIdentifierForPasteboard = document->originIdentifierForPasteboard();
     926
    924927    bool canIncludePostLayoutData = frame.view() && !frame.view()->needsLayout();
    925928    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::Yes && canIncludePostLayoutData) {
  • trunk/Tools/ChangeLog

    r234997 r235011  
     12018-08-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Paste is missing from callout bar when pasteboard only contains custom data
     4        https://bugs.webkit.org/show_bug.cgi?id=184271
     5        <rdar://problem/39256708>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        Add a new API test to verify that on both iOS and macOS, copied custom pasteboard data can only be pasted in a
     10        matching origin. Additionally verify that on iOS, the web view is capable of performing the "paste:" selector.
     11
     12        * TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:
     13        (TestWebKitAPI::TEST):
     14        (imagePath): Deleted.
     15        (writeTypesAndDataToPasteboard): Deleted.
     16        (setUpWebView): Deleted.
     17        (markupString): Deleted.
     18
     19        Make this test file no longer exclusive to macOS.
     20
     21        * TestWebKitAPI/cocoa/TestWKWebView.h:
     22        * TestWebKitAPI/cocoa/TestWKWebView.mm:
     23        (-[TestWKWebView synchronouslyLoadHTMLString:baseURL:]):
     24
     25        Tweak this helper to also take in a `baseURL`. Defaults to using the TestWebKitAPI bundle resource URL.
     26
     27        (-[TestWKWebView synchronouslyLoadHTMLString:]):
     28
    1292018-08-17  Jonathan Bedard  <jbedard@apple.com>
    230
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm

    r231531 r235011  
    2626#include "config.h"
    2727
    28 #if PLATFORM(MAC) && WK_API_ENABLED
     28#if WK_API_ENABLED
    2929
    3030#import "PlatformUtilities.h"
     31#import "TestURLSchemeHandler.h"
    3132#import "TestWKWebView.h"
    3233#import <WebKit/WKPreferencesRef.h>
    3334#import <WebKit/WKPreferencesRefPrivate.h>
     35#import <WebKit/WKWebViewPrivate.h>
    3436#import <wtf/RetainPtr.h>
    3537#import <wtf/text/WTFString.h>
     38
     39namespace TestWebKitAPI {
     40
     41#if PLATFORM(MAC)
    3642
    3743static NSString *imagePath()
     
    97103}
    98104
    99 namespace TestWebKitAPI {
    100 
    101105TEST(PasteMixedContent, ImageFileAndPlainText)
    102106{
     
    252256}
    253257
     258#endif // PLATFORM(MAC)
     259
     260TEST(PasteMixedContent, CopyAndPasteWithCustomPasteboardDataOnly)
     261{
     262    NSString *markupForSource = @"<body oncopy=\"event.preventDefault(); event.clipboardData.setData('foo', 'bar')\">hello</body>";
     263    NSString *markupForDestination = @"<input autofocus onpaste=\"event.preventDefault(); this.value = event.clipboardData.getData('foo')\">";
     264
     265    auto schemeHandler = adoptNS([[TestURLSchemeHandler alloc] init]);
     266    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     267    [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"same"];
     268    [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"different"];
     269
     270    auto source = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]);
     271    [source synchronouslyLoadHTMLString:markupForSource baseURL:[NSURL URLWithString:@"same://"]];
     272    [source selectAll:nil];
     273    [source _executeEditCommand:@"copy" argument:nil completion:nil];
     274
     275    auto destination = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]);
     276    [destination synchronouslyLoadHTMLString:markupForDestination baseURL:[NSURL URLWithString:@"same://"]];
     277    [destination paste:nil];
     278    EXPECT_WK_STREQ("bar", [destination stringByEvaluatingJavaScript:@"document.querySelector('input').value"]);
     279#if PLATFORM(IOS)
     280    EXPECT_TRUE([destination canPerformAction:@selector(paste:) withSender:nil]);
     281#endif
     282
     283    [destination synchronouslyLoadHTMLString:markupForDestination baseURL:[NSURL URLWithString:@"different://"]];
     284    [destination paste:nil];
     285    EXPECT_WK_STREQ("", [destination stringByEvaluatingJavaScript:@"document.querySelector('input').value"]);
     286#if PLATFORM(IOS)
     287    EXPECT_FALSE([destination canPerformAction:@selector(paste:) withSender:nil]);
     288#endif
     289}
     290
    254291} // namespace TestWebKitAPI
    255292
    256 #endif // PLATFORM(MAC) && WK_API_ENABLED
     293#endif // WK_API_ENABLED
  • trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h

    r234816 r235011  
    5252- (void)loadTestPageNamed:(NSString *)pageName;
    5353- (void)synchronouslyLoadHTMLString:(NSString *)html;
     54- (void)synchronouslyLoadHTMLString:(NSString *)html baseURL:(NSURL *)url;
    5455- (void)synchronouslyLoadTestPageNamed:(NSString *)pageName;
    5556- (id)objectByEvaluatingJavaScript:(NSString *)script;
  • trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm

    r234816 r235011  
    247247}
    248248
     249- (void)synchronouslyLoadHTMLString:(NSString *)html baseURL:(NSURL *)url
     250{
     251    [self loadHTMLString:html baseURL:url];
     252    [self _test_waitForDidFinishNavigation];
     253}
     254
    249255- (void)synchronouslyLoadHTMLString:(NSString *)html
    250256{
    251     NSURL *testResourceURL = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:@"TestWebKitAPI.resources"];
    252     [self loadHTMLString:html baseURL:testResourceURL];
    253     [self _test_waitForDidFinishNavigation];
     257    [self synchronouslyLoadHTMLString:html baseURL:[[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:@"TestWebKitAPI.resources"]];
    254258}
    255259
Note: See TracChangeset for help on using the changeset viewer.