Commit cb8c6e7f authored by Oriol Brufau's avatar Oriol Brufau Committed by Chromium LUCI CQ

[editing] Move caret when navigating to a fragment with caret browsing

When navigating to #foo, the element with id="foo" is scrolled into view
and is focused if focusable. This patch makes it receive the caret too,
in case caret browsing is enabled.

Bug: 1011934

TEST=editing/caret/caret-browsing-fragment-anchor.html

Change-Id: I044e6b48545426afdb0c5ee766d4ee3a218aa95b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2463308
Commit-Queue: Oriol Brufau <obrufau@igalia.com>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843064}
parent b5564629
...@@ -279,6 +279,7 @@ class TestRunnerBindings : public gin::Wrappable<TestRunnerBindings> { ...@@ -279,6 +279,7 @@ class TestRunnerBindings : public gin::Wrappable<TestRunnerBindings> {
v8::Local<v8::Function> callback); v8::Local<v8::Function> callback);
void SetBluetoothManualChooser(bool enable); void SetBluetoothManualChooser(bool enable);
void SetCanOpenWindows(); void SetCanOpenWindows();
void SetCaretBrowsingEnabled();
void SetColorProfile(const std::string& name, void SetColorProfile(const std::string& name,
v8::Local<v8::Function> callback); v8::Local<v8::Function> callback);
void SetCustomPolicyDelegate(gin::Arguments* args); void SetCustomPolicyDelegate(gin::Arguments* args);
...@@ -669,6 +670,8 @@ gin::ObjectTemplateBuilder TestRunnerBindings::GetObjectTemplateBuilder( ...@@ -669,6 +670,8 @@ gin::ObjectTemplateBuilder TestRunnerBindings::GetObjectTemplateBuilder(
&TestRunnerBindings::SetBluetoothManualChooser) &TestRunnerBindings::SetBluetoothManualChooser)
.SetMethod("setCallCloseOnWebViews", &TestRunnerBindings::NotImplemented) .SetMethod("setCallCloseOnWebViews", &TestRunnerBindings::NotImplemented)
.SetMethod("setCanOpenWindows", &TestRunnerBindings::SetCanOpenWindows) .SetMethod("setCanOpenWindows", &TestRunnerBindings::SetCanOpenWindows)
.SetMethod("setCaretBrowsingEnabled",
&TestRunnerBindings::SetCaretBrowsingEnabled)
.SetMethod("setColorProfile", &TestRunnerBindings::SetColorProfile) .SetMethod("setColorProfile", &TestRunnerBindings::SetColorProfile)
.SetMethod("setCustomPolicyDelegate", .SetMethod("setCustomPolicyDelegate",
&TestRunnerBindings::SetCustomPolicyDelegate) &TestRunnerBindings::SetCustomPolicyDelegate)
...@@ -1470,6 +1473,13 @@ void TestRunnerBindings::SetCanOpenWindows() { ...@@ -1470,6 +1473,13 @@ void TestRunnerBindings::SetCanOpenWindows() {
runner_->SetCanOpenWindows(); runner_->SetCanOpenWindows();
} }
void TestRunnerBindings::SetCaretBrowsingEnabled() {
if (invalid_)
return;
blink::WebView* web_view = GetWebFrame()->View();
web_view->GetSettings()->SetCaretBrowsingEnabled(true);
}
void TestRunnerBindings::SetImagesAllowed(bool allowed) { void TestRunnerBindings::SetImagesAllowed(bool allowed) {
if (invalid_) if (invalid_)
return; return;
...@@ -2301,6 +2311,7 @@ void TestRunner::ResetWebView(WebViewTestProxy* web_view_test_proxy) { ...@@ -2301,6 +2311,7 @@ void TestRunner::ResetWebView(WebViewTestProxy* web_view_test_proxy) {
web_view->SetTabKeyCyclesThroughElements(true); web_view->SetTabKeyCyclesThroughElements(true);
web_view->GetSettings()->SetHighlightAds(false); web_view->GetSettings()->SetHighlightAds(false);
web_view->GetSettings()->SetCaretBrowsingEnabled(false);
web_view->DisableAutoResizeForTesting(gfx::Size()); web_view->DisableAutoResizeForTesting(gfx::Size());
web_view->SetScreenOrientationOverrideForTesting( web_view->SetScreenOrientationOverrideForTesting(
fake_screen_orientation_impl_.CurrentOrientationType()); fake_screen_orientation_impl_.CurrentOrientationType());
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/events/event.h" #include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/svg/svg_svg_element.h" #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
...@@ -174,11 +175,27 @@ void ElementFragmentAnchor::ApplyFocusIfNeeded() { ...@@ -174,11 +175,27 @@ void ElementFragmentAnchor::ApplyFocusIfNeeded() {
if (!anchor_node_) if (!anchor_node_)
return; return;
frame_->GetDocument()->UpdateStyleAndLayoutTree();
// If caret browsing is enabled, move the caret to the beginning of the
// fragment, or to the first non-inert position after it.
if (frame_->IsCaretBrowsingEnabled()) {
const Position& pos = Position::FirstPositionInOrBeforeNode(*anchor_node_);
if (pos.IsConnected()) {
frame_->Selection().SetSelection(
SelectionInDOMTree::Builder().Collapse(pos).Build(),
SetSelectionOptions::Builder()
.SetShouldCloseTyping(true)
.SetShouldClearTypingStyle(true)
.SetDoNotSetFocus(true)
.Build());
}
}
// If the anchor accepts keyboard focus and fragment scrolling is allowed, // If the anchor accepts keyboard focus and fragment scrolling is allowed,
// move focus there to aid users relying on keyboard navigation. // move focus there to aid users relying on keyboard navigation.
// If anchorNode is not focusable or fragment scrolling is not allowed, // If anchorNode is not focusable or fragment scrolling is not allowed,
// clear focus, which matches the behavior of other browsers. // clear focus, which matches the behavior of other browsers.
frame_->GetDocument()->UpdateStyleAndLayoutTree();
auto* element = DynamicTo<Element>(anchor_node_.Get()); auto* element = DynamicTo<Element>(anchor_node_.Get());
if (element && element->IsFocusable()) { if (element && element->IsFocusable()) {
element->focus(); element->focus();
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>Caret browsing with fragment anchor navigation</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<meta name="assert" content="
This test checks that, when caret browsing is enabled, navigating to a fragment moves
the caret to the beginning of the fragment. Or, if the fragment is inert, to the next
non-inert position (which may be out of view, since it's the fragment what's scrolled
into view, not the caret).
" />
<style>
.separator {
margin: 50vh 0;
}
</style>
<div class="separator">Content 1</div>
<div class="test" id="normal">Normal</div>
<div class="separator">Content 2</div>
<div class="test" id="focusable" tabindex="-1" data-focusable>Focusable</div>
<div class="separator">Content 3</div>
<div class="test" id="inert" inert data-caret-target="after-inert">Inert</div>
<div class="separator" id="after-inert">Content 4</div>
<div inert>
<div class="separator">Content 5</div>
<div class="test" id="inside-inert" data-caret-target="after-inside-inert">Inside inert</div>
<div class="separator">Content 6</div>
</div>
<div class="separator" id="after-inside-inert">Content 7</div>
<div id="log"></div>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
function setHash(hash) {
return new Promise((resolve) => {
addEventListener("hashchange", resolve, {once: true});
location.hash = "#" + hash;
});
}
if (window.testRunner) {
testRunner.setCaretBrowsingEnabled();
}
const selection = getSelection();
const scroller = document.scrollingElement;
for (let target of document.querySelectorAll(".test")) {
promise_test(async (t) => {
await setHash(target.id);
// Check scroll offset
const actualScrollOffset = scroller.scrollTop;
target.scrollIntoView();
const expectedScrollOffset = scroller.scrollTop;
assert_equals(actualScrollOffset, expectedScrollOffset, "Scrolled into view");
// Check focused element
const focusTarget = target.hasAttribute("data-focusable")
? target : document.body;
assert_equals(document.activeElement, focusTarget, "Focused right element");
// Check caret
const caretTarget = target.dataset.caretTarget
? document.getElementById(target.dataset.caretTarget)
: target;
assert_equals(selection.anchorNode, caretTarget.firstChild, "Received caret");
assert_equals(selection.anchorOffset, 0, "Caret at position 0");
assert_equals(selection.isCollapsed, true, "Selection is collapsed");
}, target.id);
}
add_completion_callback(async () => {
// Scroll to log and remove hash from URL
await setHash("log");
history.pushState(null, "", " ");
});
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment