iOS: Updating input mode should update the software keyboard
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Jan 2019 07:53:14 +0000 (07:53 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Jan 2019 07:53:14 +0000 (07:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193565
<rdar://problem/47376334>

Reviewed by Wenson Hsieh.

Source/WebCore:

Let the chrome client know that the focused element's inputmode had changed.

Test: fast/forms/ios/inputmode-none-removed.html

* html/HTMLElement.cpp:
(WebCore::HTMLElement::parseAttribute):
* page/ChromeClient.h:

Source/WebKit:

Update the software keyboard when the inputmode content attribute on the focused element had been mutated.

* Scripts/webkit/messages.py:
* Shared/WebCoreArgumentCoders.h:
* UIProcess/PageClient.h:
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::focusedElementDidChangeInputMode):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _didUpdateInputMode:]):
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::focusedElementDidChangeInputMode):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::focusedElementDidChangeInputMode):
* WebProcess/WebCoreSupport/WebChromeClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::focusedElementDidChangeInputMode):
* WebProcess/WebPage/WebPage.h:

LayoutTests:

Added a regression test for removing inputmode content attribute with the value of "none".

The test methodology is different between testRunner and in-browser since we don't force
software keyboard while running layout tests inside simulator which can elimiate
the visual viewport difference inside the test runner, and in-browser testing obviously
doesn't have access to the internal keyboard metrics.

* fast/forms/ios/inputmode-none-removed-expected.txt: Added.
* fast/forms/ios/inputmode-none-removed.html: Added.

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

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/ios/inputmode-none-removed-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/ios/inputmode-none-removed.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLElement.cpp
Source/WebCore/page/ChromeClient.h
Source/WebKit/ChangeLog
Source/WebKit/Scripts/webkit/messages.py
Source/WebKit/Shared/WebCoreArgumentCoders.h
Source/WebKit/UIProcess/PageClient.h
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/ios/PageClientImplIOS.h
Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h

index 3546178..78794b4 100644 (file)
@@ -1,3 +1,21 @@
+2019-01-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        iOS: Updating input mode should update the software keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=193565
+        <rdar://problem/47376334>
+
+        Reviewed by Wenson Hsieh.
+
+        Added a regression test for removing inputmode content attribute with the value of "none".
+
+        The test methodology is different between testRunner and in-browser since we don't force
+        software keyboard while running layout tests inside simulator which can elimiate
+        the visual viewport difference inside the test runner, and in-browser testing obviously
+        doesn't have access to the internal keyboard metrics.
+
+        * fast/forms/ios/inputmode-none-removed-expected.txt: Added.
+        * fast/forms/ios/inputmode-none-removed.html: Added.
+
 2019-01-18  Justin Fan  <justin_fan@apple.com>
 
         (WIP) [WebGPU] WebGPUProgrammablePassEncoder::setBindGroup prototype
diff --git a/LayoutTests/fast/forms/ios/inputmode-none-removed-expected.txt b/LayoutTests/fast/forms/ios/inputmode-none-removed-expected.txt
new file mode 100644 (file)
index 0000000..810967a
--- /dev/null
@@ -0,0 +1,16 @@
+This tests updating inputmode of an input element from "none" to "text". The software keyboard should be updated.
+To manually test, focus the input element below. The software keyboard should show up after 3 seconds
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Element has inputmode=none
+PASS keyboardRect.height is 0
+
+inputmode has been removed
+PASS keyboardRect.height > 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/fast/forms/ios/inputmode-none-removed.html b/LayoutTests/fast/forms/ios/inputmode-none-removed.html
new file mode 100644 (file)
index 0000000..940197b
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+</head>
+<body>
+<input inputmode="none">
+<div id="countdown"></div>
+<script>
+jsTestIsAsync = true;
+
+description('This tests updating inputmode of an input element from "none" to "text". The software keyboard should be updated.<br>'
+    + 'To manually test, focus the input element below. The software keyboard should show up after 3 seconds');
+
+const input = document.querySelector("input");
+if (!window.testRunner) {
+    input.addEventListener('focus', () => {
+        let countdown = 3;
+        const id = setInterval(() => {
+            document.getElementById('countdown').textContent = countdown ? countdown : '';
+            if (!countdown) {
+                clearInterval(id);
+                input.removeAttribute('inputmode');
+            }
+            countdown--;
+        }, 1000);
+    });
+}
+
+async function runTest() {
+    debug('Element has inputmode=none');
+
+    let didResize = () => { };
+    window.visualViewport.addEventListener('resize', () => didResize());
+
+    if (window.testRunner) {
+        await UIHelper.activateFormControl(input);
+        window.keyboardRect = await UIHelper.inputViewBounds();
+        shouldBe('keyboardRect.height', '0');
+    } else {
+        await new Promise((resolve) => { didResize = resolve; });
+        shouldBeTrue('document.documentElement.clientHeight - visualViewport.height < 100');
+    }
+
+    if (window.testRunner)
+        input.removeAttribute('inputmode');
+
+    await new Promise((resolve) => { didResize = resolve; });
+
+    debug('');
+    debug('inputmode has been removed');
+
+    if (window.testRunner) {
+        window.keyboardRect = await UIHelper.inputViewBounds();
+        shouldBeTrue('keyboardRect.height > 0');
+    } else
+        shouldBeTrue('document.documentElement.clientHeight - visualViewport.height > 300');
+
+    finishJSTest();
+}
+
+window.onload = () => setTimeout(runTest, 0);
+
+</script>
+</body>
+</html>
index b37eb63..4e9ca03 100644 (file)
@@ -1,3 +1,19 @@
+2019-01-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        iOS: Updating input mode should update the software keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=193565
+        <rdar://problem/47376334>
+
+        Reviewed by Wenson Hsieh.
+
+        Let the chrome client know that the focused element's inputmode had changed.
+
+        Test: fast/forms/ios/inputmode-none-removed.html
+
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::parseAttribute):
+        * page/ChromeClient.h:
+
 2019-01-18  Brian Burg  <bburg@apple.com>
 
         Automation.computeElementLayout should return visual viewport-aware coordinates
