Expose a WKWebView API for allowing programmatic focus to trigger node assistance
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Sep 2015 13:53:40 +0000 (13:53 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Sep 2015 13:53:40 +0000 (13:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149567

Reviewed by Darin Adler.

Adds an API method to WKWebView that allows clients to override the default iOS behavior of not
starting node assistance when programmatically focusing. Also fixes a bug caused by naively
introducing the flag where the autofocus attribute could trigger node assistance before layout
occurs, causing an ASSERT to fail in EditorState::postLayoutData.

I fixed this issue by not assisting the node if the editor state is missing post layout data,
instead deferring node assistance until layout has occurred.

* UIProcess/API/Cocoa/WKWebView.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView initWithFrame:configuration:]): Initializes the new node assistance flag to NO, which
    is the current default behavior.
(-[WKWebView canAssistOnProgrammaticFocus]):
(-[WKWebView setCanAssistOnProgrammaticFocus:]): New API method that allows clients to set node
    assistance behavior upon programmatic focus.
* UIProcess/API/Cocoa/WKWebViewConfiguration.h:
* UIProcess/API/Cocoa/WKWebViewConfiguration.mm:
(-[WKWebViewConfiguration init]): Added new node assistance flag to configuration.
(-[WKWebViewConfiguration _canAssistOnProgrammaticFocus]):
(-[WKWebViewConfiguration _setCanAssistOnProgrammaticFocus:]):
* UIProcess/WebPageProxy.h: Added NodeAssistanceArguments struct.
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]): Do not return
    early if the node assistance flag is set to allow programmatic focus.
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::didCommitLayerTree): After committing the layer tree, if we have deferred
    starting node assistance, immediately trigger node assistance.
(WebKit::WebPageProxy::startAssistingNode): Defer starting node assistance if we have not yet repainted.
(WebKit::WebPageProxy::stopAssistingNode): Also cancel any deferred node assistance.

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewConfiguration.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewConfiguration.mm
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm

index b183cda..026dbd7 100644 (file)
@@ -1,3 +1,40 @@
+2015-09-27  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Expose a WKWebView API for allowing programmatic focus to trigger node assistance
+        https://bugs.webkit.org/show_bug.cgi?id=149567
+
+        Reviewed by Darin Adler.
+
+        Adds an API method to WKWebView that allows clients to override the default iOS behavior of not
+        starting node assistance when programmatically focusing. Also fixes a bug caused by naively
+        introducing the flag where the autofocus attribute could trigger node assistance before layout
+        occurs, causing an ASSERT to fail in EditorState::postLayoutData.
+
+        I fixed this issue by not assisting the node if the editor state is missing post layout data,
+        instead deferring node assistance until layout has occurred.
+
+        * UIProcess/API/Cocoa/WKWebView.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView initWithFrame:configuration:]): Initializes the new node assistance flag to NO, which
+            is the current default behavior.
+        (-[WKWebView canAssistOnProgrammaticFocus]):
+        (-[WKWebView setCanAssistOnProgrammaticFocus:]): New API method that allows clients to set node
+            assistance behavior upon programmatic focus.
+        * UIProcess/API/Cocoa/WKWebViewConfiguration.h:
+        * UIProcess/API/Cocoa/WKWebViewConfiguration.mm:
+        (-[WKWebViewConfiguration init]): Added new node assistance flag to configuration.
+        (-[WKWebViewConfiguration _canAssistOnProgrammaticFocus]):
+        (-[WKWebViewConfiguration _setCanAssistOnProgrammaticFocus:]):
+        * UIProcess/WebPageProxy.h: Added NodeAssistanceArguments struct.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _startAssistingNode:userIsInteracting:blurPreviousNode:userObject:]): Do not return
+            early if the node assistance flag is set to allow programmatic focus.
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::didCommitLayerTree): After committing the layer tree, if we have deferred
+            starting node assistance, immediately trigger node assistance.
+        (WebKit::WebPageProxy::startAssistingNode): Defer starting node assistance if we have not yet repainted.
+        (WebKit::WebPageProxy::stopAssistingNode): Also cancel any deferred node assistance.
+
 2015-09-26  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         REGRESSION(r188639): [GTK] Several inspector tests started to time out in GTK+ bots after r188639
