blob: 62030873890ce4e74a9332028cb058e0ca596a17 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc.
* All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
* Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
* (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/css/resolver/style_adjuster.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/html/html_table_cell_element.h"
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input_type_names.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_replaced.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/layout/list_marker.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
#include "third_party/blink/renderer/core/svg_names.h"
#include "third_party/blink/renderer/platform/geometry/length.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "ui/base/ui_base_features.h"
namespace blink {
namespace {
bool IsOverflowClipOrVisible(EOverflow overflow) {
return overflow == EOverflow::kClip || overflow == EOverflow::kVisible;
}
bool IsEditableElement(Element* element, const ComputedStyle& style) {
if (style.UserModify() != EUserModify::kReadOnly)
return true;
if (!element)
return false;
if (auto* textarea = DynamicTo<HTMLTextAreaElement>(*element))
return !textarea->IsDisabledOrReadOnly();
if (auto* input = DynamicTo<HTMLInputElement>(*element))
return !input->IsDisabledOrReadOnly() && input->IsTextField();
return false;
}
TouchAction AdjustTouchActionForElement(TouchAction touch_action,
const ComputedStyle& style,
Element* element) {
// if body is the viewport defining element then ScrollsOverflow should
// return false as body should have overflow-x/overflow-y set to visible
Element* body = element ? element->GetDocument().body() : nullptr;
bool is_body_and_viewport =
element && element == body &&
body == element->GetDocument().ViewportDefiningElement();
bool is_child_document =
element && element == element->GetDocument().documentElement() &&
element->GetDocument().LocalOwner();
if ((!is_body_and_viewport && style.ScrollsOverflow()) || is_child_document)
return touch_action | TouchAction::kPan | TouchAction::kInternalPanXScrolls;
return touch_action;
}
bool HostIsInputFile(const Element* element) {
if (!element || !element->IsInUserAgentShadowRoot())
return false;
if (const Element* shadow_host = element->OwnerShadowHost()) {
if (const auto* input = DynamicTo<HTMLInputElement>(shadow_host))
return input->type() == input_type_names::kFile;
}
return false;
}
} // namespace
static EDisplay EquivalentBlockDisplay(EDisplay display) {
switch (display) {
case EDisplay::kBlock:
case EDisplay::kTable:
case EDisplay::kWebkitBox:
case EDisplay::kFlex:
case EDisplay::kGrid:
case EDisplay::kBlockMath:
case EDisplay::kListItem:
case EDisplay::kFlowRoot:
case EDisplay::kLayoutCustom:
return display;
case EDisplay::kInlineTable:
return EDisplay::kTable;
case EDisplay::kWebkitInlineBox:
return EDisplay::kWebkitBox;
case EDisplay::kInlineFlex:
return EDisplay::kFlex;
case EDisplay::kInlineGrid:
return EDisplay::kGrid;
case EDisplay::kMath:
return EDisplay::kBlockMath;
case EDisplay::kInlineLayoutCustom:
return EDisplay::kLayoutCustom;
case EDisplay::kContents:
case EDisplay::kInline:
case EDisplay::kInlineBlock:
case EDisplay::kTableRowGroup:
case EDisplay::kTableHeaderGroup:
case EDisplay::kTableFooterGroup:
case EDisplay::kTableRow:
case EDisplay::kTableColumnGroup:
case EDisplay::kTableColumn:
case EDisplay::kTableCell:
case EDisplay::kTableCaption:
return EDisplay::kBlock;
case EDisplay::kNone:
NOTREACHED();
return display;
}
NOTREACHED();
return EDisplay::kBlock;
}
static bool IsOutermostSVGElement(const Element* element) {
auto* svg_element = DynamicTo<SVGElement>(element);
return svg_element && svg_element->IsOutermostSVGSVGElement();
}
static bool IsAtMediaUAShadowBoundary(const Element* element) {
if (!element)
return false;
if (ContainerNode* parent = element->parentNode()) {
if (auto* shadow_root = DynamicTo<ShadowRoot>(parent))
return shadow_root->host().IsMediaElement();
}
return false;
}
// CSS requires text-decoration to be reset at each DOM element for inline
// blocks, inline tables, floating elements, and absolute or relatively
// positioned elements. Outermost <svg> roots are considered to be atomic
// inline-level. Media elements have a special rendering where the media
// controls do not use a proper containing block model which means we need
// to manually stop text-decorations to apply to text inside media controls.
static bool StopPropagateTextDecorations(const ComputedStyle& style,
const Element* element) {
return style.Display() == EDisplay::kInlineTable ||
style.Display() == EDisplay::kInlineBlock ||
style.Display() == EDisplay::kWebkitInlineBox ||
IsAtMediaUAShadowBoundary(element) || style.IsFloating() ||
style.HasOutOfFlowPosition() || IsOutermostSVGElement(element) ||
IsA<HTMLRTElement>(element);
}
// Certain elements (<a>, <font>) override text decoration colors. "The font
// element is expected to override the color of any text decoration that spans
// the text of the element to the used value of the element's 'color' property."
// (https://html.spec.whatwg.org/C/#phrasing-content-3)
// The <a> behavior is non-standard.
static bool OverridesTextDecorationColors(const Element* element) {
return element &&
(IsA<HTMLFontElement>(element) || IsA<HTMLAnchorElement>(element));
}
// FIXME: This helper is only needed because pseudoStyleForElement passes a null
// element to adjustComputedStyle, so we can't just use element->isInTopLayer().
static bool IsInTopLayer(const Element* element, const ComputedStyle& style) {
return (element && element->IsInTopLayer()) ||
style.StyleType() == kPseudoIdBackdrop;
}
static bool LayoutParentStyleForcesZIndexToCreateStackingContext(
const ComputedStyle& layout_parent_style) {
return layout_parent_style.IsDisplayFlexibleOrGridBox();
}
void StyleAdjuster::AdjustStyleForEditing(ComputedStyle& style) {
if (style.UserModify() != EUserModify::kReadWritePlaintextOnly)
return;
// Collapsing whitespace is harmful in plain-text editing.
if (style.WhiteSpace() == EWhiteSpace::kNormal)
style.SetWhiteSpace(EWhiteSpace::kPreWrap);
else if (style.WhiteSpace() == EWhiteSpace::kNowrap)
style.SetWhiteSpace(EWhiteSpace::kPre);
else if (style.WhiteSpace() == EWhiteSpace::kPreLine)
style.SetWhiteSpace(EWhiteSpace::kPreWrap);
}
static void AdjustStyleForFirstLetter(ComputedStyle& style) {
if (style.StyleType() != kPseudoIdFirstLetter)
return;
// Force inline display (except for floating first-letters).
style.SetDisplay(style.IsFloating() ? EDisplay::kBlock : EDisplay::kInline);
// CSS2 says first-letter can't be positioned.
style.SetPosition(EPosition::kStatic);
}
static void AdjustStyleForFirstLine(ComputedStyle& style) {
if (style.StyleType() != kPseudoIdFirstLine)
return;
// Force inline display.
style.SetDisplay(EDisplay::kInline);
}
static void AdjustStyleForMarker(ComputedStyle& style,
const ComputedStyle& parent_style,
const Element& parent_element) {
if (style.StyleType() != kPseudoIdMarker)
return;
bool is_inside =
parent_style.ListStylePosition() == EListStylePosition::kInside ||
(IsA<HTMLLIElement>(parent_element) &&
!parent_style.IsInsideListElement());
if (is_inside) {
Document& document = parent_element.GetDocument();
auto margins =
ListMarker::InlineMarginsForInside(document, style, parent_style);
style.SetMarginStart(Length::Fixed(margins.first));
style.SetMarginEnd(Length::Fixed(margins.second));
} else {
// Outside list markers should generate a block container.
style.SetDisplay(EDisplay::kInlineBlock);
// Do not break inside the marker, and honor the trailing spaces.
style.SetWhiteSpace(EWhiteSpace::kPre);
// Compute margins for 'outside' during layout, because it requires the
// layout size of the marker.
// TODO(kojii): absolute position looks more reasonable, and maybe required
// in some cases, but this is currently blocked by crbug.com/734554
// style.SetPosition(EPosition::kAbsolute);
}
}
static void AdjustStyleForHTMLElement(ComputedStyle& style,
HTMLElement& element) {
// <div> and <span> are the most common elements on the web, we skip all the
// work for them.
if (IsA<HTMLDivElement>(element) || IsA<HTMLSpanElement>(element))
return;
if (IsA<HTMLTableCellElement>(element)) {
if (style.WhiteSpace() == EWhiteSpace::kWebkitNowrap) {
// Figure out if we are really nowrapping or if we should just
// use normal instead. If the width of the cell is fixed, then
// we don't actually use NOWRAP.
if (style.Width().IsFixed())
style.SetWhiteSpace(EWhiteSpace::kNormal);
else
style.SetWhiteSpace(EWhiteSpace::kNowrap);
}
return;
}
if (auto* image = DynamicTo<HTMLImageElement>(element)) {
if (image->IsCollapsed() || style.Display() == EDisplay::kContents)
style.SetDisplay(EDisplay::kNone);
return;
}
if (IsA<HTMLTableElement>(element)) {
// Tables never support the -webkit-* values for text-align and will reset
// back to the default.
if (style.GetTextAlign() == ETextAlign::kWebkitLeft ||
style.GetTextAlign() == ETextAlign::kWebkitCenter ||
style.GetTextAlign() == ETextAlign::kWebkitRight)
style.SetTextAlign(ETextAlign::kStart);
return;
}
if (IsA<HTMLFrameElement>(element) || IsA<HTMLFrameSetElement>(element)) {
// Frames and framesets never honor position:relative or position:absolute.
// This is necessary to fix a crash where a site tries to position these
// objects. They also never honor display nor floating.
style.SetPosition(EPosition::kStatic);
style.SetDisplay(EDisplay::kBlock);
style.SetFloating(EFloat::kNone);
return;
}
if (IsA<HTMLFrameElementBase>(element)) {
if (style.Display() == EDisplay::kContents) {
style.SetDisplay(EDisplay::kNone);
return;
}
// Frames cannot overflow (they are always the size we ask them to be).
// Some compositing code paths may try to draw scrollbars anyhow.
style.SetOverflowX(EOverflow::kVisible);
style.SetOverflowY(EOverflow::kVisible);
return;
}
if (IsA<HTMLRTElement>(element)) {
// Ruby text does not support float or position. This might change with
// evolution of the specification.
style.SetPosition(EPosition::kStatic);
style.SetFloating(EFloat::kNone);
return;
}
if (IsA<HTMLLegendElement>(element) &&
style.Display() != EDisplay::kContents) {
// Allow any blockified display value for legends. Note that according to
// the spec, this shouldn't affect computed style (like we do here).
// Instead, the display override should be determined during box creation,
// and even then only be applied to the rendered legend inside a
// fieldset. However, Blink determines the rendered legend during layout
// instead of during layout object creation, and also generally makes
// assumptions that the computed display value is the one to use.
style.SetDisplay(EquivalentBlockDisplay(style.Display()));
return;
}
if (IsA<HTMLMarqueeElement>(element)) {
// For now, <marquee> requires an overflow clip to work properly.
style.SetOverflowX(EOverflow::kHidden);
style.SetOverflowY(EOverflow::kHidden);
return;
}
if (IsA<HTMLTextAreaElement>(element)) {
// Textarea considers overflow visible as auto.
style.SetOverflowX(style.OverflowX() == EOverflow::kVisible
? EOverflow::kAuto
: style.OverflowX());
style.SetOverflowY(style.OverflowY() == EOverflow::kVisible
? EOverflow::kAuto
: style.OverflowY());
if (style.Display() == EDisplay::kContents)
style.SetDisplay(EDisplay::kNone);
return;
}
if (auto* html_plugin_element = DynamicTo<HTMLPlugInElement>(element)) {
style.SetRequiresAcceleratedCompositingForExternalReasons(
html_plugin_element->ShouldAccelerate());
if (style.Display() == EDisplay::kContents)
style.SetDisplay(EDisplay::kNone);
return;
}
if (IsA<HTMLUListElement>(element) || IsA<HTMLOListElement>(element)) {
style.SetIsInsideListElement();
return;
}
if (IsA<HTMLSummaryElement>(element) &&
!RuntimeEnabledFeatures::SummaryListItemEnabled()) {
// <summary> should be a list item by default, but currently it's a block
// and the disclosure symbol is not a ::marker (bug 590014). If an author
// specifies 'display: list-item', the <summary> would seem to have two
// markers (the real one and the disclosure symbol). To avoid this, compute
// to 'display: block'. This adjustment should go away with bug 590014.
if (style.Display() == EDisplay::kListItem)
style.SetDisplay(EDisplay::kBlock);
return;
}
if (style.Display() == EDisplay::kContents) {
// See https://drafts.csswg.org/css-display/#unbox-html
// Some of these elements are handled with other adjustments above.
if (IsA<HTMLBRElement>(element) || IsA<HTMLWBRElement>(element) ||
IsA<HTMLMeterElement>(element) || IsA<HTMLProgressElement>(element) ||
IsA<HTMLCanvasElement>(element) || IsA<HTMLMediaElement>(element) ||
IsA<HTMLInputElement>(element) || IsA<HTMLTextAreaElement>(element) ||
IsA<HTMLSelectElement>(element)) {
style.SetDisplay(EDisplay::kNone);
}
}
}
void StyleAdjuster::AdjustOverflow(ComputedStyle& style, Element* element) {
DCHECK(style.OverflowX() != EOverflow::kVisible ||
style.OverflowY() != EOverflow::kVisible);
if (style.IsDisplayTableBox()) {
// Tables only support overflow:hidden and overflow:visible and ignore
// anything else, see https://drafts.csswg.org/css2/visufx.html#overflow. As
// a table is not a block container box the rules for resolving conflicting
// x and y values in CSS Overflow Module Level 3 do not apply. Arguably
// overflow-x and overflow-y aren't allowed on tables but all UAs allow it.
if (style.OverflowX() != EOverflow::kHidden)
style.SetOverflowX(EOverflow::kVisible);
if (style.OverflowY() != EOverflow::kHidden)
style.SetOverflowY(EOverflow::kVisible);
// If we are left with conflicting overflow values for the x and y axes on a
// table then resolve both to OverflowVisible. This is interoperable
// behaviour but is not specced anywhere.
// TODO(https://crbug.com/966283): figure out how 'clip' should be handled.
if (style.OverflowX() == EOverflow::kVisible)
style.SetOverflowY(EOverflow::kVisible);
else if (style.OverflowY() == EOverflow::kVisible)
style.SetOverflowX(EOverflow::kVisible);
} else if (!IsOverflowClipOrVisible(style.OverflowY())) {
// Values of 'clip' and 'visible' can only be used with 'clip' and
// 'visible.' If they aren't, 'clip' and 'visible' is reset.
if (style.OverflowX() == EOverflow::kVisible)
style.SetOverflowX(EOverflow::kAuto);
else if (style.OverflowX() == EOverflow::kClip)
style.SetOverflowX(EOverflow::kHidden);
} else if (!IsOverflowClipOrVisible(style.OverflowX())) {
// Values of 'clip' and 'visible' can only be used with 'clip' and
// 'visible.' If they aren't, 'clip' and 'visible' is reset.
if (style.OverflowY() == EOverflow::kVisible)
style.SetOverflowY(EOverflow::kAuto);
else if (style.OverflowY() == EOverflow::kClip)
style.SetOverflowY(EOverflow::kHidden);
}
if (element && (style.OverflowX() == EOverflow::kClip ||
style.OverflowY() == EOverflow::kClip)) {
UseCounter::Count(element->GetDocument(),
WebFeature::kOverflowClipAlongEitherAxis);
}
}
static void AdjustStyleForDisplay(ComputedStyle& style,
const ComputedStyle& layout_parent_style,
const Element* element,
Document* document) {
// Blockify the children of flex, grid or LayoutCustom containers.
if (layout_parent_style.BlockifiesChildren() && !HostIsInputFile(element)) {
style.SetIsInBlockifyingDisplay();
if (style.Display() != EDisplay::kContents) {
style.SetDisplay(EquivalentBlockDisplay(style.Display()));
if (!style.HasOutOfFlowPosition())
style.SetIsFlexOrGridOrCustomItem();
}
if (layout_parent_style.IsDisplayFlexibleOrGridBox())
style.SetIsFlexOrGridItem();
}
if (style.Display() == EDisplay::kBlock && !style.IsFloating())
return;
if (style.Display() == EDisplay::kContents)
return;
// FIXME: Don't support this mutation for pseudo styles like first-letter or
// first-line, since it's not completely clear how that should work.
if (style.Display() == EDisplay::kInline &&
style.StyleType() == kPseudoIdNone &&
style.GetWritingMode() != layout_parent_style.GetWritingMode())
style.SetDisplay(EDisplay::kInlineBlock);
// Cannot support position: sticky for table columns and column groups because
// current code is only doing background painting through columns / column
// groups.
if ((style.Display() == EDisplay::kTableColumnGroup ||
style.Display() == EDisplay::kTableColumn) &&
style.GetPosition() == EPosition::kSticky)
style.SetPosition(EPosition::kStatic);
// writing-mode does not apply to table row groups, table column groups, table
// rows, and table columns.
// TODO(crbug.com/736072): Borders specified with logical css properties will
// not change to reflect new writing mode. ex: border-block-start.
if (style.Display() == EDisplay::kTableColumn ||
style.Display() == EDisplay::kTableColumnGroup ||
style.Display() == EDisplay::kTableFooterGroup ||
style.Display() == EDisplay::kTableHeaderGroup ||
style.Display() == EDisplay::kTableRow ||
style.Display() == EDisplay::kTableRowGroup) {
style.SetWritingMode(layout_parent_style.GetWritingMode());
style.UpdateFontOrientation();
}
// FIXME: Since we don't support block-flow on flexible boxes yet, disallow
// setting of block-flow to anything other than TopToBottomWritingMode.
// https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support.
if (style.GetWritingMode() != WritingMode::kHorizontalTb &&
style.IsDeprecatedWebkitBox()) {
style.SetWritingMode(WritingMode::kHorizontalTb);
style.UpdateFontOrientation();
}
// Disable editing custom layout elements, until EditingNG is ready.
if (!RuntimeEnabledFeatures::EditingNGEnabled() &&
(style.Display() == EDisplay::kLayoutCustom ||
style.Display() == EDisplay::kInlineLayoutCustom))
style.SetUserModify(EUserModify::kReadOnly);
if (layout_parent_style.IsDisplayFlexibleOrGridBox()) {
// We want to count vertical percentage paddings/margins on flex items
// because our current behavior is different from the spec and we want to
// gather compatibility data.
if (style.PaddingBefore().IsPercentOrCalc() ||
style.PaddingAfter().IsPercentOrCalc()) {
UseCounter::Count(document,
WebFeature::kFlexboxPercentagePaddingVertical);
}
if (style.MarginBefore().IsPercentOrCalc() ||
style.MarginAfter().IsPercentOrCalc()) {
UseCounter::Count(document, WebFeature::kFlexboxPercentageMarginVertical);
}
}
}
static void AdjustEffectiveTouchAction(ComputedStyle& style,
const ComputedStyle& parent_style,
Element* element,
bool is_svg_root) {
TouchAction inherited_action = parent_style.GetEffectiveTouchAction();
bool is_replaced_canvas = element && IsA<HTMLCanvasElement>(element) &&
element->GetExecutionContext() &&
element->GetExecutionContext()->CanExecuteScripts(
kNotAboutToExecuteScript);
bool is_non_replaced_inline_elements =
style.IsDisplayInlineType() &&
!(style.IsDisplayReplacedType() || is_svg_root ||
IsA<HTMLImageElement>(element) || is_replaced_canvas);
bool is_table_row_or_column = style.IsDisplayTableRowOrColumnType();
bool is_layout_object_needed =
element && element->LayoutObjectIsNeeded(style);
TouchAction element_touch_action = TouchAction::kAuto;
// Touch actions are only supported by elements that support both the CSS
// width and height properties.
// See https://www.w3.org/TR/pointerevents/#the-touch-action-css-property.
if (!is_non_replaced_inline_elements && !is_table_row_or_column &&
is_layout_object_needed) {
element_touch_action = style.GetTouchAction();
// kInternalPanXScrolls is only for internal usage, GetTouchAction()
// doesn't contain this bit. We set this bit when kPanX is set so it can be
// cleared for eligible editable areas later on.
if ((element_touch_action & TouchAction::kPanX) != TouchAction::kNone) {
element_touch_action |= TouchAction::kInternalPanXScrolls;
}
}
if (!element) {
style.SetEffectiveTouchAction(element_touch_action & inherited_action);
return;
}
bool is_child_document = element == element->GetDocument().documentElement();
// Apply touch action inherited from parent frame.
if (is_child_document && element->GetDocument().GetFrame()) {
inherited_action &=
TouchAction::kPan | TouchAction::kInternalPanXScrolls |
element->GetDocument().GetFrame()->InheritedEffectiveTouchAction();
}
// The effective touch action is the intersection of the touch-action values
// of the current element and all of its ancestors up to the one that
// implements the gesture. Since panning is implemented by the scroller it is
// re-enabled for scrolling elements.
// The panning-restricted cancellation should also apply to iframes, so we
// allow (panning & local touch action) on the first descendant element of a
// iframe element.
inherited_action =
AdjustTouchActionForElement(inherited_action, style, element);
TouchAction enforced_by_policy = TouchAction::kNone;
if (element->GetDocument().IsVerticalScrollEnforced())
enforced_by_policy = TouchAction::kPanY;
if (::features::IsSwipeToMoveCursorEnabled() &&
IsEditableElement(element, style)) {
element_touch_action &= ~TouchAction::kInternalPanXScrolls;
}
// Apply the adjusted parent effective touch actions.
style.SetEffectiveTouchAction((element_touch_action & inherited_action) |
enforced_by_policy);
// Propagate touch action to child frames.
if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(element)) {
Frame* content_frame = frame_owner->ContentFrame();
if (content_frame) {
content_frame->SetInheritedEffectiveTouchAction(
style.GetEffectiveTouchAction());
}
}
}
static void AdjustStateForContentVisibility(ComputedStyle& style,
Element* element) {
if (!element)
return;
auto* context = element->GetDisplayLockContext();
// The common case for most elements is that we don't have a context and have
// the default (visible) content-visibility value.
if (LIKELY(!context &&
style.ContentVisibility() == EContentVisibility::kVisible)) {
return;
}
if (!context)
context = &element->EnsureDisplayLockContext();
context->SetRequestedState(style.ContentVisibility());
context->AdjustElementStyle(&style);
}
void StyleAdjuster::AdjustForForcedColorsMode(ComputedStyle& style) {
if (!style.InForcedColorsMode() ||
style.ForcedColorAdjust() == EForcedColorAdjust::kNone)
return;
style.SetTextShadow(ComputedStyleInitialValues::InitialTextShadow());
style.SetBoxShadow(ComputedStyleInitialValues::InitialBoxShadow());
if (!style.HasUrlBackgroundImage())
style.ClearBackgroundImage();
}
void StyleAdjuster::AdjustComputedStyle(StyleResolverState& state,
Element* element) {
DCHECK(state.LayoutParentStyle());
DCHECK(state.ParentStyle());
ComputedStyle& style = state.StyleRef();
const ComputedStyle& parent_style = *state.ParentStyle();
const ComputedStyle& layout_parent_style = *state.LayoutParentStyle();
auto* html_element = DynamicTo<HTMLElement>(element);
if (html_element && (style.Display() != EDisplay::kNone ||
element->LayoutObjectIsNeeded(style))) {
AdjustStyleForHTMLElement(style, *html_element);
}
if (style.Display() != EDisplay::kNone) {
bool is_document_element =
element && element->GetDocument().documentElement() == element;
// Per the spec, position 'static' and 'relative' in the top layer compute
// to 'absolute'. Root elements that are in the top layer should just
// be left alone because the fullscreen.css doesn't apply any style to
// them.
if (IsInTopLayer(element, style) && !is_document_element &&
(style.GetPosition() == EPosition::kStatic ||
style.GetPosition() == EPosition::kRelative))
style.SetPosition(EPosition::kAbsolute);
// Absolute/fixed positioned elements, floating elements and the document
// element need block-like outside display.
if (style.Display() != EDisplay::kContents &&
(style.HasOutOfFlowPosition() || style.IsFloating()))
style.SetDisplay(EquivalentBlockDisplay(style.Display()));
if (is_document_element)
style.SetDisplay(EquivalentBlockDisplay(style.Display()));
// math display values on non-MathML elements compute to flow display
// values.
if ((!element || !element->IsMathMLElement()) &&
style.IsDisplayMathBox(style.Display())) {
style.SetDisplay(style.Display() == EDisplay::kBlockMath
? EDisplay::kBlock
: EDisplay::kInline);
}
// We don't adjust the first letter style earlier because we may change the
// display setting in adjustStyeForTagName() above.
AdjustStyleForFirstLetter(style);
AdjustStyleForFirstLine(style);
AdjustStyleForMarker(style, parent_style, state.GetElement());
AdjustStyleForDisplay(style, layout_parent_style, element,
element ? &element->GetDocument() : nullptr);
// If this is a child of a LayoutNGCustom, we need the name of the parent
// layout function for invalidation purposes.
if (layout_parent_style.IsDisplayLayoutCustomBox()) {
style.SetDisplayLayoutCustomParentName(
layout_parent_style.DisplayLayoutCustomName());
}
bool is_in_main_frame = element && element->GetDocument().IsInMainFrame();
// The root element of the main frame has no backdrop, so don't allow
// it to have a backdrop filter either.
if (is_document_element && is_in_main_frame && style.HasBackdropFilter())
style.MutableBackdropFilter().clear();
} else {
AdjustStyleForFirstLetter(style);
}
if (RuntimeEnabledFeatures::CSSContentVisibilityEnabled())
AdjustStateForContentVisibility(style, element);
// Make sure our z-index value is only applied if the object is positioned.
if (style.GetPosition() == EPosition::kStatic &&
!LayoutParentStyleForcesZIndexToCreateStackingContext(
layout_parent_style)) {
style.SetIsStackingContextWithoutContainment(false);
if (!style.HasAutoZIndex())
style.SetEffectiveZIndexZero(true);
} else if (!style.HasAutoZIndex()) {
style.SetIsStackingContextWithoutContainment(true);
}
if (style.OverflowX() != EOverflow::kVisible ||
style.OverflowY() != EOverflow::kVisible)
AdjustOverflow(style, element);
// overflow-clip-margin only applies if 'overflow: clip' is set along both
// axis or 'contain: paint'.
if (!style.ContainsPaint() && !(style.OverflowX() == EOverflow::kClip &&
style.OverflowY() == EOverflow::kClip)) {
style.SetOverflowClipMargin(
ComputedStyleInitialValues::InitialOverflowClipMargin());
}
if (StopPropagateTextDecorations(style, element))
style.ClearAppliedTextDecorations();
else
style.RestoreParentTextDecorations(parent_style);
style.ApplyTextDecorations(
parent_style.VisitedDependentColor(GetCSSPropertyTextDecorationColor()),
OverridesTextDecorationColors(element));
// Cull out any useless layers and also repeat patterns into additional
// layers.
style.AdjustBackgroundLayers();
style.AdjustMaskLayers();
// A subset of CSS properties should be forced at computed value time:
// https://drafts.csswg.org/css-color-adjust-1/#forced-colors-properties.
AdjustForForcedColorsMode(style);
// Let the theme also have a crack at adjusting the style.
LayoutTheme::GetTheme().AdjustStyle(element, style);
AdjustStyleForEditing(style);
bool is_svg_root = false;
auto* svg_element = DynamicTo<SVGElement>(element);
if (svg_element) {
is_svg_root = svg_element->IsOutermostSVGSVGElement();
if (!is_svg_root) {
// Only the root <svg> element in an SVG document fragment tree honors css
// position.
style.SetPosition(ComputedStyleInitialValues::InitialPosition());
}
if (style.Display() == EDisplay::kContents &&
(is_svg_root ||
(!IsA<SVGSVGElement>(element) && !IsA<SVGGElement>(element) &&
!IsA<SVGUseElement>(element) && !IsA<SVGTSpanElement>(element)))) {
// According to the CSS Display spec[1], nested <svg> elements, <g>,
// <use>, and <tspan> elements are not rendered and their children are
// "hoisted". For other elements display:contents behaves as display:none.
//
// [1] https://drafts.csswg.org/css-display/#unbox-svg
style.SetDisplay(EDisplay::kNone);
}
// SVG text layout code expects us to be a block-level style element.
if ((IsA<SVGForeignObjectElement>(*element) ||
IsA<SVGTextElement>(*element)) &&
style.IsDisplayInlineType())
style.SetDisplay(EDisplay::kBlock);
// Columns don't apply to svg text elements.
if (IsA<SVGTextElement>(*element))
style.ClearMultiCol();
} else if (element && element->IsMathMLElement()) {
if (style.Display() == EDisplay::kContents) {
// https://drafts.csswg.org/css-display/#unbox-mathml
style.SetDisplay(EDisplay::kNone);
}
if (style.GetWritingMode() != WritingMode::kHorizontalTb) {
// TODO(rbuis): this will not work with logical CSS properties.
// Disable vertical writing-mode for now.
style.SetWritingMode(WritingMode::kHorizontalTb);
style.UpdateFontOrientation();
}
}
// If this node is sticky it marks the creation of a sticky subtree, which we
// must track to properly handle document lifecycle in some cases.
//
// It is possible that this node is already in a sticky subtree (i.e. we have
// nested sticky nodes) - in that case the bit will already be set via
// inheritance from the ancestor and there is no harm to setting it again.
if (style.GetPosition() == EPosition::kSticky)
style.SetSubtreeIsSticky(true);
// If the inherited value of justify-items includes the 'legacy'
// keyword (plus 'left', 'right' or 'center'), 'legacy' computes to
// the the inherited value. Otherwise, 'auto' computes to 'normal'.
if (parent_style.JustifyItemsPositionType() == ItemPositionType::kLegacy &&
style.JustifyItemsPosition() == ItemPosition::kLegacy) {
style.SetJustifyItems(parent_style.JustifyItems());
}
AdjustEffectiveTouchAction(style, parent_style, element, is_svg_root);
bool is_media_control =
element && element->ShadowPseudoId().StartsWith("-webkit-media-controls");
if (is_media_control && !style.HasEffectiveAppearance()) {
// For compatibility reasons if the element is a media control and the
// -webkit-appearance is none then we should clear the background image.
style.MutableBackgroundInternal().ClearImage();
}
if (element && style.TextOverflow() == ETextOverflow::kEllipsis) {
const AtomicString& pseudo_id = element->ShadowPseudoId();
if (pseudo_id == shadow_element_names::kPseudoInputPlaceholder ||
pseudo_id == shadow_element_names::kPseudoInternalInputSuggested) {
TextControlElement* text_control =
ToTextControl(element->OwnerShadowHost());
DCHECK(text_control);
// TODO(futhark@chromium.org): We force clipping text overflow for focused
// input elements since we don't want to render ellipsis during editing.
// We should do this as a general solution which also includes
// contenteditable elements being edited. The computed style should not
// change, but LayoutBlockFlow::ShouldTruncateOverflowingText() should
// instead return false when text is being edited inside that block.
// https://crbug.com/814954
style.SetTextOverflow(text_control->ValueForTextOverflow());
}
}
if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled()) {
// When establishing a block fragmentation context for LayoutNG, we require
// that everything fragmentable inside can be laid out by NG natively, since
// NG and legacy layout cannot cooperate within the same fragmentation
// context. Set a flag, so that we can quickly determine whether we need to
// check that an element is compatible with the NG block fragmentation
// machinery.
if (style.SpecifiesColumns() ||
(element && element->GetDocument().Printing()))
style.SetInsideNGFragmentationContext(true);
}
}
} // namespace blink