Skip to content

Commit

Permalink
Highlight API: Implement painting behavior
Browse files Browse the repository at this point in the history
- Add painting code and HighlightMarker.
- Add visual update when a Range, registered Highlight or
HighlightRegistry is modified.
- Add StaticRange validation when validating HighlightMarkers.
- Move CheckForDifferentRootContainer() to
AbstractRange::HasDifferentRootContainer().
- Add correct ordering of HighlightMarkers for painting (includes
Highlight prioritization).

Bug: 1185234, 1185946
Change-Id: I6959b4cc444233c1aafbeea5f9ff011ace637555
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2857439
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Sanket Joshi <sajos@microsoft.com>
Reviewed-by: Mason Freed <masonf@chromium.org>
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: Fernando Fiori <ffiori@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#893033}
  • Loading branch information
ffiori authored and Chromium LUCI CQ committed Jun 16, 2021
1 parent 8651677 commit 5bffea2
Show file tree
Hide file tree
Showing 59 changed files with 1,265 additions and 96 deletions.
8 changes: 6 additions & 2 deletions third_party/blink/renderer/core/css/selector_checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1249,9 +1249,13 @@ bool SelectorChecker::CheckPseudoElement(const SelectorCheckingContext& context,
return true;
}
case CSSSelector::kPseudoHighlight: {
const AtomicString& highlight_name = selector.Argument();
result.dynamic_pseudo = PseudoId::kPseudoIdHighlight;
return highlight_name == pseudo_argument_;
// A null pseudo_argument_ means we are matching rules on the originating
// element. We keep track of which pseudo elements may match for the
// element through result.dynamic_pseudo. For ::highlight() pseudo
// elements we have a single flag for tracking whether an element may
// match _any_ ::highlight() element (kPseudoIdHighlight).
return !pseudo_argument_ || pseudo_argument_ == selector.Argument();
}
case CSSSelector::kPseudoTargetText:
if (!is_ua_rule_) {
Expand Down
11 changes: 7 additions & 4 deletions third_party/blink/renderer/core/css/style_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"

namespace blink {

Expand Down Expand Up @@ -57,10 +58,13 @@ class StyleRequest {
: parent_override(parent_override),
layout_parent_override(parent_override) {}

StyleRequest(PseudoId pseudo_id, const ComputedStyle* parent_override)
StyleRequest(PseudoId pseudo_id,
const ComputedStyle* parent_override,
const AtomicString& pseudo_argument = g_null_atom)
: parent_override(parent_override),
layout_parent_override(parent_override),
pseudo_id(pseudo_id) {}
pseudo_id(pseudo_id),
pseudo_argument(pseudo_argument) {}

StyleRequest(PseudoId pseudo_id,
CustomScrollbar* scrollbar,
Expand All @@ -76,8 +80,7 @@ class StyleRequest {
: pseudo_id(pseudo_id),
type(request_type),
scrollbar_part(kNoPart),
scrollbar(nullptr),
pseudo_argument(g_null_atom) {}
scrollbar(nullptr) {}
};

} // namespace blink
Expand Down
30 changes: 30 additions & 0 deletions third_party/blink/renderer/core/dom/abstract_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,39 @@

#include "third_party/blink/renderer/core/dom/abstract_range.h"

#include "third_party/blink/renderer/core/dom/character_data.h"
#include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/node.h"

namespace blink {

AbstractRange::AbstractRange() = default;
AbstractRange::~AbstractRange() = default;

bool AbstractRange::HasDifferentRootContainer(Node* start_root_container,
Node* end_root_container) {
return start_root_container->TreeRoot() != end_root_container->TreeRoot();
}

unsigned AbstractRange::LengthOfContents(const Node* node) {
// This switch statement must be consistent with that of
// Range::processContentsBetweenOffsets.
switch (node->getNodeType()) {
case Node::kTextNode:
case Node::kCdataSectionNode:
case Node::kCommentNode:
case Node::kProcessingInstructionNode:
return To<CharacterData>(node)->length();
case Node::kElementNode:
case Node::kDocumentNode:
case Node::kDocumentFragmentNode:
return To<ContainerNode>(node)->CountChildren();
case Node::kAttributeNode:
case Node::kDocumentTypeNode:
return 0;
}
NOTREACHED();
return 0;
}

} // namespace blink
5 changes: 5 additions & 0 deletions third_party/blink/renderer/core/dom/abstract_range.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class CORE_EXPORT AbstractRange : public ScriptWrappable {
virtual unsigned endOffset() const = 0;
virtual bool collapsed() const = 0;

static bool HasDifferentRootContainer(Node* start_root_container,
Node* end_root_container);
static unsigned LengthOfContents(const Node*);
virtual bool IsStaticRange() const = 0;

protected:
AbstractRange();
~AbstractRange() override;
Expand Down
10 changes: 6 additions & 4 deletions third_party/blink/renderer/core/dom/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5311,7 +5311,9 @@ bool Element::PseudoElementStylesDependOnFontMetrics() const {
return false;
}

const ComputedStyle* Element::CachedStyleForPseudoElement(PseudoId pseudo_id) {
const ComputedStyle* Element::CachedStyleForPseudoElement(
PseudoId pseudo_id,
const AtomicString& pseudo_argument) {
const ComputedStyle* style = GetComputedStyle();

if (!style || (pseudo_id < kFirstInternalPseudoId &&
Expand All @@ -5320,11 +5322,11 @@ const ComputedStyle* Element::CachedStyleForPseudoElement(PseudoId pseudo_id) {
}

if (const ComputedStyle* cached =
style->GetCachedPseudoElementStyle(pseudo_id))
style->GetCachedPseudoElementStyle(pseudo_id, pseudo_argument))
return cached;

scoped_refptr<ComputedStyle> result =
UncachedStyleForPseudoElement(StyleRequest(pseudo_id, style));
scoped_refptr<ComputedStyle> result = UncachedStyleForPseudoElement(
StyleRequest(pseudo_id, style, pseudo_argument));
if (result)
return style->AddCachedPseudoElementStyle(std::move(result));
return nullptr;
Expand Down
8 changes: 6 additions & 2 deletions third_party/blink/renderer/core/dom/element.h
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,12 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {

// Retrieve the ComputedStyle (if any) corresponding to the provided
// PseudoId from cache, calculating the ComputedStyle on-demand if it's
// missing from the cache.
const ComputedStyle* CachedStyleForPseudoElement(PseudoId);
// missing from the cache. The |pseudo_argument| is also used to match the
// ComputedStyle in cases where the PseudoId corresponds to a pseudo element
// that takes arguments (e.g. ::highlight()).
const ComputedStyle* CachedStyleForPseudoElement(
PseudoId,
const AtomicString& pseudo_argument = g_null_atom);

// Calculate the ComputedStyle corresponding to the provided StyleRequest,
// bypassing the pseudo style cache.
Expand Down
74 changes: 33 additions & 41 deletions third_party/blink/renderer/core/dom/range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@
#include "third_party/blink/renderer/core/editing/set_selection_options.h"
#include "third_party/blink/renderer/core/editing/visible_position.h"
#include "third_party/blink/renderer/core/editing/visible_units.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#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/geometry/dom_rect.h"
#include "third_party/blink/renderer/core/geometry/dom_rect_list.h"
#include "third_party/blink/renderer/core/highlight/highlight_registry.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
Expand Down Expand Up @@ -93,6 +95,8 @@ class RangeUpdateScope {
range_->RemoveFromSelectionIfInDifferentRoot(*old_document_);
range_->UpdateSelectionIfAddedToSelection();
}

range_->ScheduleVisualUpdateIfInRegisteredHighlight();
#if DCHECK_IS_ON()
current_range_ = nullptr;
#endif
Expand Down Expand Up @@ -184,20 +188,6 @@ Node* Range::commonAncestorContainer(const Node* container_a,
return container_a->CommonAncestor(*container_b, NodeTraversal::Parent);
}

static inline bool CheckForDifferentRootContainer(
const RangeBoundaryPoint& start,
const RangeBoundaryPoint& end) {
Node* end_root_container = &end.Container();
while (end_root_container->parentNode())
end_root_container = end_root_container->parentNode();
Node* start_root_container = &start.Container();
while (start_root_container->parentNode())
start_root_container = start_root_container->parentNode();

return start_root_container != end_root_container ||
(Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0);
}

void Range::setStart(Node* ref_node,
unsigned offset,
ExceptionState& exception_state) {
Expand All @@ -221,7 +211,9 @@ void Range::setStart(Node* ref_node,

start_.Set(*ref_node, offset, child_node);

if (did_move_document || CheckForDifferentRootContainer(start_, end_))
if (did_move_document ||
HasDifferentRootContainer(&start_.Container(), &end_.Container()) ||
compareBoundaryPoints(start_, end_, ASSERT_NO_EXCEPTION) > 0)
collapse(true);
}

Expand All @@ -248,7 +240,9 @@ void Range::setEnd(Node* ref_node,

end_.Set(*ref_node, offset, child_node);

if (did_move_document || CheckForDifferentRootContainer(start_, end_))
if (did_move_document ||
HasDifferentRootContainer(&start_.Container(), &end_.Container()) ||
compareBoundaryPoints(start_, end_, ASSERT_NO_EXCEPTION) > 0)
collapse(false);
}

Expand Down Expand Up @@ -490,27 +484,6 @@ static inline Node* ChildOfCommonRootBeforeOffset(Node* container,
return container;
}

static unsigned LengthOfContents(const Node* node) {
// This switch statement must be consistent with that of
// Range::processContentsBetweenOffsets.
switch (node->getNodeType()) {
case Node::kTextNode:
case Node::kCdataSectionNode:
case Node::kCommentNode:
case Node::kProcessingInstructionNode:
return To<CharacterData>(node)->length();
case Node::kElementNode:
case Node::kDocumentNode:
case Node::kDocumentFragmentNode:
return To<ContainerNode>(node)->CountChildren();
case Node::kAttributeNode:
case Node::kDocumentTypeNode:
return 0;
}
NOTREACHED();
return 0;
}

DocumentFragment* Range::ProcessContents(ActionType action,
ExceptionState& exception_state) {
DocumentFragment* fragment = nullptr;
Expand Down Expand Up @@ -568,7 +541,8 @@ DocumentFragment* Range::ProcessContents(ActionType action,
common_root->contains(&original_start.Container())) {
left_contents = ProcessContentsBetweenOffsets(
action, nullptr, &original_start.Container(), original_start.Offset(),
LengthOfContents(&original_start.Container()), exception_state);
AbstractRange::LengthOfContents(&original_start.Container()),
exception_state);
left_contents = ProcessAncestorsAndTheirSiblings(
action, &original_start.Container(), kProcessContentsForward,
left_contents, common_root, exception_state);
Expand Down Expand Up @@ -914,12 +888,15 @@ void Range::insertNode(Node* new_node, ExceptionState& exception_state) {

// 10. Let newOffset be parent's length if referenceNode is null, and
// referenceNode's index otherwise.
unsigned new_offset =
reference_node ? reference_node->NodeIndex() : LengthOfContents(&parent);
unsigned new_offset = reference_node
? reference_node->NodeIndex()
: AbstractRange::LengthOfContents(&parent);

// 11. Increase newOffset by node's length if node is a DocumentFragment node,
// and one otherwise.
new_offset += new_node->IsDocumentFragment() ? LengthOfContents(new_node) : 1;
new_offset += new_node->IsDocumentFragment()
? AbstractRange::LengthOfContents(new_node)
: 1;

// 12. Pre-insert node into parent before referenceNode.
parent.insertBefore(new_node, reference_node, exception_state);
Expand Down Expand Up @@ -1768,6 +1745,21 @@ void Range::UpdateSelectionIfAddedToSelection() {
selection.CacheRangeOfDocument(this);
}

void Range::ScheduleVisualUpdateIfInRegisteredHighlight() {
if (LocalDOMWindow* window = OwnerDocument().domWindow()) {
if (HighlightRegistry* highlight_registry =
window->Supplementable<LocalDOMWindow>::RequireSupplement<
HighlightRegistry>()) {
for (const auto& highlight : highlight_registry->GetHighlights()) {
if (highlight->Contains(this)) {
highlight_registry->ScheduleRepaint();
return;
}
}
}
}
}

void Range::RemoveFromSelectionIfInDifferentRoot(Document& old_document) {
if (!old_document.GetFrame())
return;
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/renderer/core/dom/range.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class CORE_EXPORT Range final : public AbstractRange {

static Node* CheckNodeWOffset(Node*, unsigned offset, ExceptionState&);

bool IsStaticRange() const override { return false; }
void Trace(Visitor*) const override;

private:
Expand Down Expand Up @@ -205,6 +206,7 @@ class CORE_EXPORT Range final : public AbstractRange {
Node* common_root,
ExceptionState&);
void UpdateSelectionIfAddedToSelection();
void ScheduleVisualUpdateIfInRegisteredHighlight();
void RemoveFromSelectionIfInDifferentRoot(Document& old_document);

Member<Document> owner_document_; // Cannot be null.
Expand Down
52 changes: 49 additions & 3 deletions third_party/blink/renderer/core/dom/static_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

#include "third_party/blink/renderer/bindings/core/v8/v8_static_range_init.h"
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"

Expand All @@ -16,9 +18,7 @@ namespace blink {
StaticRange::StaticRange(Document& document)
: owner_document_(document),
start_container_(document),
start_offset_(0u),
end_container_(document),
end_offset_(0u) {}
end_container_(document) {}

StaticRange::StaticRange(Document& document,
Node* start_container,
Expand Down Expand Up @@ -62,6 +62,52 @@ StaticRange* StaticRange::Create(Document& document,
static_range_init->endOffset());
}

namespace {

// Returns the lowest ancestor of |node| in the tree that has a containment set.
Node* GetLowestContainAncestor(const Node* node) {
for (Node& ancestor : NodeTraversal::InclusiveAncestorsOf(*node)) {
if (LayoutObject* node_layout_object = ancestor.GetLayoutObject()) {
if (node_layout_object->ShouldApplyAnyContainment()) {
return &ancestor;
}
}
}
return nullptr;
}

} // namespace

// Returns true if the range crosses any css-contain subtree boundary.
bool StaticRange::CrossesContainBoundary() const {
return GetLowestContainAncestor(start_container_) !=
GetLowestContainAncestor(end_container_);
}

bool StaticRange::IsValid() const {
if (dom_tree_version_ == owner_document_->DomTreeVersion())
return is_valid_;
dom_tree_version_ = owner_document_->DomTreeVersion();

// The full list of checks is:
// 1) The start offset is between 0 and the start container’s node length
// (inclusive).
// 2) The end offset is between 0 and the end container’s node length
// (inclusive).
// 3) The start and end containers of the static range are in the same DOM
// tree.
// 4) The position of the start boundary point is before or equal to the
// position of the end boundary point.
is_valid_ =
start_offset_ <= AbstractRange::LengthOfContents(start_container_) &&
end_offset_ <= AbstractRange::LengthOfContents(end_container_) &&
!HasDifferentRootContainer(start_container_, end_container_) &&
ComparePositionsInDOMTree(start_container_, start_offset_, end_container_,
end_offset_) <= 0;

return is_valid_;
}

void StaticRange::setStart(Node* container, unsigned offset) {
start_container_ = container;
start_offset_ = offset;
Expand Down
Loading

0 comments on commit 5bffea2

Please sign in to comment.