Add AssistedNodeInformation plumbing for form control placeholder text and label...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2018 02:28:29 +0000 (02:28 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2018 02:28:29 +0000 (02:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183802
<rdar://problem/38686273>

Reviewed by Tim Horton.

Source/WebKit:

Surfaces some additional information about the currently focused element to the input delegate in the UI process.
See comments below for more details.

Test: WebKit.FocusedElementInfo

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

Add `placeholder` and `label` to AssistedNodeInformation, which capture the value of the placeholder attribute
and the text of the first associated label element for the focused form control. Also add boilerplate encoder/
decoder support for these members.

* UIProcess/API/Cocoa/_WKFocusedElementInfo.h:

Augment _WKFocusedElementInfo to include placeholder and label.

* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKFocusedElementInfo initWithAssistedNodeInformation:isUserInitiated:userObject:]):
(-[WKFocusedElementInfo label]):
(-[WKFocusedElementInfo placeholder]):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::getAssistedNodeInformation):

For input elements and textareas, set the placeholder to the value of the placeholder attribute. For all
elements with associated labels, grab the inner text of the first label that is not empty, ignoring all labels
that are `display: none` (i.e. not being rendered).

Tools:

Adds a new API test to exercise new placeholder and label SPI on _WKFocusedFormElement.

* TestWebKitAPI/Tests/WebKitCocoa/_WKInputDelegate.mm:
(-[InputDelegate _webView:focusShouldStartInputSession:]):
(-[InputDelegate shouldStartInputSessionHandler]):
(-[InputDelegate setShouldStartInputSessionHandler:]):
(TEST):
(-[FormSubmissionDelegate webView:startURLSchemeTask:]): Deleted.
(-[FormSubmissionDelegate webView:stopURLSchemeTask:]): Deleted.
(-[FormSubmissionDelegate _webView:willSubmitFormValues:userObject:submissionHandler:]): Deleted.

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

Source/WebKit/ChangeLog
Source/WebKit/Shared/AssistedNodeInformation.cpp
Source/WebKit/Shared/AssistedNodeInformation.h
Source/WebKit/UIProcess/API/Cocoa/_WKFocusedElementInfo.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKInputDelegate.mm

index 1329b6c..da30f83 100644 (file)
@@ -1,3 +1,40 @@
+2018-03-20  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add AssistedNodeInformation plumbing for form control placeholder text and label text
+        https://bugs.webkit.org/show_bug.cgi?id=183802
+        <rdar://problem/38686273>
+
+        Reviewed by Tim Horton.
+
+        Surfaces some additional information about the currently focused element to the input delegate in the UI process.
+        See comments below for more details.
+
+        Test: WebKit.FocusedElementInfo
+
+        * Shared/AssistedNodeInformation.cpp:
+        (WebKit::AssistedNodeInformation::encode const):
+        (WebKit::AssistedNodeInformation::decode):
+        * Shared/AssistedNodeInformation.h:
+
+        Add `placeholder` and `label` to AssistedNodeInformation, which capture the value of the placeholder attribute
+        and the text of the first associated label element for the focused form control. Also add boilerplate encoder/
+        decoder support for these members.
+
+        * UIProcess/API/Cocoa/_WKFocusedElementInfo.h:
+
+        Augment _WKFocusedElementInfo to include placeholder and label.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKFocusedElementInfo initWithAssistedNodeInformation:isUserInitiated:userObject:]):
+        (-[WKFocusedElementInfo label]):
+        (-[WKFocusedElementInfo placeholder]):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::getAssistedNodeInformation):
+
+        For input elements and textareas, set the placeholder to the value of the placeholder attribute. For all
+        elements with associated labels, grab the inner text of the first label that is not empty, ignoring all labels
+        that are `display: none` (i.e. not being rendered).
+
 2018-03-20  Brady Eidson  <beidson@apple.com>
 
         First piece of process swapping on navigation.
index dc984c8..a7884a9 100644 (file)
@@ -89,6 +89,8 @@ void AssistedNodeInformation::encode(IPC::Encoder& encoder) const
     encoder << acceptsAutofilledLoginCredentials;
     encoder << representingPageURL;
     encoder.encodeEnum(autofillFieldName);
+    encoder << placeholder;
+    encoder << label;
     encoder << assistedNodeIdentifier;
 }
 
@@ -172,6 +174,12 @@ bool AssistedNodeInformation::decode(IPC::Decoder& decoder, AssistedNodeInformat
     if (!decoder.decodeEnum(result.autofillFieldName))
         return false;
 
+    if (!decoder.decode(result.placeholder))
+        return false;
+
+    if (!decoder.decode(result.label))
+        return false;
+
     if (!decoder.decode(result.assistedNodeIdentifier))
         return false;
 
index e4f3d49..6bcf337 100644 (file)
@@ -113,6 +113,8 @@ struct AssistedNodeInformation {
     bool acceptsAutofilledLoginCredentials { false };
     WebCore::URL representingPageURL;
     WebCore::AutofillFieldName autofillFieldName { WebCore::AutofillFieldName::None };
+    String placeholder;
+    String label;
 
     uint64_t assistedNodeIdentifier { 0 };
 
index 6a15f56..7af290f 100644 (file)
@@ -68,6 +68,12 @@ typedef NS_ENUM(NSInteger, WKInputType) {
 /* The value of the input at the time it was focused. */
 @property (nonatomic, readonly, copy) NSString *value;
 
+/* The placeholder text of the input. */
+@property (nonatomic, readonly, copy) NSString *placeholder;
+
+/* The text of a label element associated with the input. */
+@property (nonatomic, readonly, copy) NSString *label;
+
 /**
  * Whether the element was focused due to user interaction. NO indicates that
  * the element was focused programmatically, e.g. by calling focus() in JavaScript
index 9cdc605..279a5e0 100644 (file)
@@ -407,6 +407,8 @@ const CGFloat minimumTapHighlightRadius = 2.0;
     RetainPtr<NSString> _value;
     BOOL _isUserInitiated;
     RetainPtr<NSObject <NSSecureCoding>> _userObject;
+    RetainPtr<NSString> _placeholder;
+    RetainPtr<NSString> _label;
 }
 
 - (instancetype)initWithAssistedNodeInformation:(const AssistedNodeInformation&)information isUserInitiated:(BOOL)isUserInitiated userObject:(NSObject <NSSecureCoding> *)userObject
@@ -473,6 +475,8 @@ const CGFloat minimumTapHighlightRadius = 2.0;
     _value = information.value;
     _isUserInitiated = isUserInitiated;
     _userObject = userObject;
+    _placeholder = information.placeholder;
+    _label = information.label;
     return self;
 }
 
@@ -495,6 +499,17 @@ const CGFloat minimumTapHighlightRadius = 2.0;
 {
     return _userObject.get();
 }
+
+- (NSString *)label
+{
+    return _label.get();
+}
+
+- (NSString *)placeholder
+{
+    return _placeholder.get();
+}
+
 @end
 
 #if ENABLE(DRAG_SUPPORT)
index 2987080..2a947fd 100644 (file)
@@ -91,6 +91,7 @@
 #import <WebCore/MainFrame.h>
 #import <WebCore/MediaSessionManagerIOS.h>
 #import <WebCore/Node.h>
+#import <WebCore/NodeList.h>
 #import <WebCore/NotImplemented.h>
 #import <WebCore/Page.h>
 #import <WebCore/Pasteboard.h>
@@ -2749,6 +2750,22 @@ void WebPage::getAssistedNodeInformation(AssistedNodeInformation& information)
     information.hasPreviousNode = hasAssistableElement(m_assistedNode.get(), *m_page, false);
     information.assistedNodeIdentifier = m_currentAssistedNodeIdentifier;
 
+    if (is<LabelableElement>(*m_assistedNode)) {
+        auto labels = downcast<LabelableElement>(*m_assistedNode).labels();
+        Vector<Ref<Element>> associatedLabels;
+        for (unsigned index = 0; index < labels->length(); ++index) {
+            if (is<Element>(labels->item(index)) && labels->item(index)->renderer())
+                associatedLabels.append(downcast<Element>(*labels->item(index)));
+        }
+        for (auto& labelElement : associatedLabels) {
+            auto text = labelElement->innerText();
+            if (!text.isEmpty()) {
+                information.label = WTFMove(text);
+                break;
+            }
+        }
+    }
+
     if (is<HTMLSelectElement>(*m_assistedNode)) {
         HTMLSelectElement& element = downcast<HTMLSelectElement>(*m_assistedNode);
         information.elementType = InputType::Select;
@@ -2779,6 +2796,7 @@ void WebPage::getAssistedNodeInformation(AssistedNodeInformation& information)
         information.isReadOnly = element.isReadOnly();
         information.value = element.value();
         information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
+        information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
     } else if (is<HTMLInputElement>(*m_assistedNode)) {
         HTMLInputElement& element = downcast<HTMLInputElement>(*m_assistedNode);
         HTMLFormElement* form = element.form();
@@ -2788,6 +2806,7 @@ void WebPage::getAssistedNodeInformation(AssistedNodeInformation& information)
         information.representingPageURL = element.document().urlForBindings();
         information.autocapitalizeType = element.autocapitalizeType();
         information.isAutocorrect = element.shouldAutocorrect();
+        information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
         if (element.isPasswordField())
             information.elementType = InputType::Password;
         else if (element.isSearchField())
index 4d7c94f..63f0a2b 100644 (file)
@@ -1,3 +1,22 @@
+2018-03-20  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add AssistedNodeInformation plumbing for form control placeholder text and label text
+        https://bugs.webkit.org/show_bug.cgi?id=183802
+        <rdar://problem/38686273>
+
+        Reviewed by Tim Horton.
+
+        Adds a new API test to exercise new placeholder and label SPI on _WKFocusedFormElement.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/_WKInputDelegate.mm:
+        (-[InputDelegate _webView:focusShouldStartInputSession:]):
+        (-[InputDelegate shouldStartInputSessionHandler]):
+        (-[InputDelegate setShouldStartInputSessionHandler:]):
+        (TEST):
+        (-[FormSubmissionDelegate webView:startURLSchemeTask:]): Deleted.
+        (-[FormSubmissionDelegate webView:stopURLSchemeTask:]): Deleted.
+        (-[FormSubmissionDelegate _webView:willSubmitFormValues:userObject:submissionHandler:]): Deleted.
+
 2018-03-20  Basuke Suzuki  <Basuke.Suzuki@sony.com>
 
         [WinCairo] Fix to run-webkit-httpd from native Windows.
index 09c96a1..d132b21 100644 (file)
 
 #import "PlatformUtilities.h"
 #import "Test.h"
+#import "TestWKWebView.h"
 #import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/_WKFocusedElementInfo.h>
+#import <WebKit/_WKFormInputSession.h>
 #import <WebKit/_WKInputDelegate.h>
+#import <wtf/BlockPtr.h>
 #import <wtf/RetainPtr.h>
 
 #if WK_API_ENABLED
 static bool done;
 static bool willSubmitFormValuesCalled;
 
-@interface FormSubmissionDelegate : NSObject <_WKInputDelegate, WKURLSchemeHandler>
+@interface InputDelegate : NSObject <_WKInputDelegate, WKURLSchemeHandler>
+@property (nonatomic, copy) BOOL(^shouldStartInputSessionHandler)(id <_WKFocusedElementInfo>);
 @end
 
-@implementation FormSubmissionDelegate
+@implementation InputDelegate {
+    BlockPtr<BOOL(id <_WKFocusedElementInfo>)> _shouldStartInputSessionHandler;
+}
 
 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
 {
@@ -64,11 +71,28 @@ static bool willSubmitFormValuesCalled;
     submissionHandler();
 }
 
+- (BOOL)_webView:(WKWebView *)webView focusShouldStartInputSession:(id <_WKFocusedElementInfo>)info
+{
+    if (_shouldStartInputSessionHandler)
+        return _shouldStartInputSessionHandler(info);
+    return [info isUserInitiated];
+}
+
+- (BOOL(^)(id <_WKFocusedElementInfo>))shouldStartInputSessionHandler
+{
+    return _shouldStartInputSessionHandler.get();
+}
+
+- (void)setShouldStartInputSessionHandler:(BOOL(^)(id <_WKFocusedElementInfo>))handler
+{
+    _shouldStartInputSessionHandler = makeBlockPtr(handler);
+}
+
 @end
 
 TEST(WebKit, FormSubmission)
 {
-    auto delegate = adoptNS([[FormSubmissionDelegate alloc] init]);
+    auto delegate = adoptNS([[InputDelegate alloc] init]);
     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
@@ -81,6 +105,47 @@ TEST(WebKit, FormSubmission)
     TestWebKitAPI::Util::run(&done);
 }
 
+#if PLATFORM(IOS)
+
+TEST(WebKit, FocusedElementInfo)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[InputDelegate alloc] init]);
+    [webView _setInputDelegate:delegate.get()];
+
+    __block RetainPtr<id <_WKFocusedElementInfo>> currentElement;
+    [delegate setShouldStartInputSessionHandler:^BOOL(id<_WKFocusedElementInfo> element) {
+        currentElement = element;
+        return NO;
+    }];
+
+    [webView synchronouslyLoadHTMLString:@"<label for='foo'>bar</label><input id='foo'>"];
+    [webView stringByEvaluatingJavaScript:@"foo.focus()"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_WK_STREQ("", [currentElement placeholder]);
+    EXPECT_WK_STREQ("bar", [currentElement label]);
+
+    [webView synchronouslyLoadHTMLString:@"<input placeholder='bar'>"];
+    [webView stringByEvaluatingJavaScript:@"document.querySelector('input').focus()"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_WK_STREQ("bar", [currentElement placeholder]);
+    EXPECT_WK_STREQ("", [currentElement label]);
+
+    [webView synchronouslyLoadHTMLString:@"<label for='baz'>garply</label><select id='baz'></select>"];
+    [webView stringByEvaluatingJavaScript:@"baz.focus()"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_WK_STREQ("", [currentElement placeholder]);
+    EXPECT_WK_STREQ("garply", [currentElement label]);
+
+    [webView synchronouslyLoadHTMLString:@"<label for='foo' style='display: none'>bar</label><label for='foo'></label><input id='foo'><label for='foo'>garply</label>"];
+    [webView stringByEvaluatingJavaScript:@"foo.focus()"];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_WK_STREQ("", [currentElement placeholder]);
+    EXPECT_WK_STREQ("garply", [currentElement label]);
+}
+
+#endif // PLATFORM(IOS)
+
 #endif // WK_API_ENABLED