summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc')
-rw-r--r--chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc98
1 files changed, 96 insertions, 2 deletions
diff --git a/chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc b/chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc
index 71e240e746c..0aceee8edd7 100644
--- a/chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc
+++ b/chromium/third_party/blink/renderer/modules/accessibility/ax_selection.cc
@@ -15,11 +15,20 @@
#include "third_party/blink/renderer/core/editing/text_affinity.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object.h"
+#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
namespace blink {
namespace {
+// TODO(nektar): Add Web tests for this event.
+void ScheduleSelectEvent(TextControlElement& text_control) {
+ Event* event = Event::CreateBubble(event_type_names::kSelect);
+ event->SetTarget(&text_control);
+ text_control.GetDocument().EnqueueAnimationFrameEvent(event);
+}
+
+// TODO(nektar): Add Web tests for this event.
DispatchEventResult DispatchSelectStart(Node* node) {
if (!node)
return DispatchEventResult::kNotCanceled;
@@ -111,11 +120,11 @@ void AXSelection::ClearCurrentSelection(Document& document) {
AXSelection AXSelection::FromCurrentSelection(
const Document& document,
const AXSelectionBehavior selection_behavior) {
- LocalFrame* frame = document.GetFrame();
+ const LocalFrame* frame = document.GetFrame();
if (!frame)
return {};
- FrameSelection& frame_selection = frame->Selection();
+ const FrameSelection& frame_selection = frame->Selection();
if (!frame_selection.IsAvailable())
return {};
@@ -124,6 +133,30 @@ AXSelection AXSelection::FromCurrentSelection(
}
// static
+AXSelection AXSelection::FromCurrentSelection(
+ const TextControlElement& text_control) {
+ const Document& document = text_control.GetDocument();
+ AXObjectCache* ax_object_cache = document.ExistingAXObjectCache();
+ if (!ax_object_cache)
+ return {};
+
+ auto* ax_object_cache_impl = static_cast<AXObjectCacheImpl*>(ax_object_cache);
+ const AXObject* ax_text_control =
+ ax_object_cache_impl->GetOrCreate(&text_control);
+ DCHECK(ax_text_control);
+ const TextAffinity affinity = text_control.Selection().Affinity();
+ const auto ax_base = AXPosition::CreatePositionInTextObject(
+ *ax_text_control, static_cast<int>(text_control.selectionStart()));
+ const auto ax_extent = AXPosition::CreatePositionInTextObject(
+ *ax_text_control, static_cast<int>(text_control.selectionEnd()),
+ affinity);
+
+ AXSelection::Builder selection_builder;
+ selection_builder.SetBase(ax_base).SetExtent(ax_extent);
+ return selection_builder.Build();
+}
+
+// static
AXSelection AXSelection::FromSelection(
const SelectionInDOMTree& selection,
const AXSelectionBehavior selection_behavior) {
@@ -188,6 +221,33 @@ bool AXSelection::IsValid() const {
return false;
}
+ //
+ // The following code checks if a text position in a text control is valid.
+ // Since the contents of a text control are implemented using user agent
+ // shadow DOM, we want to prevent users from selecting across the shadow DOM
+ // boundary.
+ //
+ // TODO(nektar): Generalize this logic to adjust user selection if it crosses
+ // disallowed shadow DOM boundaries such as user agent shadow DOM, editing
+ // boundaries, replaced elements, CSS user-select, etc.
+ //
+
+ if (base_.IsTextPosition() &&
+ base_.ContainerObject()->IsNativeTextControl() &&
+ !(base_.ContainerObject() == extent_.ContainerObject() &&
+ extent_.IsTextPosition() &&
+ extent_.ContainerObject()->IsNativeTextControl())) {
+ return false;
+ }
+
+ if (extent_.IsTextPosition() &&
+ extent_.ContainerObject()->IsNativeTextControl() &&
+ !(base_.ContainerObject() == extent_.ContainerObject() &&
+ base_.IsTextPosition() &&
+ base_.ContainerObject()->IsNativeTextControl())) {
+ return false;
+ }
+
DCHECK(!base_.ContainerObject()->GetDocument()->NeedsLayoutTreeUpdate());
#if DCHECK_IS_ON()
DCHECK_EQ(base_.ContainerObject()->GetDocument()->DomTreeVersion(),
@@ -244,6 +304,22 @@ bool AXSelection::Select(const AXSelectionBehavior selection_behavior) {
return false;
}
+ base::Optional<AXSelection::TextControlSelection> text_control_selection =
+ AsTextControlSelection();
+ if (text_control_selection.has_value()) {
+ DCHECK_LE(text_control_selection->start, text_control_selection->end);
+ TextControlElement& text_control =
+ ToTextControl(*base_.ContainerObject()->GetNode());
+ if (!text_control.SetSelectionRange(text_control_selection->start,
+ text_control_selection->end,
+ text_control_selection->direction)) {
+ return false;
+ }
+
+ ScheduleSelectEvent(text_control);
+ return true;
+ }
+
const SelectionInDOMTree selection = AsSelection(selection_behavior);
DCHECK(selection.AssertValid());
Document* document = selection.Base().GetDocument();
@@ -313,6 +389,24 @@ String AXSelection::ToString() const {
return "AXSelection from " + Base().ToString() + " to " + Extent().ToString();
}
+base::Optional<AXSelection::TextControlSelection>
+AXSelection::AsTextControlSelection() const {
+ if (!IsValid() || !base_.IsTextPosition() || !extent_.IsTextPosition() ||
+ base_.ContainerObject() != extent_.ContainerObject() ||
+ !base_.ContainerObject()->IsNativeTextControl() ||
+ !IsTextControl(base_.ContainerObject()->GetNode())) {
+ return {};
+ }
+
+ if (base_ <= extent_) {
+ return TextControlSelection(base_.TextOffset(), extent_.TextOffset(),
+ kSelectionHasForwardDirection);
+ } else {
+ return TextControlSelection(extent_.TextOffset(), base_.TextOffset(),
+ kSelectionHasBackwardDirection);
+ }
+}
+
bool operator==(const AXSelection& a, const AXSelection& b) {
DCHECK(a.IsValid() && b.IsValid());
return a.Base() == b.Base() && a.Extent() == b.Extent();