Support form submission from spatial navigation.
When we get to the last non-submit-button element of a form the IME
action should change from Next to Submit.
(cherry picked from commit 18b921a22c85e88c3b7314b3ab8885281f48e5ca)
Bug: 968557
Change-Id: I5e61f184d30e6088fa80f3cdf95bbed1c344f46c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1648464
Reviewed-by: David Bokan <bokan@chromium.org>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#667156}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1661365
Reviewed-by: Michael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/branch-heads/3770@{#1019}
Cr-Branched-From: a9eee1c7c727ef42a15d86e9fa7b77ff0e63840a-refs/heads/master@{#652427}
diff --git a/third_party/blink/public/mojom/page/spatial_navigation.mojom b/third_party/blink/public/mojom/page/spatial_navigation.mojom
index 51538fc..8358e7e 100644
--- a/third_party/blink/public/mojom/page/spatial_navigation.mojom
+++ b/third_party/blink/public/mojom/page/spatial_navigation.mojom
@@ -11,6 +11,8 @@
bool can_exit_focus;
// True if Spatial Navigation has a target that can be selected.
bool can_select_element;
+ // True if the currently focused element is a form element.
+ bool is_form_focused;
// True if the currently focused element is a form element, and there is a
// next form element available to move to.
bool has_next_form_element;
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.cc b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
index aefdaab..5dac4952 100644
--- a/third_party/blink/renderer/core/input/keyboard_event_manager.cc
+++ b/third_party/blink/renderer/core/input/keyboard_event_manager.cc
@@ -339,6 +339,9 @@
DefaultEscapeEventHandler(event);
} else if (event->key() == "Enter") {
DefaultEnterEventHandler(event);
+ } else if (static_cast<int>(event->KeyEvent()->dom_key) == 0x00200310) {
+ // TODO(bokan): Cleanup magic numbers once https://crbug.com/949766 lands.
+ DefaultImeSubmitHandler(event);
} else {
// TODO(bokan): Seems odd to call the default _arrow_ event handler on
// events that aren't necessarily arrow keys.
@@ -509,6 +512,17 @@
}
}
+void KeyboardEventManager::DefaultImeSubmitHandler(KeyboardEvent* event) {
+ Page* page = frame_->GetPage();
+ if (!page)
+ return;
+
+ if (IsSpatialNavigationEnabled(frame_) &&
+ !frame_->GetDocument()->InDesignMode()) {
+ page->GetSpatialNavigationController().HandleImeSubmitKeyboardEvent(event);
+ }
+}
+
static OverrideCapsLockState g_override_caps_lock_state;
void KeyboardEventManager::SetCurrentCapsLockState(
diff --git a/third_party/blink/renderer/core/input/keyboard_event_manager.h b/third_party/blink/renderer/core/input/keyboard_event_manager.h
index 3d9afb6..3f1b4c4 100644
--- a/third_party/blink/renderer/core/input/keyboard_event_manager.h
+++ b/third_party/blink/renderer/core/input/keyboard_event_manager.h
@@ -56,6 +56,7 @@
void DefaultTabEventHandler(KeyboardEvent*);
void DefaultEscapeEventHandler(KeyboardEvent*);
void DefaultEnterEventHandler(KeyboardEvent*);
+ void DefaultImeSubmitHandler(KeyboardEvent*);
void DefaultArrowEventHandler(KeyboardEvent*, Node*);
bool DefaultSpatNavBackEventHandler(KeyboardEvent*);
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index 8e8348d..041804d5 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -1158,9 +1158,12 @@
if (form_element->formOwner() != form_owner ||
form_element->IsDisabledOrReadOnly())
continue;
- // Focusless spatial navigation supports all form types.
+ // Focusless spatial navigation supports all form types. However, submit
+ // buttons are explicitly excluded as moving to them isn't necessary - the
+ // IME should just submit instead.
if (RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled() &&
- page_->GetSettings().GetSpatialNavigationEnabled()) {
+ page_->GetSettings().GetSpatialNavigationEnabled() &&
+ !form_element->CanBeSuccessfulSubmitButton()) {
return next_element;
}
LayoutObject* layout = next_element->GetLayoutObject();
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index 26f4ad85..2b0071c 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -17,6 +17,8 @@
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
@@ -176,6 +178,19 @@
return true;
}
+bool SpatialNavigationController::HandleImeSubmitKeyboardEvent(
+ KeyboardEvent* event) {
+ DCHECK(page_->GetSettings().GetSpatialNavigationEnabled());
+
+ if (!IsHTMLFormControlElement(GetFocusedElement()))
+ return false;
+
+ HTMLFormControlElement* element =
+ ToHTMLFormControlElement(GetFocusedElement());
+ element->formOwner()->SubmitImplicitly(*event, true);
+ return true;
+}
+
bool SpatialNavigationController::HandleEscapeKeyboardEvent(
KeyboardEvent* event) {
DCHECK(page_->GetSettings().GetSpatialNavigationEnabled());
@@ -516,6 +531,7 @@
bool change = false;
change |= UpdateCanExitFocus(element);
change |= UpdateCanSelectInterestedElement(element);
+ change |= UpdateIsFormFocused(element);
change |= UpdateHasNextFormElement(element);
change |= UpdateHasDefaultVideoControls(element);
if (change)
@@ -560,6 +576,15 @@
return true;
}
+bool SpatialNavigationController::UpdateIsFormFocused(Element* element) {
+ bool is_form_focused = IsFocused(element) && element->IsFormControlElement();
+
+ if (is_form_focused == spatial_navigation_state_->is_form_focused)
+ return false;
+ spatial_navigation_state_->is_form_focused = is_form_focused;
+ return true;
+}
+
bool SpatialNavigationController::UpdateHasDefaultVideoControls(
Element* element) {
bool has_default_video_controls =
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.h b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
index 12d6555..faee8c69 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.h
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.h
@@ -30,6 +30,7 @@
bool HandleArrowKeyboardEvent(KeyboardEvent* event);
bool HandleEnterKeyboardEvent(KeyboardEvent* event);
bool HandleEscapeKeyboardEvent(KeyboardEvent* event);
+ bool HandleImeSubmitKeyboardEvent(KeyboardEvent* event);
// Returns the element that's currently interested. i.e. the Element that's
// currently indicated to the user.
@@ -95,6 +96,7 @@
bool UpdateCanExitFocus(Element* element);
bool UpdateCanSelectInterestedElement(Element* element);
bool UpdateHasNextFormElement(Element* element);
+ bool UpdateIsFormFocused(Element* element);
bool UpdateHasDefaultVideoControls(Element* element);
const mojom::blink::SpatialNavigationHostPtr& GetSpatialNavigationHost();
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js b/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
index d475a8e7..4808163 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
+++ b/third_party/blink/web_tests/fast/spatial-navigation/resources/mock-snav-service.js
@@ -4,6 +4,7 @@
constructor() {
this.canExitFocus = false;
this.canSelectElement = false;
+ this.isFormFocused = false;
this.hasNextFormElement = false;
this.hasDefaultVideoControls = false;
this.callback = null;
@@ -18,6 +19,7 @@
spatialNavigationStateChanged(state) {
this.canExitFocus = state.canExitFocus;
this.canSelectElement = state.canSelectElement;
+ this.isFormFocused = state.isFormFocused;
this.hasNextFormElement = state.hasNextFormElement;
this.hasDefaultVideoControls = state.hasDefaultVideoControls;
if (this.callback) {
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit-a11y.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit-a11y.html
index 213abef..c5c66e3c 100644
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit-a11y.html
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit-a11y.html
@@ -25,8 +25,8 @@
"Should not be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
- assert_true(mockSnavService.hasNextFormElement,
- "Should not be able to move to next form element.");
+ assert_false(mockSnavService.hasNextFormElement,
+ "Should not be able to move to next form element.");
assert_equals(document.activeElement,
first,
@@ -38,7 +38,7 @@
"Should be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
- assert_true(mockSnavService.hasNextFormElement,
- "Should be able to move to next form element.");
+ assert_false(mockSnavService.hasNextFormElement,
+ "Should not be able to move to next form element.");
}, 'Form does not submit when passing spat nav focus.');
</script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html
index 36887c0..90865a5 100644
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html
@@ -30,7 +30,7 @@
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
assert_false(mockSnavService.hasNextFormElement,
- "Should not be able to move to next form element.");
+ "Should not be able to move to next form element.");
eventSender.keyDown('Enter');
assert_equals(document.activeElement,
@@ -43,7 +43,7 @@
"Should be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
- assert_true(mockSnavService.hasNextFormElement,
- "Should be able to move to next form element.");
+ assert_false(mockSnavService.hasNextFormElement,
+ "Should not be able to move to next form element.");
}, 'Form does not submit when passing spat nav focus.');
</script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
index 6501c24..3f7fc8da 100644
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
@@ -10,6 +10,7 @@
<input type="text" id="first" autofocus></input>
<input type="text" id="second"></input>
<input type="checkbox" id="third"></input>
+<input type="submit" id="submit">Submit</input>
</form>
<script>
@@ -38,6 +39,8 @@
"Should be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
+ assert_true(mockSnavService.isFormFocused,
+ "Form should be focused.");
assert_true(mockSnavService.hasNextFormElement,
"Should be able to move to next form element.");
@@ -59,6 +62,8 @@
"Should be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
+ assert_true(mockSnavService.isFormFocused,
+ "Form should be focused.");
assert_false(mockSnavService.hasNextFormElement,
"Should be able to move to next form element.");
@@ -69,6 +74,8 @@
"Should be able to exit focus.");
assert_true(mockSnavService.canSelectElement,
"Should be able to select element.");
+ assert_false(mockSnavService.isFormFocused,
+ "Form should not be focused.");
assert_false(mockSnavService.hasNextFormElement,
"Should be able to move to next form element.");