index 82b371e..796e42d 100644 (file)
@@ -29,6 +29,8 @@
 #include "CSSPropertyNames.h"
 #include "CSSValueKeywords.h"
 #include "CSSValuePool.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
 #include "DOMTokenList.h"
 #include "DocumentFragment.h"
 #include "ElementAncestorIterator.h"
@@ -448,6 +450,14 @@ void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString&
             setTabIndexExplicitly(optionalTabIndex.value());
         return;
     }
+    
+    if (name == inputmodeAttr) {
+        auto& document = this->document();
+        if (this == document.focusedElement()) {
+            if (auto* page = document.page())
+                page->chrome().client().focusedElementDidChangeInputMode(*this, canonicalInputMode());
+        }
+    }
 
     auto& eventName = eventNameForEventHandlerAttribute(name);
     if (!eventName.isNull())
index 32040e1..a75d97c 100644 (file)
@@ -34,6 +34,7 @@
 #include "HTMLMediaElementEnums.h"
 #include "HostWindow.h"
 #include "Icon.h"
+#include "InputMode.h"
 #include "LayerFlushThrottleState.h"
 #include "MediaProducer.h"
 #include "PopupMenu.h"
@@ -292,7 +293,9 @@ public:
     virtual void elementDidFocus(Element&) { }
     virtual void elementDidBlur(Element&) { }
     virtual void elementDidRefocus(Element&) { }
-    
+
+    virtual void focusedElementDidChangeInputMode(Element&, InputMode) { }
+
     virtual bool shouldPaintEntireContents() const { return false; }
     virtual bool hasStablePageScaleFactor() const { return true; }
 
index 34d43f3..889ca88 100644 (file)
@@ -1,3 +1,33 @@
+2019-01-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        iOS: Updating input mode should update the software keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=193565
+        <rdar://problem/47376334>
+
+        Reviewed by Wenson Hsieh.
+
+        Update the software keyboard when the inputmode content attribute on the focused element had been mutated.
+
+        * Scripts/webkit/messages.py:
+        * Shared/WebCoreArgumentCoders.h:
+        * UIProcess/PageClient.h:
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::focusedElementDidChangeInputMode):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _didUpdateInputMode:]):
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::focusedElementDidChangeInputMode):
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::focusedElementDidChangeInputMode):
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::focusedElementDidChangeInputMode):
+        * WebProcess/WebPage/WebPage.h:
+
 2019-01-18  Tim Horton  <timothy_horton@apple.com>
 
         Adjust WKDrawingView protocol method name
index c774bc4..bfed964 100644 (file)
@@ -407,6 +407,7 @@ def headers_for_type(type):
         'WebCore::HasInsecureContent': ['<WebCore/FrameLoaderTypes.h>'],
         'WebCore::Highlight': ['<WebCore/InspectorOverlay.h>'],
         'WebCore::IncludeSecureCookies': ['<WebCore/CookieJar.h>'],
+        'WebCore::InputMode': ['<WebCore/InputMode.h>'],
         'WebCore::KeyframeValueList': ['<WebCore/GraphicsLayer.h>'],
         'WebCore::KeypressCommand': ['<WebCore/KeyboardEvent.h>'],
         'WebCore::LockBackForwardList': ['<WebCore/FrameLoaderTypes.h>'],
