blob: 5426d6fb34729eb2c287936758f2c5c5f8599f1c [file] [log] [blame]
// Copyright 2017 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 "third_party/blink/renderer/core/paint/selection_painting_utils.h"
#include "third_party/blink/renderer/core/css/pseudo_style_request.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/text_paint_style.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/graphics/color.h"
namespace blink {
namespace {
bool NodeIsSelectable(const ComputedStyle& style, Node* node) {
return !node->IsInert() && !(style.UserSelect() == EUserSelect::kNone &&
style.UserModify() == EUserModify::kReadOnly);
}
scoped_refptr<ComputedStyle> GetUncachedSelectionStyle(Node* node) {
if (!node)
return nullptr;
// In Blink, ::selection only applies to direct children of the element on
// which ::selection is matched. In order to be able to style ::selection
// inside elements implemented with a UA shadow tree, like input::selection,
// we calculate ::selection style on the shadow host for elements inside the
// UA shadow.
if (ShadowRoot* root = node->ContainingShadowRoot()) {
if (root->IsUserAgent()) {
if (Element* shadow_host = node->OwnerShadowHost()) {
return shadow_host->StyleForPseudoElement(
PseudoStyleRequest(kPseudoIdSelection));
}
}
}
// If we request ::selection style for LayoutText, query ::selection style on
// the parent element instead, as that is the node for which ::selection
// matches.
Element* element = Traversal<Element>::FirstAncestorOrSelf(*node);
if (!element || element->IsPseudoElement())
return nullptr;
return element->StyleForPseudoElement(PseudoStyleRequest(kPseudoIdSelection));
}
Color SelectionColor(const Document& document,
const ComputedStyle& style,
Node* node,
const CSSProperty& color_property,
const GlobalPaintFlags global_paint_flags) {
// If the element is unselectable, or we are only painting the selection,
// don't override the foreground color with the selection foreground color.
if ((node && !NodeIsSelectable(style, node)) ||
(global_paint_flags & kGlobalPaintSelectionOnly))
return style.VisitedDependentColor(color_property);
if (scoped_refptr<ComputedStyle> pseudo_style =
GetUncachedSelectionStyle(node))
return pseudo_style->VisitedDependentColor(color_property);
if (!LayoutTheme::GetTheme().SupportsSelectionForegroundColors())
return style.VisitedDependentColor(color_property);
return document.GetFrame()->Selection().FrameIsFocusedAndActive()
? LayoutTheme::GetTheme().ActiveSelectionForegroundColor()
: LayoutTheme::GetTheme().InactiveSelectionForegroundColor();
}
const ComputedStyle* SelectionPseudoStyle(Node* node) {
if (!node)
return nullptr;
Element* element = Traversal<Element>::FirstAncestorOrSelf(*node);
if (!element)
return nullptr;
return element->CachedStyleForPseudoElement(
PseudoStyleRequest(kPseudoIdSelection));
}
} // anonymous namespace
Color SelectionPaintingUtils::SelectionBackgroundColor(
const Document& document,
const ComputedStyle& style,
Node* node) {
if (node && !NodeIsSelectable(style, node))
return Color::kTransparent;
if (scoped_refptr<ComputedStyle> pseudo_style =
GetUncachedSelectionStyle(node)) {
return pseudo_style->VisitedDependentColor(GetCSSPropertyBackgroundColor())
.BlendWithWhite();
}
return document.GetFrame()->Selection().FrameIsFocusedAndActive()
? LayoutTheme::GetTheme().ActiveSelectionBackgroundColor()
: LayoutTheme::GetTheme().InactiveSelectionBackgroundColor();
}
Color SelectionPaintingUtils::SelectionForegroundColor(
const Document& document,
const ComputedStyle& style,
Node* node,
const GlobalPaintFlags global_paint_flags) {
return SelectionColor(document, style, node,
GetCSSPropertyWebkitTextFillColor(),
global_paint_flags);
}
Color SelectionPaintingUtils::SelectionEmphasisMarkColor(
const Document& document,
const ComputedStyle& style,
Node* node,
const GlobalPaintFlags global_paint_flags) {
return SelectionColor(document, style, node,
GetCSSPropertyWebkitTextEmphasisColor(),
global_paint_flags);
}
TextPaintStyle SelectionPaintingUtils::SelectionPaintingStyle(
const Document& document,
const ComputedStyle& style,
Node* node,
bool have_selection,
const TextPaintStyle& text_style,
const PaintInfo& paint_info) {
TextPaintStyle selection_style = text_style;
bool uses_text_as_clip = paint_info.phase == PaintPhase::kTextClip;
bool is_printing = paint_info.IsPrinting();
const GlobalPaintFlags global_paint_flags = paint_info.GetGlobalPaintFlags();
if (have_selection) {
if (!uses_text_as_clip) {
selection_style.fill_color =
SelectionForegroundColor(document, style, node, global_paint_flags);
selection_style.emphasis_mark_color =
SelectionEmphasisMarkColor(document, style, node, global_paint_flags);
}
if (const ComputedStyle* pseudo_style = SelectionPseudoStyle(node)) {
selection_style.stroke_color =
uses_text_as_clip ? Color::kBlack
: pseudo_style->VisitedDependentColor(
GetCSSPropertyWebkitTextStrokeColor());
selection_style.stroke_width = pseudo_style->TextStrokeWidth();
selection_style.shadow =
uses_text_as_clip ? nullptr : pseudo_style->TextShadow();
}
// Text shadows are disabled when printing. http://crbug.com/258321
if (is_printing)
selection_style.shadow = nullptr;
}
return selection_style;
}
} // namespace blink