blob: 1afcf7b3b3b19fea02c91a5fdf902de6dcb9533a [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/accessibility/AXSelection.h"
#include "core/dom/Document.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/PositionWithAffinity.h"
#include "core/editing/SelectionTemplate.h"
#include "core/editing/SetSelectionOptions.h"
#include "core/editing/TextAffinity.h"
#include "core/frame/LocalFrame.h"
#include "modules/accessibility/AXObject.h"
namespace blink {
AXSelection::Builder& AXSelection::Builder::SetBase(const AXPosition& base) {
DCHECK(base.IsValid());
selection_.base_ = base;
return *this;
}
AXSelection::Builder& AXSelection::Builder::SetBase(const Position& base) {
const auto ax_base = AXPosition::FromPosition(base);
DCHECK(ax_base.IsValid());
selection_.base_ = ax_base;
return *this;
}
AXSelection::Builder& AXSelection::Builder::SetExtent(
const AXPosition& extent) {
DCHECK(extent.IsValid());
selection_.extent_ = extent;
return *this;
}
AXSelection::Builder& AXSelection::Builder::SetExtent(const Position& extent) {
const auto ax_extent = AXPosition::FromPosition(extent);
DCHECK(ax_extent.IsValid());
selection_.extent_ = ax_extent;
return *this;
}
AXSelection::Builder& AXSelection::Builder::SetSelection(
const SelectionInDOMTree& selection) {
if (selection.IsNone())
return *this;
selection_.base_ = AXPosition::FromPosition(selection.Base());
selection_.extent_ = AXPosition::FromPosition(selection.Extent());
return *this;
}
const AXSelection AXSelection::Builder::Build() {
if (!selection_.Base().IsValid() || !selection_.Extent().IsValid())
return {};
const Document* document = selection_.Base().ContainerObject()->GetDocument();
DCHECK(document);
DCHECK(document->IsActive());
DCHECK(!document->NeedsLayoutTreeUpdate());
#if DCHECK_IS_ON()
selection_.dom_tree_version_ = document->DomTreeVersion();
selection_.style_version_ = document->StyleVersion();
#endif
return selection_;
}
AXSelection::AXSelection() : base_(), extent_() {
#if DCHECK_IS_ON()
dom_tree_version_ = 0;
style_version_ = 0;
#endif
}
bool AXSelection::IsValid() const {
if (!base_.IsValid() || !extent_.IsValid())
return false;
// We don't support selections that span across documents.
if (base_.ContainerObject()->GetDocument() !=
extent_.ContainerObject()->GetDocument())
return false;
DCHECK(!base_.ContainerObject()->GetDocument()->NeedsLayoutTreeUpdate());
#if DCHECK_IS_ON()
DCHECK_EQ(base_.ContainerObject()->GetDocument()->DomTreeVersion(),
dom_tree_version_);
DCHECK_EQ(base_.ContainerObject()->GetDocument()->StyleVersion(),
style_version_);
#endif // DCHECK_IS_ON()
return true;
}
const SelectionInDOMTree AXSelection::AsSelection() const {
if (!IsValid())
return {};
const auto dom_base = base_.ToPositionWithAffinity();
const auto dom_extent = extent_.ToPositionWithAffinity();
SelectionInDOMTree::Builder selection_builder;
selection_builder.SetBaseAndExtent(dom_base.GetPosition(),
dom_extent.GetPosition());
selection_builder.SetAffinity(extent_.Affinity());
return selection_builder.Build();
}
void AXSelection::Select() {
if (!IsValid()) {
NOTREACHED();
return;
}
const SelectionInDOMTree selection = AsSelection();
DCHECK(selection.AssertValid());
Document* document = selection.Base().GetDocument();
if (!document) {
NOTREACHED();
return;
}
LocalFrame* frame = document->GetFrame();
if (!frame) {
NOTREACHED();
return;
}
FrameSelection& frame_selection = frame->Selection();
frame_selection.SetSelection(selection, SetSelectionOptions());
}
bool operator==(const AXSelection& a, const AXSelection& b) {
DCHECK(!a.IsValid() || !b.IsValid());
return a.Base() == b.Base() && a.Extent() == b.Extent();
}
bool operator!=(const AXSelection& a, const AXSelection& b) {
return !(a == b);
}
std::ostream& operator<<(std::ostream& ostream, const AXSelection& selection) {
if (!selection.IsValid())
return ostream << "Invalid selection";
return ostream << "Selection from " << selection.Base() << " to "
<< selection.Extent();
}
} // namespace blink