index 573aa75..3ae7e47 100644 (file)
@@ -32,6 +32,7 @@
 #include <WebCore/DiagnosticLoggingClient.h>
 #include <WebCore/FrameLoaderTypes.h>
 #include <WebCore/IndexedDB.h>
+#include <WebCore/InputMode.h>
 #include <WebCore/MediaSelectionOption.h>
 #include <WebCore/NetworkLoadMetrics.h>
 #include <WebCore/NotificationDirection.h>
@@ -766,6 +767,21 @@ template<> struct EnumTraits<WebCore::AutoplayEvent> {
     >;
 };
 
+template<> struct EnumTraits<WebCore::InputMode> {
+    using values = EnumValues<
+        WebCore::InputMode,
+        WebCore::InputMode::Unspecified,
+        WebCore::InputMode::None,
+        WebCore::InputMode::Text,
+        WebCore::InputMode::Telephone,
+        WebCore::InputMode::Url,
+        WebCore::InputMode::Email,
+        WebCore::InputMode::Numeric,
+        WebCore::InputMode::Decimal,
+        WebCore::InputMode::Search
+    >;
+};
+
 template<> struct EnumTraits<WebCore::NetworkLoadPriority> {
     using values = EnumValues<
         WebCore::NetworkLoadPriority,
index bf4eb2d..0795cd8 100644 (file)
@@ -34,6 +34,7 @@
 #include <WebCore/AlternativeTextClient.h>
 #include <WebCore/EditorClient.h>
 #include <WebCore/FocusDirection.h>
+#include <WebCore/InputMode.h>
 #include <WebCore/UserInterfaceLayoutDirection.h>
 #include <WebCore/ValidationBubble.h>
 #include <wtf/CompletionHandler.h>
@@ -371,6 +372,7 @@ public:
 
     virtual void elementDidFocus(const FocusedElementInformation&, bool userIsInteracting, bool blurPreviousNode, bool changingActivityState, API::Object* userData) = 0;
     virtual void elementDidBlur() = 0;
+    virtual void focusedElementDidChangeInputMode(WebCore::InputMode) = 0;
     virtual void didReceiveEditorStateUpdateAfterFocus() = 0;
     virtual bool isFocusingElement() = 0;
     virtual bool interpretKeyEvent(const NativeWebKeyboardEvent&, bool isCharEvent) = 0;
index 2286b35..773eb8d 100644 (file)
@@ -75,6 +75,7 @@
 #include <WebCore/FontAttributes.h>
 #include <WebCore/FrameLoaderTypes.h>
 #include <WebCore/FrameView.h> // FIXME: Move LayoutViewportConstraint to its own file and stop including this.
+#include <WebCore/InputMode.h>
 #include <WebCore/LayoutPoint.h>
 #include <WebCore/LayoutSize.h>
 #include <WebCore/MediaPlaybackTargetContext.h>
@@ -1803,6 +1804,7 @@ private:
 
     void elementDidFocus(const FocusedElementInformation&, bool userIsInteracting, bool blurPreviousNode, bool changingActivityState, const UserData&);
     void elementDidBlur();
+    void focusedElementDidChangeInputMode(WebCore::InputMode);
     void didReceiveEditorStateUpdateAfterFocus();
 
     void showInspectorHighlight(const WebCore::Highlight&);
index 2d28ee1..389a32b 100644 (file)
@@ -405,6 +405,7 @@ messages -> WebPageProxy {
 
     ElementDidFocus(struct WebKit::FocusedElementInformation information, bool userIsInteracting, bool blurPreviousNode, bool changingActivityState, WebKit::UserData userData)
     ElementDidBlur()
+    FocusedElementDidChangeInputMode(enum:uint8_t WebCore::InputMode mode)
     ScrollingNodeScrollWillStartScroll()
     ScrollingNodeScrollDidEndScroll()
     ShowInspectorHighlight(struct WebCore::Highlight highlight)
index 9183f8c..68e54db 100644 (file)
@@ -143,6 +143,7 @@ private:
 
     void elementDidFocus(const FocusedElementInformation&, bool userIsInteracting, bool blurPreviousNode, bool changingActivityState, API::Object* userData) override;
     void elementDidBlur() override;
+    void focusedElementDidChangeInputMode(WebCore::InputMode) override;
     void didReceiveEditorStateUpdateAfterFocus() override;
     bool isFocusingElement() override;
     void selectionDidChange() override;
index b610724..71ea6b8 100644 (file)
@@ -547,6 +547,11 @@ void PageClientImpl::elementDidBlur()
     [m_contentView _elementDidBlur];
 }
 
+void PageClientImpl::focusedElementDidChangeInputMode(WebCore::InputMode mode)
+{
+    [m_contentView _didUpdateInputMode:mode];
+}
+
 void PageClientImpl::didReceiveEditorStateUpdateAfterFocus()
 {
     [m_contentView _didReceiveEditorStateUpdateAfterFocus];
index a7ffff7..d508a3e 100644 (file)
@@ -388,6 +388,7 @@ FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW)
 - (void)_disableDoubleTapGesturesDuringTapIfNecessary:(uint64_t)requestID;
 - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode changingActivityState:(BOOL)changingActivityState userObject:(NSObject <NSSecureCoding> *)userObject;
 - (void)_elementDidBlur;
+- (void)_didUpdateInputMode:(WebCore::InputMode)mode;
 - (void)_didReceiveEditorStateUpdateAfterFocus;
 - (void)_selectionChanged;
 - (void)_updateChangedSelection;
index e86b629..8038a92 100644 (file)
@@ -4637,6 +4637,17 @@ static const double minimumFocusedElementAreaForSuppressingSelectionAssistant =
     }
 }
 