index 55debea..4a7dec7 100644 (file)
@@ -241,6 +241,11 @@ WK_CLASS_AVAILABLE(10_10, 8_0)
 /*! @abstract The scroll view associated with the web view.
  */
 @property (nonatomic, readonly, strong) UIScrollView *scrollView;
+
+/*! @abstract A Boolean value indicating whether programmatic focus on
+ an element is allowed to trigger assistance.
+ */
+@property (nonatomic) BOOL canAssistOnProgrammaticFocus;
 #endif
 
 #if !TARGET_OS_IPHONE
index 6b06a36..19a025b 100644 (file)
@@ -211,6 +211,8 @@ WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
     BOOL _pageIsPrintingToPDF;
     RetainPtr<CGPDFDocumentRef> _printedDocument;
     Vector<std::function<void ()>> _snapshotsDeferredDuringResize;
+
+    BOOL _canAssistOnProgrammaticFocus;
 #endif
 #if PLATFORM(MAC)
     RetainPtr<WKView> _wkView;
@@ -366,6 +368,8 @@ static bool shouldAllowPictureInPictureMediaPlayback()
     _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
 
     [[_configuration _contentProviderRegistry] addPage:*_page];
+
+    [self setCanAssistOnProgrammaticFocus:[_configuration canAssistOnProgrammaticFocus]];
 #endif
 
 #if PLATFORM(MAC)
@@ -716,6 +720,16 @@ static WKErrorCode callbackErrorCode(WebKit::CallbackBase::Error error)
     return _scrollView.get();
 }
 
+- (BOOL)canAssistOnProgrammaticFocus
+{
+    return _canAssistOnProgrammaticFocus;
+}
+
+- (void)setCanAssistOnProgrammaticFocus:(BOOL)canAssistOnProgrammaticFocus
+{
+    _canAssistOnProgrammaticFocus = canAssistOnProgrammaticFocus;
+}
+
 - (WKBrowsingContextController *)browsingContextController
 {
     return [_contentView browsingContextController];
index fe7ac78..dbdf2d1 100644 (file)
@@ -119,6 +119,12 @@ WK_CLASS_AVAILABLE(10_10, 8_0)
  */
 @property (nonatomic) BOOL allowsPictureInPictureMediaPlayback WK_AVAILABLE(NA, 9_0);
 
+/*! @abstract A Boolean value indicating whether programmatic focus on
+ an element is allowed to trigger assistance.
+ @discussion The default value is NO.
+ */
+@property (nonatomic) BOOL canAssistOnProgrammaticFocus;
+
 #endif
 
 @end
index f657e24..a2308d3 100644 (file)
@@ -112,6 +112,7 @@ private:
     _allowsInlineMediaPlayback = WKGetDeviceClass() == WKDeviceClassiPad;
     _inlineMediaPlaybackRequiresPlaysInlineAttribute = !_allowsInlineMediaPlayback;
     _mediaDataLoadsAutomatically = NO;
+    _canAssistOnProgrammaticFocus = NO;
 #endif
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
@@ -364,6 +365,16 @@ static NSString *defaultApplicationNameForUserAgent()
 {
     _mediaDataLoadsAutomatically = mediaDataLoadsAutomatically;
 }
+
+- (BOOL)_canAssistOnProgrammaticFocus
+{
+    return _canAssistOnProgrammaticFocus;
+}
+
+- (void)_setCanAssistOnProgrammaticFocus:(BOOL)canAssistOnProgrammaticFocus
+{
+    _canAssistOnProgrammaticFocus = canAssistOnProgrammaticFocus;
+}
 #endif
 
 @end
index ffae307..014a16e 100644 (file)
@@ -248,6 +248,12 @@ typedef GenericCallback<const String&, double, bool> FontAtSelectionCallback;
 #if PLATFORM(IOS)
 typedef GenericCallback<const WebCore::IntPoint&, uint32_t, uint32_t, uint32_t> GestureCallback;
 typedef GenericCallback<const WebCore::IntPoint&, uint32_t> TouchesCallback;
+struct NodeAssistanceArguments {
+    AssistedNodeInformation m_nodeInformation;
+    bool m_userIsInteracting;
+    bool m_blurPreviousNode;
+    RefPtr<API::Object> m_userData;
+};
 #endif
 
 #if PLATFORM(COCOA)
@@ -1765,6 +1771,11 @@ private:
 #endif
 
     bool m_userContentExtensionsEnabled { true };
+
+#if PLATFORM(IOS)
+    bool m_hasDeferredStartAssistingNode { false };
+    std::unique_ptr<NodeAssistanceArguments> m_deferredNodeAssistanceArguments;
+#endif
 };
 
 } // namespace WebKit
index 1230f79..e074707 100644 (file)
@@ -3015,7 +3015,7 @@ static bool isAssistableInputType(InputType type)
 {
     // FIXME: This is a temporary workaround for <rdar://problem/22126518>. The real fix will involve refactoring
     // the way we assist programmatically focused nodes.
-    if (!userIsInteracting && !_textSelectionAssistant)
+    if (!userIsInteracting && !_textSelectionAssistant && !_webView.canAssistOnProgrammaticFocus)
         return;
 
     if (blurPreviousNode)
index ed3fd48..a3ee2b5 100644 (file)
@@ -358,6 +358,12 @@ void WebPageProxy::didCommitLayerTree(const WebKit::RemoteLayerTreeTransaction&
         m_hitRenderTreeSizeThreshold = true;
         didLayout(WebCore::ReachedSessionRestorationRenderTreeSizeThreshold);
     }
+
+    if (m_hasDeferredStartAssistingNode) {
+        m_pageClient.startAssistingNode(m_deferredNodeAssistanceArguments->m_nodeInformation, m_deferredNodeAssistanceArguments->m_userIsInteracting, m_deferredNodeAssistanceArguments->m_blurPreviousNode, m_deferredNodeAssistanceArguments->m_userData.get());
+        m_hasDeferredStartAssistingNode = false;
+        m_deferredNodeAssistanceArguments = nullptr;
+    }
 }
 
 void WebPageProxy::selectWithGesture(const WebCore::IntPoint point, WebCore::TextGranularity granularity, uint32_t gestureType, uint32_t gestureState, std::function<void (const WebCore::IntPoint&, uint32_t, uint32_t, uint32_t, CallbackBase::Error)> callbackFunction)
@@ -805,11 +811,22 @@ void WebPageProxy::didGetTapHighlightGeometries(uint64_t requestID, const WebCor
 
 void WebPageProxy::startAssistingNode(const AssistedNodeInformation& information, bool userIsInteracting, bool blurPreviousNode, const UserData& userData)
 {
-    m_pageClient.startAssistingNode(information, userIsInteracting, blurPreviousNode, process().transformHandlesToObjects(userData.object()).get());
+    API::Object* userDataObject = process().transformHandlesToObjects(userData.object()).get();
+    if (m_editorState.isMissingPostLayoutData) {
+        m_deferredNodeAssistanceArguments = std::make_unique<NodeAssistanceArguments>(NodeAssistanceArguments { information, userIsInteracting, blurPreviousNode, userDataObject });
+        m_hasDeferredStartAssistingNode = true;
+        return;
+    }
+
+    m_pageClient.startAssistingNode(information, userIsInteracting, blurPreviousNode, userDataObject);
 }
 
 void WebPageProxy::stopAssistingNode()
 {
+    if (m_hasDeferredStartAssistingNode) {
+        m_hasDeferredStartAssistingNode = false;
+        m_deferredNodeAssistanceArguments = nullptr;
+    }
     m_pageClient.stopAssistingNode();
 }