+- (void)_didUpdateInputMode:(WebCore::InputMode)mode
+{
+    if (!self.inputDelegate || _focusedElementInformation.elementType == WebKit::InputType::None)
+        return;
+
+#if !PLATFORM(WATCHOS)
+    _focusedElementInformation.inputMode = mode;
+    [self reloadInputViews];
+#endif
+}
+
 - (void)_didReceiveEditorStateUpdateAfterFocus
 {
     [self _updateInitialWritingDirectionIfNecessary];
index b9bd70c..8a5302d 100644 (file)
@@ -920,6 +920,11 @@ void WebPageProxy::elementDidBlur()
     pageClient().elementDidBlur();
 }
 
+void WebPageProxy::focusedElementDidChangeInputMode(WebCore::InputMode mode)
+{
+    pageClient().focusedElementDidChangeInputMode(mode);
+}
+
 void WebPageProxy::autofillLoginCredentials(const String& username, const String& password)
 {
     m_process->send(Messages::WebPage::AutofillLoginCredentials(username, password), m_pageID);
index 2bd16da..6c8245f 100644 (file)
@@ -213,6 +213,11 @@ void WebChromeClient::elementDidBlur(Element& element)
     m_page.elementDidBlur(element);
 }
 
+void WebChromeClient::focusedElementDidChangeInputMode(Element& element, InputMode mode)
+{
+    m_page.focusedElementDidChangeInputMode(element, mode);
+}
+
 void WebChromeClient::makeFirstResponder()
 {
     m_page.send(Messages::WebPageProxy::MakeFirstResponder());
index b1415d1..38048be 100644 (file)
@@ -275,6 +275,7 @@ private:
     void elementDidFocus(WebCore::Element&) final;
     void elementDidBlur(WebCore::Element&) final;
     void elementDidRefocus(WebCore::Element&) final;
+    void focusedElementDidChangeInputMode(WebCore::Element&, WebCore::InputMode) final;
 
     void makeFirstResponder() final;
     void assistiveTechnologyMakeFirstResponder() final;
index dd97d6e..26ec534 100644 (file)
 #include <WebCore/HTMLOListElement.h>
 #include <WebCore/HTMLPlugInElement.h>
 #include <WebCore/HTMLPlugInImageElement.h>
+#include <WebCore/HTMLTextAreaElement.h>
 #include <WebCore/HTMLUListElement.h>
 #include <WebCore/HistoryController.h>
 #include <WebCore/HistoryItem.h>
@@ -5360,6 +5361,22 @@ void WebPage::elementDidBlur(WebCore::Element& element)
     }
 }
 
+void WebPage::focusedElementDidChangeInputMode(WebCore::Element& element, WebCore::InputMode mode)
+{
+#if PLATFORM(IOS_FAMILY)
+    ASSERT(m_focusedElement == &element);
+    ASSERT(element.canonicalInputMode() == mode);
+
+    if (!is<HTMLTextAreaElement>(*m_focusedElement) && !is<HTMLInputElement>(*m_focusedElement) && !m_focusedElement->hasEditableStyle())
+        return;
+
+    send(Messages::WebPageProxy::FocusedElementDidChangeInputMode(mode));
+#else
+    UNUSED_PARAM(element);
+    UNUSED_PARAM(mode);
+#endif
+}
+
 void WebPage::didUpdateComposition()
 {
     sendEditorStateUpdate();
index f3b934e..d4d21d3 100644 (file)
@@ -589,6 +589,7 @@ public:
     void elementDidFocus(WebCore::Element&);
     void elementDidRefocus(WebCore::Element&);
     void elementDidBlur(WebCore::Element&);
+    void focusedElementDidChangeInputMode(WebCore::Element&, WebCore::InputMode);
     void resetFocusedElementForFrame(WebFrame*);
 
     void disabledAdaptationsDidChange(const OptionSet<WebCore::DisabledAdaptations>&);