| /* |
| * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights |
| * reserved. |
| * Copyright (C) 2011 Adobe Systems Incorporated. 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 "core/style/ComputedStyle.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include "core/animation/css/CSSAnimationData.h" |
| #include "core/animation/css/CSSTransitionData.h" |
| #include "core/css/CSSPaintValue.h" |
| #include "core/css/CSSPrimitiveValue.h" |
| #include "core/css/CSSPropertyEquality.h" |
| #include "core/css/resolver/StyleResolver.h" |
| #include "core/layout/LayoutTheme.h" |
| #include "core/layout/TextAutosizer.h" |
| #include "core/style/AppliedTextDecoration.h" |
| #include "core/style/BorderEdge.h" |
| #include "core/style/ComputedStyleConstants.h" |
| #include "core/style/ContentData.h" |
| #include "core/style/CursorData.h" |
| #include "core/style/DataEquivalency.h" |
| #include "core/style/QuotesData.h" |
| #include "core/style/ShadowList.h" |
| #include "core/style/StyleImage.h" |
| #include "core/style/StyleInheritedData.h" |
| #include "core/style/StyleInheritedVariables.h" |
| #include "core/style/StyleNonInheritedVariables.h" |
| #include "platform/LengthFunctions.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/fonts/Font.h" |
| #include "platform/fonts/FontSelector.h" |
| #include "platform/geometry/FloatRoundedRect.h" |
| #include "platform/graphics/GraphicsContext.h" |
| #include "platform/transforms/RotateTransformOperation.h" |
| #include "platform/transforms/ScaleTransformOperation.h" |
| #include "platform/transforms/TranslateTransformOperation.h" |
| #include "platform/wtf/MathExtras.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "platform/wtf/SaturatedArithmetic.h" |
| #include "platform/wtf/SizeAssertions.h" |
| |
| namespace blink { |
| |
| struct SameSizeAsBorderValue { |
| RGBA32 color_; |
| unsigned bitfield_; |
| }; |
| |
| ASSERT_SIZE(BorderValue, SameSizeAsBorderValue); |
| |
| // Since different compilers/architectures pack ComputedStyle differently, |
| // re-create the same structure for an accurate size comparison. |
| struct SameSizeAsComputedStyle : public RefCounted<SameSizeAsComputedStyle> { |
| struct ComputedStyleBase { |
| void* data_refs[3]; |
| unsigned bitfields_[4]; |
| } base_; |
| |
| void* data_refs[4]; |
| void* own_ptrs[1]; |
| void* data_ref_svg_style; |
| }; |
| |
| // If this assert fails, it means that size of ComputedStyle has changed. Please |
| // check that you really *do* what to increase the size of ComputedStyle, then |
| // update the SameSizeAsComputedStyle struct to match the updated storage of |
| // ComputedStyle. |
| ASSERT_SIZE(ComputedStyle, SameSizeAsComputedStyle); |
| |
| PassRefPtr<ComputedStyle> ComputedStyle::Create() { |
| return AdoptRef(new ComputedStyle(InitialStyle())); |
| } |
| |
| PassRefPtr<ComputedStyle> ComputedStyle::CreateInitialStyle() { |
| return AdoptRef(new ComputedStyle()); |
| } |
| |
| void ComputedStyle::InvalidateInitialStyle() { |
| MutableInitialStyle().SetTapHighlightColor(InitialTapHighlightColor()); |
| } |
| |
| PassRefPtr<ComputedStyle> ComputedStyle::CreateAnonymousStyleWithDisplay( |
| const ComputedStyle& parent_style, |
| EDisplay display) { |
| RefPtr<ComputedStyle> new_style = ComputedStyle::Create(); |
| new_style->InheritFrom(parent_style); |
| new_style->SetUnicodeBidi(parent_style.GetUnicodeBidi()); |
| new_style->SetDisplay(display); |
| return new_style; |
| } |
| |
| PassRefPtr<ComputedStyle> ComputedStyle::Clone(const ComputedStyle& other) { |
| return AdoptRef(new ComputedStyle(other)); |
| } |
| |
| ALWAYS_INLINE ComputedStyle::ComputedStyle() |
| : ComputedStyleBase(), RefCounted<ComputedStyle>() { |
| box_data_.Init(); |
| rare_non_inherited_data_.Init(); |
| rare_non_inherited_data_.Access()->deprecated_flexible_box_.Init(); |
| rare_non_inherited_data_.Access()->flexible_box_.Init(); |
| rare_non_inherited_data_.Access()->multi_col_.Init(); |
| rare_non_inherited_data_.Access()->transform_.Init(); |
| rare_non_inherited_data_.Access()->will_change_.Init(); |
| rare_non_inherited_data_.Access()->filter_.Init(); |
| rare_non_inherited_data_.Access()->backdrop_filter_.Init(); |
| rare_non_inherited_data_.Access()->grid_.Init(); |
| rare_non_inherited_data_.Access()->grid_item_.Init(); |
| rare_non_inherited_data_.Access()->scroll_snap_.Init(); |
| rare_inherited_data_.Init(); |
| inherited_data_.Init(); |
| svg_style_.Init(); |
| } |
| |
| ALWAYS_INLINE ComputedStyle::ComputedStyle(const ComputedStyle& o) |
| : ComputedStyleBase(o), |
| RefCounted<ComputedStyle>(), |
| box_data_(o.box_data_), |
| rare_non_inherited_data_(o.rare_non_inherited_data_), |
| rare_inherited_data_(o.rare_inherited_data_), |
| inherited_data_(o.inherited_data_), |
| svg_style_(o.svg_style_) {} |
| |
| static StyleRecalcChange DiffPseudoStyles(const ComputedStyle& old_style, |
| const ComputedStyle& new_style) { |
| // If the pseudoStyles have changed, ensure layoutObject triggers setStyle. |
| if (!old_style.HasAnyPublicPseudoStyles() && |
| !new_style.HasAnyPublicPseudoStyles()) |
| return kNoChange; |
| for (PseudoId pseudo_id = kFirstPublicPseudoId; |
| pseudo_id < kFirstInternalPseudoId; |
| pseudo_id = static_cast<PseudoId>(pseudo_id + 1)) { |
| if (!old_style.HasPseudoStyle(pseudo_id) && |
| !new_style.HasPseudoStyle(pseudo_id)) |
| continue; |
| const ComputedStyle* new_pseudo_style = |
| new_style.GetCachedPseudoStyle(pseudo_id); |
| if (!new_pseudo_style) |
| return kNoInherit; |
| const ComputedStyle* old_pseudo_style = |
| old_style.GetCachedPseudoStyle(pseudo_id); |
| if (old_pseudo_style && *old_pseudo_style != *new_pseudo_style) |
| return kNoInherit; |
| } |
| return kNoChange; |
| } |
| |
| StyleRecalcChange ComputedStyle::StylePropagationDiff( |
| const ComputedStyle* old_style, |
| const ComputedStyle* new_style) { |
| // If the style has changed from display none or to display none, then the |
| // layout subtree needs to be reattached |
| if ((!old_style && new_style) || (old_style && !new_style)) |
| return kReattach; |
| |
| if (!old_style && !new_style) |
| return kNoChange; |
| |
| if (old_style->Display() != new_style->Display() || |
| old_style->HasPseudoStyle(kPseudoIdFirstLetter) != |
| new_style->HasPseudoStyle(kPseudoIdFirstLetter) || |
| !old_style->ContentDataEquivalent(new_style) || |
| old_style->HasTextCombine() != new_style->HasTextCombine()) |
| return kReattach; |
| |
| bool independent_equal = old_style->IndependentInheritedEqual(*new_style); |
| bool non_independent_equal = |
| old_style->NonIndependentInheritedEqual(*new_style); |
| if (!independent_equal || !non_independent_equal) { |
| if (non_independent_equal && !old_style->HasExplicitlyInheritedProperties()) |
| return kIndependentInherit; |
| return kInherit; |
| } |
| |
| if (!old_style->LoadingCustomFontsEqual(*new_style) || |
| old_style->AlignItems() != new_style->AlignItems() || |
| old_style->JustifyItems() != new_style->JustifyItems()) |
| return kInherit; |
| |
| if (*old_style == *new_style) |
| return DiffPseudoStyles(*old_style, *new_style); |
| |
| if (old_style->HasExplicitlyInheritedProperties()) |
| return kInherit; |
| |
| return kNoInherit; |
| } |
| |
| void ComputedStyle::PropagateIndependentInheritedProperties( |
| const ComputedStyle& parent_style) { |
| ComputedStyleBase::PropagateIndependentInheritedProperties(parent_style); |
| } |
| |
| StyleSelfAlignmentData ResolvedSelfAlignment( |
| const StyleSelfAlignmentData& value, |
| ItemPosition normal_value_behavior) { |
| // To avoid needing to copy the RareNonInheritedData, we repurpose the 'auto' |
| // flag to not just mean 'auto' prior to running the StyleAdjuster but also |
| // mean 'normal' after running it. |
| if (value.GetPosition() == kItemPositionNormal || |
| value.GetPosition() == kItemPositionAuto) |
| return {normal_value_behavior, kOverflowAlignmentDefault}; |
| return value; |
| } |
| |
| StyleSelfAlignmentData ComputedStyle::ResolvedAlignItems( |
| ItemPosition normal_value_behaviour) const { |
| // We will return the behaviour of 'normal' value if needed, which is specific |
| // of each layout model. |
| return ResolvedSelfAlignment(AlignItems(), normal_value_behaviour); |
| } |
| |
| StyleSelfAlignmentData ComputedStyle::ResolvedAlignSelf( |
| ItemPosition normal_value_behaviour, |
| const ComputedStyle* parent_style) const { |
| // We will return the behaviour of 'normal' value if needed, which is specific |
| // of each layout model. |
| if (!parent_style || AlignSelfPosition() != kItemPositionAuto) |
| return ResolvedSelfAlignment(AlignSelf(), normal_value_behaviour); |
| |
| // We shouldn't need to resolve any 'auto' value in post-adjusment |
| // ComputedStyle, but some layout models can generate anonymous boxes that may |
| // need 'auto' value resolution during layout. |
| // The 'auto' keyword computes to the parent's align-items computed value. |
| return parent_style->ResolvedAlignItems(normal_value_behaviour); |
| } |
| |
| StyleSelfAlignmentData ComputedStyle::ResolvedJustifyItems( |
| ItemPosition normal_value_behaviour) const { |
| // We will return the behaviour of 'normal' value if needed, which is specific |
| // of each layout model. |
| return ResolvedSelfAlignment(JustifyItems(), normal_value_behaviour); |
| } |
| |
| StyleSelfAlignmentData ComputedStyle::ResolvedJustifySelf( |
| ItemPosition normal_value_behaviour, |
| const ComputedStyle* parent_style) const { |
| // We will return the behaviour of 'normal' value if needed, which is specific |
| // of each layout model. |
| if (!parent_style || JustifySelfPosition() != kItemPositionAuto) |
| return ResolvedSelfAlignment(JustifySelf(), normal_value_behaviour); |
| |
| // We shouldn't need to resolve any 'auto' value in post-adjusment |
| // ComputedStyle, but some layout models can generate anonymous boxes that may |
| // need 'auto' value resolution during layout. |
| // The auto keyword computes to the parent's justify-items computed value. |
| return parent_style->ResolvedJustifyItems(normal_value_behaviour); |
| } |
| |
| static inline ContentPosition ResolvedContentAlignmentPosition( |
| const StyleContentAlignmentData& value, |
| const StyleContentAlignmentData& normal_value_behavior) { |
| return (value.GetPosition() == kContentPositionNormal && |
| value.Distribution() == kContentDistributionDefault) |
| ? normal_value_behavior.GetPosition() |
| : value.GetPosition(); |
| } |
| |
| static inline ContentDistributionType ResolvedContentAlignmentDistribution( |
| const StyleContentAlignmentData& value, |
| const StyleContentAlignmentData& normal_value_behavior) { |
| return (value.GetPosition() == kContentPositionNormal && |
| value.Distribution() == kContentDistributionDefault) |
| ? normal_value_behavior.Distribution() |
| : value.Distribution(); |
| } |
| |
| ContentPosition ComputedStyle::ResolvedJustifyContentPosition( |
| const StyleContentAlignmentData& normal_value_behavior) const { |
| return ResolvedContentAlignmentPosition(JustifyContent(), |
| normal_value_behavior); |
| } |
| |
| ContentDistributionType ComputedStyle::ResolvedJustifyContentDistribution( |
| const StyleContentAlignmentData& normal_value_behavior) const { |
| return ResolvedContentAlignmentDistribution(JustifyContent(), |
| normal_value_behavior); |
| } |
| |
| ContentPosition ComputedStyle::ResolvedAlignContentPosition( |
| const StyleContentAlignmentData& normal_value_behavior) const { |
| return ResolvedContentAlignmentPosition(AlignContent(), |
| normal_value_behavior); |
| } |
| |
| ContentDistributionType ComputedStyle::ResolvedAlignContentDistribution( |
| const StyleContentAlignmentData& normal_value_behavior) const { |
| return ResolvedContentAlignmentDistribution(AlignContent(), |
| normal_value_behavior); |
| } |
| |
| void ComputedStyle::InheritFrom(const ComputedStyle& inherit_parent, |
| IsAtShadowBoundary is_at_shadow_boundary) { |
| EUserModify current_user_modify = UserModify(); |
| |
| ComputedStyleBase::InheritFrom(inherit_parent, is_at_shadow_boundary); |
| rare_inherited_data_ = inherit_parent.rare_inherited_data_; |
| inherited_data_ = inherit_parent.inherited_data_; |
| if (svg_style_ != inherit_parent.svg_style_) |
| svg_style_.Access()->InheritFrom(inherit_parent.svg_style_.Get()); |
| |
| if (is_at_shadow_boundary == kAtShadowBoundary) { |
| // Even if surrounding content is user-editable, shadow DOM should act as a |
| // single unit, and not necessarily be editable |
| SetUserModify(current_user_modify); |
| } |
| } |
| |
| void ComputedStyle::CopyNonInheritedFromCached(const ComputedStyle& other) { |
| ComputedStyleBase::CopyNonInheritedFromCached(other); |
| box_data_ = other.box_data_; |
| rare_non_inherited_data_ = other.rare_non_inherited_data_; |
| |
| // The flags are copied one-by-one because they contain |
| // bunch of stuff other than real style data. |
| // See comments for each skipped flag below. |
| |
| // These are not generated in ComputedStyleBase |
| SetHasViewportUnits(other.HasViewportUnits()); |
| SetHasRemUnitsInternal(other.HasRemUnits()); |
| |
| // Correctly set during selector matching: |
| // m_styleType |
| // m_pseudoBits |
| |
| // Set correctly while computing style for children: |
| // m_explicitInheritance |
| |
| // unique() styles are not cacheable. |
| DCHECK(!other.Unique()); |
| |
| // styles with non inherited properties that reference variables are not |
| // cacheable. |
| DCHECK(!other.HasVariableReferenceFromNonInheritedProperty()); |
| |
| // The following flags are set during matching before we decide that we get a |
| // match in the MatchedPropertiesCache which in turn calls this method. The |
| // reason why we don't copy these flags is that they're already correctly set |
| // and that they may differ between elements which have the same set of |
| // matched properties. For instance, given the rule: |
| // |
| // :-webkit-any(:hover, :focus) { background-color: green }" |
| // |
| // A hovered element, and a focused element may use the same cached matched |
| // properties here, but the affectedBy flags will be set differently based on |
| // the matching order of the :-webkit-any components. |
| // |
| // m_emptyState |
| // m_affectedByFocus |
| // m_affectedByHover |
| // m_affectedByActive |
| // m_affectedByDrag |
| // m_isLink |
| |
| if (svg_style_ != other.svg_style_) |
| svg_style_.Access()->CopyNonInheritedFromCached(other.svg_style_.Get()); |
| DCHECK_EQ(Zoom(), InitialZoom()); |
| } |
| |
| bool ComputedStyle::operator==(const ComputedStyle& o) const { |
| return InheritedEqual(o) && NonInheritedEqual(o); |
| } |
| |
| bool ComputedStyle::IsStyleAvailable() const { |
| return this != StyleResolver::StyleNotYetAvailable(); |
| } |
| |
| bool ComputedStyle::HasUniquePseudoStyle() const { |
| if (!cached_pseudo_styles_ || StyleType() != kPseudoIdNone) |
| return false; |
| |
| for (size_t i = 0; i < cached_pseudo_styles_->size(); ++i) { |
| const ComputedStyle& pseudo_style = *cached_pseudo_styles_->at(i); |
| if (pseudo_style.Unique()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| ComputedStyle* ComputedStyle::GetCachedPseudoStyle(PseudoId pid) const { |
| if (!cached_pseudo_styles_ || !cached_pseudo_styles_->size()) |
| return 0; |
| |
| if (StyleType() != kPseudoIdNone) |
| return 0; |
| |
| for (size_t i = 0; i < cached_pseudo_styles_->size(); ++i) { |
| ComputedStyle* pseudo_style = cached_pseudo_styles_->at(i).Get(); |
| if (pseudo_style->StyleType() == pid) |
| return pseudo_style; |
| } |
| |
| return 0; |
| } |
| |
| ComputedStyle* ComputedStyle::AddCachedPseudoStyle( |
| PassRefPtr<ComputedStyle> pseudo) { |
| if (!pseudo) |
| return 0; |
| |
| DCHECK_GT(pseudo->StyleType(), kPseudoIdNone); |
| |
| ComputedStyle* result = pseudo.Get(); |
| |
| if (!cached_pseudo_styles_) |
| cached_pseudo_styles_ = WTF::WrapUnique(new PseudoStyleCache); |
| |
| cached_pseudo_styles_->push_back(std::move(pseudo)); |
| |
| return result; |
| } |
| |
| void ComputedStyle::RemoveCachedPseudoStyle(PseudoId pid) { |
| if (!cached_pseudo_styles_) |
| return; |
| for (size_t i = 0; i < cached_pseudo_styles_->size(); ++i) { |
| ComputedStyle* pseudo_style = cached_pseudo_styles_->at(i).Get(); |
| if (pseudo_style->StyleType() == pid) { |
| cached_pseudo_styles_->erase(i); |
| return; |
| } |
| } |
| } |
| |
| bool ComputedStyle::InheritedEqual(const ComputedStyle& other) const { |
| return IndependentInheritedEqual(other) && |
| NonIndependentInheritedEqual(other); |
| } |
| |
| bool ComputedStyle::IndependentInheritedEqual( |
| const ComputedStyle& other) const { |
| return ComputedStyleBase::IndependentInheritedEqual(other); |
| } |
| |
| bool ComputedStyle::NonIndependentInheritedEqual( |
| const ComputedStyle& other) const { |
| return ComputedStyleBase::NonIndependentInheritedEqual(other) && |
| inherited_data_ == other.inherited_data_ && |
| svg_style_->InheritedEqual(*other.svg_style_) && |
| rare_inherited_data_ == other.rare_inherited_data_; |
| } |
| |
| bool ComputedStyle::LoadingCustomFontsEqual(const ComputedStyle& other) const { |
| return GetFont().LoadingCustomFonts() == other.GetFont().LoadingCustomFonts(); |
| } |
| |
| bool ComputedStyle::NonInheritedEqual(const ComputedStyle& other) const { |
| // compare everything except the pseudoStyle pointer |
| return ComputedStyleBase::NonInheritedEqual(other) && |
| box_data_ == other.box_data_ && |
| rare_non_inherited_data_ == other.rare_non_inherited_data_ && |
| svg_style_->NonInheritedEqual(*other.svg_style_); |
| } |
| |
| bool ComputedStyle::InheritedDataShared(const ComputedStyle& other) const { |
| // This is a fast check that only looks if the data structures are shared. |
| // TODO(sashab): Should ComputedStyleBase have an inheritedDataShared method? |
| return ComputedStyleBase::InheritedEqual(other) && |
| inherited_data_.Get() == other.inherited_data_.Get() && |
| svg_style_.Get() == other.svg_style_.Get() && |
| rare_inherited_data_.Get() == other.rare_inherited_data_.Get(); |
| } |
| |
| static bool DependenceOnContentHeightHasChanged(const ComputedStyle& a, |
| const ComputedStyle& b) { |
| // If top or bottom become auto/non-auto then it means we either have to solve |
| // height based on the content or stop doing so |
| // (http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-height) |
| // - either way requires a layout. |
| return a.LogicalTop().IsAuto() != b.LogicalTop().IsAuto() || |
| a.LogicalBottom().IsAuto() != b.LogicalBottom().IsAuto(); |
| } |
| |
| StyleDifference ComputedStyle::VisualInvalidationDiff( |
| const ComputedStyle& other) const { |
| // Note, we use .Get() on each DataRef below because DataRef::operator== will |
| // do a deep compare, which is duplicate work when we're going to compare each |
| // property inside this function anyway. |
| |
| StyleDifference diff; |
| if (svg_style_.Get() != other.svg_style_.Get()) |
| diff = svg_style_->Diff(other.svg_style_.Get()); |
| |
| if ((!diff.NeedsFullLayout() || !diff.NeedsFullPaintInvalidation()) && |
| DiffNeedsFullLayoutAndPaintInvalidation(other)) { |
| diff.SetNeedsFullLayout(); |
| diff.SetNeedsPaintInvalidationObject(); |
| } |
| |
| if (!diff.NeedsFullLayout() && DiffNeedsFullLayout(other)) |
| diff.SetNeedsFullLayout(); |
| |
| if (!diff.NeedsFullLayout() && !MarginEqual(other)) { |
| // Relative-positioned elements collapse their margins so need a full |
| // layout. |
| if (HasOutOfFlowPosition()) |
| diff.SetNeedsPositionedMovementLayout(); |
| else |
| diff.SetNeedsFullLayout(); |
| } |
| |
| if (!diff.NeedsFullLayout() && GetPosition() != EPosition::kStatic && |
| !OffsetEqual(other)) { |
| // Optimize for the case where a positioned layer is moving but not changing |
| // size. |
| if (DependenceOnContentHeightHasChanged(*this, other)) |
| diff.SetNeedsFullLayout(); |
| else |
| diff.SetNeedsPositionedMovementLayout(); |
| } |
| |
| if (DiffNeedsPaintInvalidationSubtree(other)) |
| diff.SetNeedsPaintInvalidationSubtree(); |
| else if (DiffNeedsPaintInvalidationObject(other)) |
| diff.SetNeedsPaintInvalidationObject(); |
| |
| if (DiffNeedsVisualRectUpdate(other)) |
| diff.SetNeedsVisualRectUpdate(); |
| |
| UpdatePropertySpecificDifferences(other, diff); |
| |
| // The following condition needs to be at last, because it may depend on |
| // conditions in diff computed above. |
| if (ScrollAnchorDisablingPropertyChanged(other, diff)) |
| diff.SetScrollAnchorDisablingPropertyChanged(); |
| |
| // Cursors are not checked, since they will be set appropriately in response |
| // to mouse events, so they don't need to cause any paint invalidation or |
| // layout. |
| |
| // Animations don't need to be checked either. We always set the new style on |
| // the layoutObject, so we will get a chance to fire off the resulting |
| // transition properly. |
| |
| return diff; |
| } |
| |
| bool ComputedStyle::ScrollAnchorDisablingPropertyChanged( |
| const ComputedStyle& other, |
| const StyleDifference& diff) const { |
| if (GetPosition() != other.GetPosition()) |
| return true; |
| |
| if (box_data_.Get() != other.box_data_.Get()) { |
| if (box_data_->width_ != other.box_data_->width_ || |
| box_data_->min_width_ != other.box_data_->min_width_ || |
| box_data_->max_width_ != other.box_data_->max_width_ || |
| box_data_->height_ != other.box_data_->height_ || |
| box_data_->min_height_ != other.box_data_->min_height_ || |
| box_data_->max_height_ != other.box_data_->max_height_) |
| return true; |
| } |
| |
| if (ComputedStyleBase::ScrollAnchorDisablingPropertyChanged(other, diff)) |
| return true; |
| |
| if (diff.TransformChanged()) |
| return true; |
| |
| return false; |
| } |
| |
| bool ComputedStyle::DiffNeedsFullLayoutAndPaintInvalidation( |
| const ComputedStyle& other) const { |
| // FIXME: Not all cases in this method need both full layout and paint |
| // invalidation. |
| // Should move cases into DiffNeedsFullLayout() if |
| // - don't need paint invalidation at all; |
| // - or the layoutObject knows how to exactly invalidate paints caused by the |
| // layout change instead of forced full paint invalidation. |
| |
| if (surround_data_.Get() != other.surround_data_.Get()) { |
| // If our border widths change, then we need to layout. Other changes to |
| // borders only necessitate a paint invalidation. |
| if (!(BorderWidthEquals(BorderLeftWidth(), other.BorderLeftWidth())) || |
| !(BorderWidthEquals(BorderTopWidth(), other.BorderTopWidth())) || |
| !(BorderWidthEquals(BorderBottomWidth(), other.BorderBottomWidth())) || |
| !(BorderWidthEquals(BorderRightWidth(), other.BorderRightWidth()))) |
| return true; |
| } |
| |
| if (ComputedStyleBase::DiffNeedsFullLayoutAndPaintInvalidation(other)) |
| return true; |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->appearance_ != |
| other.rare_non_inherited_data_->appearance_ || |
| rare_non_inherited_data_->margin_before_collapse != |
| other.rare_non_inherited_data_->margin_before_collapse || |
| rare_non_inherited_data_->margin_after_collapse != |
| other.rare_non_inherited_data_->margin_after_collapse || |
| rare_non_inherited_data_->line_clamp != |
| other.rare_non_inherited_data_->line_clamp || |
| rare_non_inherited_data_->text_overflow != |
| other.rare_non_inherited_data_->text_overflow || |
| rare_non_inherited_data_->shape_margin_ != |
| other.rare_non_inherited_data_->shape_margin_ || |
| rare_non_inherited_data_->order_ != |
| other.rare_non_inherited_data_->order_ || |
| rare_non_inherited_data_->HasFilters() != |
| other.rare_non_inherited_data_->HasFilters()) |
| return true; |
| |
| if (rare_non_inherited_data_->grid_.Get() != |
| other.rare_non_inherited_data_->grid_.Get() && |
| *rare_non_inherited_data_->grid_.Get() != |
| *other.rare_non_inherited_data_->grid_.Get()) |
| return true; |
| |
| if (rare_non_inherited_data_->grid_item_.Get() != |
| other.rare_non_inherited_data_->grid_item_.Get() && |
| *rare_non_inherited_data_->grid_item_.Get() != |
| *other.rare_non_inherited_data_->grid_item_.Get()) |
| return true; |
| |
| if (rare_non_inherited_data_->deprecated_flexible_box_.Get() != |
| other.rare_non_inherited_data_->deprecated_flexible_box_.Get() && |
| *rare_non_inherited_data_->deprecated_flexible_box_.Get() != |
| *other.rare_non_inherited_data_->deprecated_flexible_box_.Get()) |
| return true; |
| |
| if (rare_non_inherited_data_->flexible_box_.Get() != |
| other.rare_non_inherited_data_->flexible_box_.Get() && |
| *rare_non_inherited_data_->flexible_box_.Get() != |
| *other.rare_non_inherited_data_->flexible_box_.Get()) |
| return true; |
| |
| if (rare_non_inherited_data_->multi_col_.Get() != |
| other.rare_non_inherited_data_->multi_col_.Get() && |
| *rare_non_inherited_data_->multi_col_.Get() != |
| *other.rare_non_inherited_data_->multi_col_.Get()) |
| return true; |
| |
| // If the counter directives change, trigger a relayout to re-calculate |
| // counter values and rebuild the counter node tree. |
| const CounterDirectiveMap* map_a = |
| rare_non_inherited_data_->counter_directives_.get(); |
| const CounterDirectiveMap* map_b = |
| other.rare_non_inherited_data_->counter_directives_.get(); |
| if (!(map_a == map_b || (map_a && map_b && *map_a == *map_b))) |
| return true; |
| |
| // We only need do layout for opacity changes if adding or losing opacity |
| // could trigger a change |
| // in us being a stacking context. |
| if (IsStackingContext() != other.IsStackingContext() && |
| rare_non_inherited_data_->HasOpacity() != |
| other.rare_non_inherited_data_->HasOpacity()) { |
| // FIXME: We would like to use SimplifiedLayout here, but we can't quite |
| // do that yet. We need to make sure SimplifiedLayout can operate |
| // correctly on LayoutInlines (we will need to add a |
| // selfNeedsSimplifiedLayout bit in order to not get confused and taint |
| // every line). In addition we need to solve the floating object issue |
| // when layers come and go. Right now a full layout is necessary to keep |
| // floating object lists sane. |
| return true; |
| } |
| } |
| |
| if (rare_inherited_data_.Get() != other.rare_inherited_data_.Get()) { |
| if (rare_inherited_data_->highlight_ != |
| other.rare_inherited_data_->highlight_ || |
| rare_inherited_data_->indent_ != other.rare_inherited_data_->indent_ || |
| rare_inherited_data_->text_align_last_ != |
| other.rare_inherited_data_->text_align_last_ || |
| rare_inherited_data_->text_indent_line_ != |
| other.rare_inherited_data_->text_indent_line_ || |
| rare_inherited_data_->effective_zoom_ != |
| other.rare_inherited_data_->effective_zoom_ || |
| rare_inherited_data_->word_break_ != |
| other.rare_inherited_data_->word_break_ || |
| rare_inherited_data_->overflow_wrap_ != |
| other.rare_inherited_data_->overflow_wrap_ || |
| rare_inherited_data_->line_break_ != |
| other.rare_inherited_data_->line_break_ || |
| rare_inherited_data_->text_security_ != |
| other.rare_inherited_data_->text_security_ || |
| rare_inherited_data_->hyphens_ != |
| other.rare_inherited_data_->hyphens_ || |
| rare_inherited_data_->hyphenation_limit_before_ != |
| other.rare_inherited_data_->hyphenation_limit_before_ || |
| rare_inherited_data_->hyphenation_limit_after_ != |
| other.rare_inherited_data_->hyphenation_limit_after_ || |
| rare_inherited_data_->hyphenation_string_ != |
| other.rare_inherited_data_->hyphenation_string_ || |
| rare_inherited_data_->respect_image_orientation_ != |
| other.rare_inherited_data_->respect_image_orientation_ || |
| rare_inherited_data_->ruby_position_ != |
| other.rare_inherited_data_->ruby_position_ || |
| rare_inherited_data_->text_emphasis_mark_ != |
| other.rare_inherited_data_->text_emphasis_mark_ || |
| rare_inherited_data_->text_emphasis_position_ != |
| other.rare_inherited_data_->text_emphasis_position_ || |
| rare_inherited_data_->text_emphasis_custom_mark_ != |
| other.rare_inherited_data_->text_emphasis_custom_mark_ || |
| rare_inherited_data_->text_justify_ != |
| other.rare_inherited_data_->text_justify_ || |
| rare_inherited_data_->text_orientation_ != |
| other.rare_inherited_data_->text_orientation_ || |
| rare_inherited_data_->text_combine_ != |
| other.rare_inherited_data_->text_combine_ || |
| rare_inherited_data_->tab_size_ != |
| other.rare_inherited_data_->tab_size_ || |
| rare_inherited_data_->text_size_adjust_ != |
| other.rare_inherited_data_->text_size_adjust_ || |
| rare_inherited_data_->list_style_image_ != |
| other.rare_inherited_data_->list_style_image_ || |
| rare_inherited_data_->line_height_step_ != |
| other.rare_inherited_data_->line_height_step_ || |
| rare_inherited_data_->text_stroke_width_ != |
| other.rare_inherited_data_->text_stroke_width_) |
| return true; |
| |
| if (!rare_inherited_data_->ShadowDataEquivalent( |
| *other.rare_inherited_data_.Get())) |
| return true; |
| |
| if (!rare_inherited_data_->QuotesDataEquivalent( |
| *other.rare_inherited_data_.Get())) |
| return true; |
| } |
| |
| if (inherited_data_->text_autosizing_multiplier_ != |
| other.inherited_data_->text_autosizing_multiplier_) |
| return true; |
| |
| if (inherited_data_->font_.LoadingCustomFonts() != |
| other.inherited_data_->font_.LoadingCustomFonts()) |
| return true; |
| |
| if (inherited_data_.Get() != other.inherited_data_.Get()) { |
| if (inherited_data_->line_height_ != other.inherited_data_->line_height_ || |
| inherited_data_->font_ != other.inherited_data_->font_ || |
| inherited_data_->horizontal_border_spacing_ != |
| other.inherited_data_->horizontal_border_spacing_ || |
| inherited_data_->vertical_border_spacing_ != |
| other.inherited_data_->vertical_border_spacing_) |
| return true; |
| } |
| |
| if (BoxDirection() != other.BoxDirection() || |
| RtlOrdering() != other.RtlOrdering() || |
| GetTextAlign() != other.GetTextAlign() || |
| TextTransform() != other.TextTransform() || |
| Direction() != other.Direction() || WhiteSpace() != other.WhiteSpace() || |
| GetWritingMode() != other.GetWritingMode()) |
| return true; |
| |
| if (OverflowX() != other.OverflowX() || OverflowY() != other.OverflowY() || |
| Clear() != other.Clear() || GetUnicodeBidi() != other.GetUnicodeBidi() || |
| Floating() != other.Floating() || |
| OriginalDisplay() != other.OriginalDisplay()) |
| return true; |
| |
| if (IsDisplayTableType(Display())) { |
| if (BorderCollapse() != other.BorderCollapse() || |
| EmptyCells() != other.EmptyCells() || |
| CaptionSide() != other.CaptionSide() || |
| TableLayout() != other.TableLayout()) |
| return true; |
| |
| // In the collapsing border model, 'hidden' suppresses other borders, while |
| // 'none' does not, so these style differences can be width differences. |
| if ((BorderCollapse() == EBorderCollapse::kCollapse) && |
| ((BorderTopStyle() == kBorderStyleHidden && |
| other.BorderTopStyle() == kBorderStyleNone) || |
| (BorderTopStyle() == kBorderStyleNone && |
| other.BorderTopStyle() == kBorderStyleHidden) || |
| (BorderBottomStyle() == kBorderStyleHidden && |
| other.BorderBottomStyle() == kBorderStyleNone) || |
| (BorderBottomStyle() == kBorderStyleNone && |
| other.BorderBottomStyle() == kBorderStyleHidden) || |
| (BorderLeftStyle() == kBorderStyleHidden && |
| other.BorderLeftStyle() == kBorderStyleNone) || |
| (BorderLeftStyle() == kBorderStyleNone && |
| other.BorderLeftStyle() == kBorderStyleHidden) || |
| (BorderRightStyle() == kBorderStyleHidden && |
| other.BorderRightStyle() == kBorderStyleNone) || |
| (BorderRightStyle() == kBorderStyleNone && |
| other.BorderRightStyle() == kBorderStyleHidden))) |
| return true; |
| } else if (Display() == EDisplay::kListItem) { |
| if (ListStyleType() != other.ListStyleType() || |
| ListStylePosition() != other.ListStylePosition()) |
| return true; |
| } |
| |
| if ((Visibility() == EVisibility::kCollapse) != |
| (other.Visibility() == EVisibility::kCollapse)) |
| return true; |
| |
| if (HasPseudoStyle(kPseudoIdScrollbar) != |
| other.HasPseudoStyle(kPseudoIdScrollbar)) |
| return true; |
| |
| // Movement of non-static-positioned object is special cased in |
| // ComputedStyle::VisualInvalidationDiff(). |
| |
| return false; |
| } |
| |
| bool ComputedStyle::DiffNeedsFullLayout(const ComputedStyle& other) const { |
| if (box_data_.Get() != other.box_data_.Get()) { |
| if (box_data_->width_ != other.box_data_->width_ || |
| box_data_->min_width_ != other.box_data_->min_width_ || |
| box_data_->max_width_ != other.box_data_->max_width_ || |
| box_data_->height_ != other.box_data_->height_ || |
| box_data_->min_height_ != other.box_data_->min_height_ || |
| box_data_->max_height_ != other.box_data_->max_height_) |
| return true; |
| |
| if (box_data_->vertical_align_length_ != |
| other.box_data_->vertical_align_length_) |
| return true; |
| |
| if (box_data_->box_sizing_ != other.box_data_->box_sizing_) |
| return true; |
| } |
| |
| if (VerticalAlign() != other.VerticalAlign() || |
| GetPosition() != other.GetPosition()) |
| return true; |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->align_content_ != |
| other.rare_non_inherited_data_->align_content_ || |
| rare_non_inherited_data_->align_items_ != |
| other.rare_non_inherited_data_->align_items_ || |
| rare_non_inherited_data_->align_self_ != |
| other.rare_non_inherited_data_->align_self_ || |
| rare_non_inherited_data_->justify_content_ != |
| other.rare_non_inherited_data_->justify_content_ || |
| rare_non_inherited_data_->justify_items_ != |
| other.rare_non_inherited_data_->justify_items_ || |
| rare_non_inherited_data_->justify_self_ != |
| other.rare_non_inherited_data_->justify_self_ || |
| rare_non_inherited_data_->contain_ != |
| other.rare_non_inherited_data_->contain_) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ComputedStyle::DiffNeedsPaintInvalidationSubtree( |
| const ComputedStyle& other) const { |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->effective_blend_mode_ != |
| other.rare_non_inherited_data_->effective_blend_mode_ || |
| rare_non_inherited_data_->isolation_ != |
| other.rare_non_inherited_data_->isolation_) |
| return true; |
| |
| if (rare_non_inherited_data_->mask_ != |
| other.rare_non_inherited_data_->mask_ || |
| rare_non_inherited_data_->mask_box_image_ != |
| other.rare_non_inherited_data_->mask_box_image_) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ComputedStyle::DiffNeedsPaintInvalidationObject( |
| const ComputedStyle& other) const { |
| if (Visibility() != other.Visibility() || |
| PrintColorAdjust() != other.PrintColorAdjust() || |
| InsideLink() != other.InsideLink() || |
| !Border().VisuallyEqual(other.Border()) || !RadiiEqual(other) || |
| !BorderColorVisuallyEquals(other) || !BorderSizeEquals(other) || |
| *background_data_ != *other.background_data_) |
| return true; |
| |
| if (rare_inherited_data_.Get() != other.rare_inherited_data_.Get()) { |
| if (rare_inherited_data_->user_modify_ != |
| other.rare_inherited_data_->user_modify_ || |
| rare_inherited_data_->user_select_ != |
| other.rare_inherited_data_->user_select_ || |
| rare_inherited_data_->image_rendering_ != |
| other.rare_inherited_data_->image_rendering_) |
| return true; |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->user_drag != |
| other.rare_non_inherited_data_->user_drag || |
| rare_non_inherited_data_->object_fit_ != |
| other.rare_non_inherited_data_->object_fit_ || |
| rare_non_inherited_data_->object_position_ != |
| other.rare_non_inherited_data_->object_position_ || |
| !rare_non_inherited_data_->ShadowDataEquivalent( |
| *other.rare_non_inherited_data_.Get()) || |
| !rare_non_inherited_data_->ShapeOutsideDataEquivalent( |
| *other.rare_non_inherited_data_.Get()) || |
| !rare_non_inherited_data_->ClipPathDataEquivalent( |
| *other.rare_non_inherited_data_.Get()) || |
| !rare_non_inherited_data_->outline_.VisuallyEqual( |
| other.rare_non_inherited_data_->outline_) || |
| (VisitedLinkBorderLeftColor() != other.VisitedLinkBorderLeftColor() && |
| BorderLeftWidth()) || |
| (VisitedLinkBorderRightColor() != other.VisitedLinkBorderRightColor() && |
| BorderRightWidth()) || |
| (VisitedLinkBorderBottomColor() != |
| other.VisitedLinkBorderBottomColor() && |
| BorderBottomWidth()) || |
| (VisitedLinkBorderTopColor() != other.VisitedLinkBorderTopColor() && |
| BorderTopWidth()) || |
| (VisitedLinkOutlineColor() != other.VisitedLinkOutlineColor() && |
| OutlineWidth()) || |
| (VisitedLinkBackgroundColor() != other.VisitedLinkBackgroundColor())) |
| return true; |
| } |
| |
| if (Resize() != other.Resize()) |
| return true; |
| |
| if (rare_non_inherited_data_->paint_images_) { |
| for (const auto& image : *rare_non_inherited_data_->paint_images_) { |
| if (DiffNeedsPaintInvalidationObjectForPaintImage(image, other)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool ComputedStyle::DiffNeedsPaintInvalidationObjectForPaintImage( |
| const StyleImage* image, |
| const ComputedStyle& other) const { |
| CSSPaintValue* value = ToCSSPaintValue(image->CssValue()); |
| |
| // NOTE: If the invalidation properties vectors are null, we are invalid as |
| // we haven't yet been painted (and can't provide the invalidation |
| // properties yet). |
| if (!value->NativeInvalidationProperties() || |
| !value->CustomInvalidationProperties()) |
| return true; |
| |
| for (CSSPropertyID property_id : *value->NativeInvalidationProperties()) { |
| // TODO(ikilpatrick): remove IsInterpolableProperty check once |
| // CSSPropertyEquality::PropertiesEqual correctly handles all properties. |
| if (!CSSPropertyMetadata::IsInterpolableProperty(property_id) || |
| !CSSPropertyEquality::PropertiesEqual(property_id, *this, other)) |
| return true; |
| } |
| |
| if (InheritedVariables() || NonInheritedVariables() || |
| other.InheritedVariables() || other.NonInheritedVariables()) { |
| for (const AtomicString& property : |
| *value->CustomInvalidationProperties()) { |
| if (!DataEquivalent(GetVariable(property), other.GetVariable(property))) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // This doesn't include conditions needing layout or overflow recomputation |
| // which implies visual rect update. |
| bool ComputedStyle::DiffNeedsVisualRectUpdate( |
| const ComputedStyle& other) const { |
| // Visual rect is empty if visibility is hidden. |
| if (Visibility() != other.Visibility()) |
| return true; |
| |
| // Need to update visual rect of the resizer. |
| if (Resize() != other.Resize()) |
| return true; |
| |
| return false; |
| } |
| |
| void ComputedStyle::UpdatePropertySpecificDifferences( |
| const ComputedStyle& other, |
| StyleDifference& diff) const { |
| if (box_data_->z_index_ != other.box_data_->z_index_ || |
| IsStackingContext() != other.IsStackingContext()) |
| diff.SetZIndexChanged(); |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| // It's possible for the old and new style transform data to be equivalent |
| // while hasTransform() differs, as it checks a number of conditions aside |
| // from just the matrix, including but not limited to animation state. |
| if (HasTransform() != other.HasTransform() || |
| !TransformDataEquivalent(other) || |
| rare_non_inherited_data_->perspective_ != |
| other.rare_non_inherited_data_->perspective_ || |
| rare_non_inherited_data_->perspective_origin_ != |
| other.rare_non_inherited_data_->perspective_origin_) |
| diff.SetTransformChanged(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->opacity != |
| other.rare_non_inherited_data_->opacity) |
| diff.SetOpacityChanged(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->filter_ != |
| other.rare_non_inherited_data_->filter_) |
| diff.SetFilterChanged(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (!rare_non_inherited_data_->ShadowDataEquivalent( |
| *other.rare_non_inherited_data_.Get())) |
| diff.SetNeedsRecomputeOverflow(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (rare_non_inherited_data_->backdrop_filter_ != |
| other.rare_non_inherited_data_->backdrop_filter_) |
| diff.SetBackdropFilterChanged(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (!rare_non_inherited_data_->ReflectionDataEquivalent( |
| *other.rare_non_inherited_data_.Get())) |
| diff.SetFilterChanged(); |
| } |
| |
| if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { |
| if (!rare_non_inherited_data_->outline_.VisuallyEqual( |
| other.rare_non_inherited_data_->outline_)) |
| diff.SetNeedsRecomputeOverflow(); |
| } |
| |
| if (!Border().VisualOverflowEqual(other.Border())) |
| diff.SetNeedsRecomputeOverflow(); |
| |
| if (!diff.NeedsFullPaintInvalidation()) { |
| if (inherited_data_->color_ != other.inherited_data_->color_ || |
| inherited_data_->visited_link_color_ != |
| other.inherited_data_->visited_link_color_ || |
| HasSimpleUnderlineInternal() != other.HasSimpleUnderlineInternal() || |
| visual_data_->text_decoration_ != |
| other.visual_data_->text_decoration_) { |
| diff.SetTextDecorationOrColorChanged(); |
| } else { |
| if (rare_non_inherited_data_.Get() != |
| other.rare_non_inherited_data_.Get() && |
| (rare_non_inherited_data_->text_decoration_style_ != |
| other.rare_non_inherited_data_->text_decoration_style_ || |
| rare_non_inherited_data_->text_decoration_color_ != |
| other.rare_non_inherited_data_->text_decoration_color_ || |
| rare_non_inherited_data_->visited_link_text_decoration_color_ != |
| other.rare_non_inherited_data_ |
| ->visited_link_text_decoration_color_)) { |
| diff.SetTextDecorationOrColorChanged(); |
| } else { |
| if (rare_inherited_data_.Get() != other.rare_inherited_data_.Get() && |
| (rare_inherited_data_->TextFillColor() != |
| other.rare_inherited_data_->TextFillColor() || |
| rare_inherited_data_->TextStrokeColor() != |
| other.rare_inherited_data_->TextStrokeColor() || |
| rare_inherited_data_->TextEmphasisColor() != |
| other.rare_inherited_data_->TextEmphasisColor() || |
| rare_inherited_data_->VisitedLinkTextFillColor() != |
| other.rare_inherited_data_->VisitedLinkTextFillColor() || |
| rare_inherited_data_->VisitedLinkTextStrokeColor() != |
| other.rare_inherited_data_->VisitedLinkTextStrokeColor() || |
| rare_inherited_data_->VisitedLinkTextEmphasisColor() != |
| other.rare_inherited_data_->VisitedLinkTextEmphasisColor() || |
| rare_inherited_data_->text_emphasis_fill_ != |
| other.rare_inherited_data_->text_emphasis_fill_ || |
| rare_inherited_data_->text_underline_position_ != |
| other.rare_inherited_data_->text_underline_position_ || |
| rare_inherited_data_->text_decoration_skip_ != |
| other.rare_inherited_data_->text_decoration_skip_ || |
| rare_inherited_data_->applied_text_decorations_ != |
| other.rare_inherited_data_->applied_text_decorations_ || |
| rare_inherited_data_->CaretColor() != |
| other.rare_inherited_data_->CaretColor() || |
| rare_inherited_data_->VisitedLinkCaretColor() != |
| other.rare_inherited_data_->VisitedLinkCaretColor())) { |
| diff.SetTextDecorationOrColorChanged(); |
| } |
| } |
| } |
| } |
| |
| bool has_clip = HasOutOfFlowPosition() && !visual_data_->has_auto_clip_; |
| bool other_has_clip = |
| other.HasOutOfFlowPosition() && !other.visual_data_->has_auto_clip_; |
| if (has_clip != other_has_clip || |
| (has_clip && visual_data_->clip_ != other.visual_data_->clip_)) |
| diff.SetCSSClipChanged(); |
| } |
| |
| void ComputedStyle::AddPaintImage(StyleImage* image) { |
| if (!rare_non_inherited_data_.Access()->paint_images_) { |
| rare_non_inherited_data_.Access()->paint_images_ = |
| WTF::MakeUnique<Vector<Persistent<StyleImage>>>(); |
| } |
| rare_non_inherited_data_.Access()->paint_images_->push_back(image); |
| } |
| |
| void ComputedStyle::AddCursor(StyleImage* image, |
| bool hot_spot_specified, |
| const IntPoint& hot_spot) { |
| if (!rare_inherited_data_.Access()->cursor_data_) |
| rare_inherited_data_.Access()->cursor_data_ = new CursorList; |
| rare_inherited_data_.Access()->cursor_data_->push_back( |
| CursorData(image, hot_spot_specified, hot_spot)); |
| } |
| |
| void ComputedStyle::SetCursorList(CursorList* other) { |
| rare_inherited_data_.Access()->cursor_data_ = other; |
| } |
| |
| void ComputedStyle::SetQuotes(PassRefPtr<QuotesData> q) { |
| rare_inherited_data_.Access()->quotes_ = std::move(q); |
| } |
| |
| void ComputedStyle::ClearCursorList() { |
| if (rare_inherited_data_->cursor_data_) |
| rare_inherited_data_.Access()->cursor_data_ = nullptr; |
| } |
| |
| static bool HasPropertyThatCreatesStackingContext( |
| const Vector<CSSPropertyID>& properties) { |
| for (CSSPropertyID property : properties) { |
| switch (property) { |
| case CSSPropertyOpacity: |
| case CSSPropertyTransform: |
| case CSSPropertyAliasWebkitTransform: |
| case CSSPropertyTransformStyle: |
| case CSSPropertyAliasWebkitTransformStyle: |
| case CSSPropertyPerspective: |
| case CSSPropertyAliasWebkitPerspective: |
| case CSSPropertyTranslate: |
| case CSSPropertyRotate: |
| case CSSPropertyScale: |
| case CSSPropertyOffsetPath: |
| case CSSPropertyOffsetPosition: |
| case CSSPropertyWebkitMask: |
| case CSSPropertyWebkitMaskBoxImage: |
| case CSSPropertyClipPath: |
| case CSSPropertyAliasWebkitClipPath: |
| case CSSPropertyWebkitBoxReflect: |
| case CSSPropertyFilter: |
| case CSSPropertyAliasWebkitFilter: |
| case CSSPropertyBackdropFilter: |
| case CSSPropertyZIndex: |
| case CSSPropertyPosition: |
| case CSSPropertyMixBlendMode: |
| case CSSPropertyIsolation: |
| return true; |
| default: |
| break; |
| } |
| } |
| return false; |
| } |
| |
| void ComputedStyle::UpdateIsStackingContext(bool is_document_element, |
| bool is_in_top_layer) { |
| if (IsStackingContext()) |
| return; |
| |
| // Force a stacking context for transform-style: preserve-3d. This happens |
| // even if preserves-3d is ignored due to a 'grouping property' being present |
| // which requires flattening. See ComputedStyle::UsedTransformStyle3D() and |
| // ComputedStyle::HasGroupingProperty(). |
| // This is legacy behavior that is left ambiguous in the official specs. |
| // See crbug.com/663650 for more details." |
| if (TransformStyle3D() == kTransformStyle3DPreserve3D) { |
| SetIsStackingContext(true); |
| return; |
| } |
| |
| if (is_document_element || is_in_top_layer || |
| StyleType() == kPseudoIdBackdrop || HasOpacity() || |
| HasTransformRelatedProperty() || HasMask() || ClipPath() || |
| BoxReflect() || HasFilterInducingProperty() || HasBackdropFilter() || |
| HasBlendMode() || HasIsolation() || HasViewportConstrainedPosition() || |
| HasPropertyThatCreatesStackingContext(WillChangeProperties()) || |
| ContainsPaint()) { |
| SetIsStackingContext(true); |
| } |
| } |
| |
| void ComputedStyle::AddCallbackSelector(const String& selector) { |
| if (!rare_non_inherited_data_->callback_selectors_.Contains(selector)) |
| rare_non_inherited_data_.Access()->callback_selectors_.push_back(selector); |
| } |
| |
| void ComputedStyle::SetContent(ContentData* content_data) { |
| SET_VAR(rare_non_inherited_data_, content_, content_data); |
| } |
| |
| bool ComputedStyle::HasWillChangeCompositingHint() const { |
| for (size_t i = 0; |
| i < rare_non_inherited_data_->will_change_->properties_.size(); ++i) { |
| switch (rare_non_inherited_data_->will_change_->properties_[i]) { |
| case CSSPropertyOpacity: |
| case CSSPropertyTransform: |
| case CSSPropertyAliasWebkitTransform: |
| case CSSPropertyTop: |
| case CSSPropertyLeft: |
| case CSSPropertyBottom: |
| case CSSPropertyRight: |
| return true; |
| default: |
| break; |
| } |
| } |
| return false; |
| } |
| |
| bool ComputedStyle::HasWillChangeTransformHint() const { |
| for (const auto& property : |
| rare_non_inherited_data_->will_change_->properties_) { |
| switch (property) { |
| case CSSPropertyTransform: |
| case CSSPropertyAliasWebkitTransform: |
| case CSSPropertyPerspective: |
| case CSSPropertyTranslate: |
| case CSSPropertyScale: |
| case CSSPropertyRotate: |
| case CSSPropertyOffsetPath: |
| case CSSPropertyOffsetPosition: |
| return true; |
| default: |
| break; |
| } |
| } |
| return false; |
| } |
| |
| bool ComputedStyle::RequireTransformOrigin( |
| ApplyTransformOrigin apply_origin, |
| ApplyMotionPath apply_motion_path) const { |
| // transform-origin brackets the transform with translate operations. |
| // Optimize for the case where the only transform is a translation, since the |
| // transform-origin is irrelevant in that case. |
| if (apply_origin != kIncludeTransformOrigin) |
| return false; |
| |
| if (apply_motion_path == kIncludeMotionPath) |
| return true; |
| |
| for (const auto& operation : Transform().Operations()) { |
| TransformOperation::OperationType type = operation->GetType(); |
| if (type != TransformOperation::kTranslateX && |
| type != TransformOperation::kTranslateY && |
| type != TransformOperation::kTranslate && |
| type != TransformOperation::kTranslateZ && |
| type != TransformOperation::kTranslate3D) |
| return true; |
| } |
| |
| return Scale() || Rotate(); |
| } |
| |
| void ComputedStyle::ApplyTransform( |
| TransformationMatrix& result, |
| const LayoutSize& border_box_size, |
| ApplyTransformOrigin apply_origin, |
| ApplyMotionPath apply_motion_path, |
| ApplyIndependentTransformProperties apply_independent_transform_properties) |
| const { |
| ApplyTransform(result, FloatRect(FloatPoint(), FloatSize(border_box_size)), |
| apply_origin, apply_motion_path, |
| apply_independent_transform_properties); |
| } |
| |
| void ComputedStyle::ApplyTransform( |
| TransformationMatrix& result, |
| const FloatRect& bounding_box, |
| ApplyTransformOrigin apply_origin, |
| ApplyMotionPath apply_motion_path, |
| ApplyIndependentTransformProperties apply_independent_transform_properties) |
| const { |
| if (!HasOffset()) |
| apply_motion_path = kExcludeMotionPath; |
| bool apply_transform_origin = |
| RequireTransformOrigin(apply_origin, apply_motion_path); |
| |
| float origin_x = 0; |
| float origin_y = 0; |
| float origin_z = 0; |
| |
| const FloatSize& box_size = bounding_box.Size(); |
| if (apply_transform_origin || |
| // We need to calculate originX and originY for applying motion path. |
| apply_motion_path == kIncludeMotionPath) { |
| origin_x = FloatValueForLength(TransformOriginX(), box_size.Width()) + |
| bounding_box.X(); |
| origin_y = FloatValueForLength(TransformOriginY(), box_size.Height()) + |
| bounding_box.Y(); |
| if (apply_transform_origin) { |
| origin_z = TransformOriginZ(); |
| result.Translate3d(origin_x, origin_y, origin_z); |
| } |
| } |
| |
| if (apply_independent_transform_properties == |
| kIncludeIndependentTransformProperties) { |
| if (Translate()) |
| Translate()->Apply(result, box_size); |
| |
| if (Rotate()) |
| Rotate()->Apply(result, box_size); |
| |
| if (Scale()) |
| Scale()->Apply(result, box_size); |
| } |
| |
| if (apply_motion_path == kIncludeMotionPath) |
| ApplyMotionPathTransform(origin_x, origin_y, bounding_box, result); |
| |
| for (const auto& operation : Transform().Operations()) |
| operation->Apply(result, box_size); |
| |
| if (apply_transform_origin) { |
| result.Translate3d(-origin_x, -origin_y, -origin_z); |
| } |
| } |
| |
| void ComputedStyle::ApplyMotionPathTransform( |
| float origin_x, |
| float origin_y, |
| const FloatRect& bounding_box, |
| TransformationMatrix& transform) const { |
| const StyleMotionData& motion_data = |
| rare_non_inherited_data_->transform_->motion_; |
| // TODO(ericwilligers): crbug.com/638055 Apply offset-position. |
| if (!motion_data.path_) { |
| return; |
| } |
| const LengthPoint& position = OffsetPosition(); |
| const LengthPoint& anchor = OffsetAnchor(); |
| const StylePath& motion_path = *motion_data.path_; |
| float path_length = motion_path.length(); |
| float distance = FloatValueForLength(motion_data.distance_, path_length); |
| float computed_distance; |
| if (motion_path.IsClosed() && path_length > 0) { |
| computed_distance = fmod(distance, path_length); |
| if (computed_distance < 0) |
| computed_distance += path_length; |
| } else { |
| computed_distance = clampTo<float>(distance, 0, path_length); |
| } |
| |
| FloatPoint point; |
| float angle; |
| motion_path.GetPath().PointAndNormalAtLength(computed_distance, point, angle); |
| |
| if (motion_data.rotation_.type == kOffsetRotationFixed) |
| angle = 0; |
| |
| float origin_shift_x = 0; |
| float origin_shift_y = 0; |
| // If offset-Position and offset-anchor properties are not yet enabled, |
| // they will have the default value, auto. |
| if (position.X() != Length(kAuto) || anchor.X() != Length(kAuto)) { |
| // Shift the origin from transform-origin to offset-anchor. |
| origin_shift_x = |
| FloatValueForLength(anchor.X(), bounding_box.Width()) - |
| FloatValueForLength(TransformOriginX(), bounding_box.Width()); |
| origin_shift_y = |
| FloatValueForLength(anchor.Y(), bounding_box.Height()) - |
| FloatValueForLength(TransformOriginY(), bounding_box.Height()); |
| } |
| |
| transform.Translate(point.X() - origin_x + origin_shift_x, |
| point.Y() - origin_y + origin_shift_y); |
| transform.Rotate(angle + motion_data.rotation_.angle); |
| |
| if (position.X() != Length(kAuto) || anchor.X() != Length(kAuto)) |
| // Shift the origin back to transform-origin. |
| transform.Translate(-origin_shift_x, -origin_shift_y); |
| } |
| |
| void ComputedStyle::SetTextShadow(PassRefPtr<ShadowList> s) { |
| rare_inherited_data_.Access()->text_shadow_ = std::move(s); |
| } |
| |
| void ComputedStyle::SetBoxShadow(PassRefPtr<ShadowList> s) { |
| rare_non_inherited_data_.Access()->box_shadow_ = std::move(s); |
| } |
| |
| static FloatRoundedRect::Radii CalcRadiiFor(const BorderData& border, |
| const LengthSize& top_left, |
| const LengthSize& top_right, |
| const LengthSize& bottom_left, |
| const LengthSize& bottom_right, |
| LayoutSize size) { |
| return FloatRoundedRect::Radii( |
| FloatSize( |
| FloatValueForLength(top_left.Width(), size.Width().ToFloat()), |
| FloatValueForLength(top_left.Height(), size.Height().ToFloat())), |
| FloatSize( |
| FloatValueForLength(top_right.Width(), size.Width().ToFloat()), |
| FloatValueForLength(top_right.Height(), size.Height().ToFloat())), |
| FloatSize( |
| FloatValueForLength(bottom_left.Width(), size.Width().ToFloat()), |
| FloatValueForLength(bottom_left.Height(), size.Height().ToFloat())), |
| FloatSize( |
| FloatValueForLength(bottom_right.Width(), size.Width().ToFloat()), |
| FloatValueForLength(bottom_right.Height(), size.Height().ToFloat()))); |
| } |
| |
| StyleImage* ComputedStyle::ListStyleImage() const { |
| return rare_inherited_data_->list_style_image_.Get(); |
| } |
| void ComputedStyle::SetListStyleImage(StyleImage* v) { |
| if (rare_inherited_data_->list_style_image_ != v) |
| rare_inherited_data_.Access()->list_style_image_ = v; |
| } |
| |
| Color ComputedStyle::GetColor() const { |
| return inherited_data_->color_; |
| } |
| Color ComputedStyle::VisitedLinkColor() const { |
| return inherited_data_->visited_link_color_; |
| } |
| void ComputedStyle::SetColor(const Color& v) { |
| SET_VAR(inherited_data_, color_, v); |
| } |
| void ComputedStyle::SetVisitedLinkColor(const Color& v) { |
| SET_VAR(inherited_data_, visited_link_color_, v); |
| } |
| |
| short ComputedStyle::HorizontalBorderSpacing() const { |
| return inherited_data_->horizontal_border_spacing_; |
| } |
| short ComputedStyle::VerticalBorderSpacing() const { |
| return inherited_data_->vertical_border_spacing_; |
| } |
| void ComputedStyle::SetHorizontalBorderSpacing(short v) { |
| SET_VAR(inherited_data_, horizontal_border_spacing_, v); |
| } |
| void ComputedStyle::SetVerticalBorderSpacing(short v) { |
| SET_VAR(inherited_data_, vertical_border_spacing_, v); |
| } |
| |
| FloatRoundedRect ComputedStyle::GetRoundedBorderFor( |
| const LayoutRect& border_rect, |
| bool include_logical_left_edge, |
| bool include_logical_right_edge) const { |
| FloatRoundedRect rounded_rect(PixelSnappedIntRect(border_rect)); |
| if (HasBorderRadius()) { |
| FloatRoundedRect::Radii radii = |
| CalcRadiiFor(Border(), BorderTopLeftRadius(), BorderTopRightRadius(), |
| BorderBottomLeftRadius(), BorderBottomRightRadius(), |
| border_rect.Size()); |
| rounded_rect.IncludeLogicalEdges(radii, IsHorizontalWritingMode(), |
| include_logical_left_edge, |
| include_logical_right_edge); |
| rounded_rect.ConstrainRadii(); |
| } |
| return rounded_rect; |
| } |
| |
| FloatRoundedRect ComputedStyle::GetRoundedInnerBorderFor( |
| const LayoutRect& border_rect, |
| bool include_logical_left_edge, |
| bool include_logical_right_edge) const { |
| bool horizontal = IsHorizontalWritingMode(); |
| |
| int left_width = (!horizontal || include_logical_left_edge) |
| ? roundf(BorderLeftWidth()) |
| : 0; |
| int right_width = (!horizontal || include_logical_right_edge) |
| ? roundf(BorderRightWidth()) |
| : 0; |
| int top_width = |
| (horizontal || include_logical_left_edge) ? roundf(BorderTopWidth()) : 0; |
| int bottom_width = (horizontal || include_logical_right_edge) |
| ? roundf(BorderBottomWidth()) |
| : 0; |
| |
| return GetRoundedInnerBorderFor( |
| border_rect, |
| LayoutRectOutsets(-top_width, -right_width, -bottom_width, -left_width), |
| include_logical_left_edge, include_logical_right_edge); |
| } |
| |
| FloatRoundedRect ComputedStyle::GetRoundedInnerBorderFor( |
| const LayoutRect& border_rect, |
| const LayoutRectOutsets& insets, |
| bool include_logical_left_edge, |
| bool include_logical_right_edge) const { |
| LayoutRect inner_rect(border_rect); |
| inner_rect.Expand(insets); |
| LayoutSize inner_rect_size = inner_rect.Size(); |
| inner_rect_size.ClampNegativeToZero(); |
| inner_rect.SetSize(inner_rect_size); |
| |
| FloatRoundedRect rounded_rect(PixelSnappedIntRect(inner_rect)); |
| |
| if (HasBorderRadius()) { |
| FloatRoundedRect::Radii radii = GetRoundedBorderFor(border_rect).GetRadii(); |
| // Insets use negative values. |
| radii.Shrink(-insets.Top().ToFloat(), -insets.Bottom().ToFloat(), |
| -insets.Left().ToFloat(), -insets.Right().ToFloat()); |
| rounded_rect.IncludeLogicalEdges(radii, IsHorizontalWritingMode(), |
| include_logical_left_edge, |
| include_logical_right_edge); |
| } |
| return rounded_rect; |
| } |
| |
| static bool AllLayersAreFixed(const FillLayer& layer) { |
| for (const FillLayer* curr_layer = &layer; curr_layer; |
| curr_layer = curr_layer->Next()) { |
| if (!curr_layer->GetImage() || |
| curr_layer->Attachment() != kFixedBackgroundAttachment) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ComputedStyle::HasEntirelyFixedBackground() const { |
| return AllLayersAreFixed(BackgroundLayers()); |
| } |
| |
| const CounterDirectiveMap* ComputedStyle::GetCounterDirectives() const { |
| return rare_non_inherited_data_->counter_directives_.get(); |
| } |
| |
| CounterDirectiveMap& ComputedStyle::AccessCounterDirectives() { |
| std::unique_ptr<CounterDirectiveMap>& map = |
| rare_non_inherited_data_.Access()->counter_directives_; |
| if (!map) |
| map = WTF::WrapUnique(new CounterDirectiveMap); |
| return *map; |
| } |
| |
| const CounterDirectives ComputedStyle::GetCounterDirectives( |
| const AtomicString& identifier) const { |
| if (const CounterDirectiveMap* directives = GetCounterDirectives()) |
| return directives->at(identifier); |
| return CounterDirectives(); |
| } |
| |
| void ComputedStyle::ClearIncrementDirectives() { |
| if (!GetCounterDirectives()) |
| return; |
| |
| // This makes us copy even if we may not be removing any items. |
| CounterDirectiveMap& map = AccessCounterDirectives(); |
| typedef CounterDirectiveMap::iterator Iterator; |
| |
| Iterator end = map.end(); |
| for (Iterator it = map.begin(); it != end; ++it) |
| it->value.ClearIncrement(); |
| } |
| |
| void ComputedStyle::ClearResetDirectives() { |
| if (!GetCounterDirectives()) |
| return; |
| |
| // This makes us copy even if we may not be removing any items. |
| CounterDirectiveMap& map = AccessCounterDirectives(); |
| typedef CounterDirectiveMap::iterator Iterator; |
| |
| Iterator end = map.end(); |
| for (Iterator it = map.begin(); it != end; ++it) |
| it->value.ClearReset(); |
| } |
| |
| AtomicString ComputedStyle::LocaleForLineBreakIterator() const { |
| LineBreakIteratorMode mode = LineBreakIteratorMode::kDefault; |
| switch (GetLineBreak()) { |
| case kLineBreakAuto: |
| case kLineBreakAfterWhiteSpace: |
| return Locale(); |
| case kLineBreakNormal: |
| mode = LineBreakIteratorMode::kNormal; |
| break; |
| case kLineBreakStrict: |
| mode = LineBreakIteratorMode::kStrict; |
| break; |
| case kLineBreakLoose: |
| mode = LineBreakIteratorMode::kLoose; |
| break; |
| } |
| if (const LayoutLocale* locale = GetFontDescription().Locale()) |
| return locale->LocaleWithBreakKeyword(mode); |
| return Locale(); |
| } |
| |
| Hyphenation* ComputedStyle::GetHyphenation() const { |
| return GetHyphens() == kHyphensAuto |
| ? GetFontDescription().LocaleOrDefault().GetHyphenation() |
| : nullptr; |
| } |
| |
| const AtomicString& ComputedStyle::HyphenString() const { |
| const AtomicString& hyphenation_string = |
| rare_inherited_data_.Get()->hyphenation_string_; |
| if (!hyphenation_string.IsNull()) |
| return hyphenation_string; |
| |
| // FIXME: This should depend on locale. |
| DEFINE_STATIC_LOCAL(AtomicString, hyphen_minus_string, |
| (&kHyphenMinusCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, hyphen_string, (&kHyphenCharacter, 1)); |
| const SimpleFontData* primary_font = GetFont().PrimaryFont(); |
| DCHECK(primary_font); |
| return primary_font && primary_font->GlyphForCharacter(kHyphenCharacter) |
| ? hyphen_string |
| : hyphen_minus_string; |
| } |
| |
| const AtomicString& ComputedStyle::TextEmphasisMarkString() const { |
| switch (GetTextEmphasisMark()) { |
| case kTextEmphasisMarkNone: |
| return g_null_atom; |
| case kTextEmphasisMarkCustom: |
| return TextEmphasisCustomMark(); |
| case kTextEmphasisMarkDot: { |
| DEFINE_STATIC_LOCAL(AtomicString, filled_dot_string, |
| (&kBulletCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, open_dot_string, |
| (&kWhiteBulletCharacter, 1)); |
| return GetTextEmphasisFill() == kTextEmphasisFillFilled |
| ? filled_dot_string |
| : open_dot_string; |
| } |
| case kTextEmphasisMarkCircle: { |
| DEFINE_STATIC_LOCAL(AtomicString, filled_circle_string, |
| (&kBlackCircleCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, open_circle_string, |
| (&kWhiteCircleCharacter, 1)); |
| return GetTextEmphasisFill() == kTextEmphasisFillFilled |
| ? filled_circle_string |
| : open_circle_string; |
| } |
| case kTextEmphasisMarkDoubleCircle: { |
| DEFINE_STATIC_LOCAL(AtomicString, filled_double_circle_string, |
| (&kFisheyeCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, open_double_circle_string, |
| (&kBullseyeCharacter, 1)); |
| return GetTextEmphasisFill() == kTextEmphasisFillFilled |
| ? filled_double_circle_string |
| : open_double_circle_string; |
| } |
| case kTextEmphasisMarkTriangle: { |
| DEFINE_STATIC_LOCAL(AtomicString, filled_triangle_string, |
| (&kBlackUpPointingTriangleCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, open_triangle_string, |
| (&kWhiteUpPointingTriangleCharacter, 1)); |
| return GetTextEmphasisFill() == kTextEmphasisFillFilled |
| ? filled_triangle_string |
| : open_triangle_string; |
| } |
| case kTextEmphasisMarkSesame: { |
| DEFINE_STATIC_LOCAL(AtomicString, filled_sesame_string, |
| (&kSesameDotCharacter, 1)); |
| DEFINE_STATIC_LOCAL(AtomicString, open_sesame_string, |
| (&kWhiteSesameDotCharacter, 1)); |
| return GetTextEmphasisFill() == kTextEmphasisFillFilled |
| ? filled_sesame_string |
| : open_sesame_string; |
| } |
| case kTextEmphasisMarkAuto: |
| NOTREACHED(); |
| return g_null_atom; |
| } |
| |
| NOTREACHED(); |
| return g_null_atom; |
| } |
| |
| CSSAnimationData& ComputedStyle::AccessAnimations() { |
| if (!rare_non_inherited_data_.Access()->animations_) |
| rare_non_inherited_data_.Access()->animations_ = CSSAnimationData::Create(); |
| return *rare_non_inherited_data_->animations_; |
| } |
| |
| CSSTransitionData& ComputedStyle::AccessTransitions() { |
| if (!rare_non_inherited_data_.Access()->transitions_) |
| rare_non_inherited_data_.Access()->transitions_ = |
| CSSTransitionData::Create(); |
| return *rare_non_inherited_data_->transitions_; |
| } |
| |
| const Font& ComputedStyle::GetFont() const { |
| return inherited_data_->font_; |
| } |
| const FontDescription& ComputedStyle::GetFontDescription() const { |
| return inherited_data_->font_.GetFontDescription(); |
| } |
| float ComputedStyle::SpecifiedFontSize() const { |
| return GetFontDescription().SpecifiedSize(); |
| } |
| float ComputedStyle::ComputedFontSize() const { |
| return GetFontDescription().ComputedSize(); |
| } |
| LayoutUnit ComputedStyle::ComputedFontSizeAsFixed() const { |
| return LayoutUnit::FromFloatRound(GetFontDescription().ComputedSize()); |
| } |
| int ComputedStyle::FontSize() const { |
| return GetFontDescription().ComputedPixelSize(); |
| } |
| float ComputedStyle::FontSizeAdjust() const { |
| return GetFontDescription().SizeAdjust(); |
| } |
| bool ComputedStyle::HasFontSizeAdjust() const { |
| return GetFontDescription().HasSizeAdjust(); |
| } |
| FontWeight ComputedStyle::GetFontWeight() const { |
| return GetFontDescription().Weight(); |
| } |
| FontStretch ComputedStyle::GetFontStretch() const { |
| return GetFontDescription().Stretch(); |
| } |
| |
| TextDecoration ComputedStyle::TextDecorationsInEffect() const { |
| if (HasSimpleUnderlineInternal()) |
| return kTextDecorationUnderline; |
| if (!rare_inherited_data_->applied_text_decorations_) |
| return kTextDecorationNone; |
| |
| int decorations = 0; |
| |
| const Vector<AppliedTextDecoration>& applied = AppliedTextDecorations(); |
| |
| for (size_t i = 0; i < applied.size(); ++i) |
| decorations |= applied[i].Lines(); |
| |
| return static_cast<TextDecoration>(decorations); |
| } |
| |
| const Vector<AppliedTextDecoration>& ComputedStyle::AppliedTextDecorations() |
| const { |
| if (HasSimpleUnderlineInternal()) { |
| DEFINE_STATIC_LOCAL( |
| Vector<AppliedTextDecoration>, underline, |
| (1, AppliedTextDecoration( |
| kTextDecorationUnderline, kTextDecorationStyleSolid, |
| VisitedDependentColor(CSSPropertyTextDecorationColor)))); |
| // Since we only have one of these in memory, just update the color before |
| // returning. |
| underline.at(0).SetColor( |
| VisitedDependentColor(CSSPropertyTextDecorationColor)); |
| return underline; |
| } |
| if (!rare_inherited_data_->applied_text_decorations_) { |
| DEFINE_STATIC_LOCAL(Vector<AppliedTextDecoration>, empty, ()); |
| return empty; |
| } |
| |
| return rare_inherited_data_->applied_text_decorations_->GetVector(); |
| } |
| |
| StyleInheritedVariables* ComputedStyle::InheritedVariables() const { |
| return rare_inherited_data_->variables_.Get(); |
| } |
| |
| StyleNonInheritedVariables* ComputedStyle::NonInheritedVariables() const { |
| return rare_non_inherited_data_->variables_.get(); |
| } |
| |
| StyleInheritedVariables& ComputedStyle::MutableInheritedVariables() { |
| RefPtr<StyleInheritedVariables>& variables = |
| rare_inherited_data_.Access()->variables_; |
| if (!variables) |
| variables = StyleInheritedVariables::Create(); |
| else if (!variables->HasOneRef()) |
| variables = variables->Copy(); |
| return *variables; |
| } |
| |
| StyleNonInheritedVariables& ComputedStyle::MutableNonInheritedVariables() { |
| std::unique_ptr<StyleNonInheritedVariables>& variables = |
| rare_non_inherited_data_.Access()->variables_; |
| if (!variables) |
| variables = StyleNonInheritedVariables::Create(); |
| return *variables; |
| } |
| |
| void ComputedStyle::SetUnresolvedInheritedVariable( |
| const AtomicString& name, |
| PassRefPtr<CSSVariableData> value) { |
| DCHECK(value && value->NeedsVariableResolution()); |
| MutableInheritedVariables().SetVariable(name, std::move(value)); |
| } |
| |
| void ComputedStyle::SetUnresolvedNonInheritedVariable( |
| const AtomicString& name, |
| PassRefPtr<CSSVariableData> value) { |
| DCHECK(value && value->NeedsVariableResolution()); |
| MutableNonInheritedVariables().SetVariable(name, std::move(value)); |
| } |
| |
| void ComputedStyle::SetResolvedUnregisteredVariable( |
| const AtomicString& name, |
| PassRefPtr<CSSVariableData> value) { |
| DCHECK(value && !value->NeedsVariableResolution()); |
| MutableInheritedVariables().SetVariable(name, std::move(value)); |
| } |
| |
| void ComputedStyle::SetResolvedInheritedVariable( |
| const AtomicString& name, |
| PassRefPtr<CSSVariableData> value, |
| const CSSValue* parsed_value) { |
| DCHECK(!!value == !!parsed_value); |
| DCHECK(!(value && value->NeedsVariableResolution())); |
| |
| StyleInheritedVariables& variables = MutableInheritedVariables(); |
| variables.SetVariable(name, std::move(value)); |
| variables.SetRegisteredVariable(name, parsed_value); |
| } |
| |
| void ComputedStyle::SetResolvedNonInheritedVariable( |
| const AtomicString& name, |
| PassRefPtr<CSSVariableData> value, |
| const CSSValue* parsed_value) { |
| DCHECK(!!value == !!parsed_value); |
| DCHECK(!(value && value->NeedsVariableResolution())); |
| |
| StyleNonInheritedVariables& variables = MutableNonInheritedVariables(); |
| variables.SetVariable(name, std::move(value)); |
| variables.SetRegisteredVariable(name, parsed_value); |
| } |
| |
| void ComputedStyle::RemoveVariable(const AtomicString& name, |
| bool is_inherited_property) { |
| if (is_inherited_property) { |
| MutableInheritedVariables().RemoveVariable(name); |
| } else { |
| MutableNonInheritedVariables().RemoveVariable(name); |
| } |
| } |
| |
| CSSVariableData* ComputedStyle::GetVariable(const AtomicString& name) const { |
| CSSVariableData* variable = GetVariable(name, true); |
| if (variable) { |
| return variable; |
| } |
| return GetVariable(name, false); |
| } |
| |
| CSSVariableData* ComputedStyle::GetVariable(const AtomicString& name, |
| bool is_inherited_property) const { |
| if (is_inherited_property) { |
| return InheritedVariables() ? InheritedVariables()->GetVariable(name) |
| : nullptr; |
| } |
| return NonInheritedVariables() ? NonInheritedVariables()->GetVariable(name) |
| : nullptr; |
| } |
| |
| const CSSValue* ComputedStyle::GetRegisteredVariable( |
| const AtomicString& name, |
| bool is_inherited_property) const { |
| if (is_inherited_property) { |
| return InheritedVariables() ? InheritedVariables()->RegisteredVariable(name) |
| : nullptr; |
| } |
| return NonInheritedVariables() |
| ? NonInheritedVariables()->RegisteredVariable(name) |
| : nullptr; |
| } |
| |
| const CSSValue* ComputedStyle::GetRegisteredVariable( |
| const AtomicString& name) const { |
| // Registered custom properties are by default non-inheriting so check there |
| // first. |
| const CSSValue* result = GetRegisteredVariable(name, false); |
| if (result) { |
| return result; |
| } |
| return GetRegisteredVariable(name, true); |
| } |
| |
| float ComputedStyle::WordSpacing() const { |
| return GetFontDescription().WordSpacing(); |
| } |
| float ComputedStyle::LetterSpacing() const { |
| return GetFontDescription().LetterSpacing(); |
| } |
| |
| bool ComputedStyle::SetFontDescription(const FontDescription& v) { |
| if (inherited_data_->font_.GetFontDescription() != v) { |
| inherited_data_.Access()->font_ = Font(v); |
| return true; |
| } |
| return false; |
| } |
| |
| void ComputedStyle::SetFont(const Font& font) { |
| inherited_data_.Access()->font_ = font; |
| } |
| |
| bool ComputedStyle::HasIdenticalAscentDescentAndLineGap( |
| const ComputedStyle& other) const { |
| const SimpleFontData* font_data = GetFont().PrimaryFont(); |
| const SimpleFontData* other_font_data = other.GetFont().PrimaryFont(); |
| return font_data && other_font_data && |
| font_data->GetFontMetrics().HasIdenticalAscentDescentAndLineGap( |
| other_font_data->GetFontMetrics()); |
| } |
| |
| const Length& ComputedStyle::SpecifiedLineHeight() const { |
| return inherited_data_->line_height_; |
| } |
| Length ComputedStyle::LineHeight() const { |
| const Length& lh = inherited_data_->line_height_; |
| // Unlike getFontDescription().computedSize() and hence fontSize(), this is |
| // recalculated on demand as we only store the specified line height. |
| // FIXME: Should consider scaling the fixed part of any calc expressions |
| // too, though this involves messily poking into CalcExpressionLength. |
| if (lh.IsFixed()) { |
| float multiplier = TextAutosizingMultiplier(); |
| return Length( |
| TextAutosizer::ComputeAutosizedFontSize(lh.Value(), multiplier), |
| kFixed); |
| } |
| |
| return lh; |
| } |
| |
| void ComputedStyle::SetLineHeight(const Length& specified_line_height) { |
| SET_VAR(inherited_data_, line_height_, specified_line_height); |
| } |
| |
| int ComputedStyle::ComputedLineHeight() const { |
| const Length& lh = LineHeight(); |
| |
| // Negative value means the line height is not set. Use the font's built-in |
| // spacing, if avalible. |
| if (lh.IsNegative() && GetFont().PrimaryFont()) |
| return GetFont().PrimaryFont()->GetFontMetrics().LineSpacing(); |
| |
| if (lh.IsPercentOrCalc()) |
| return MinimumValueForLength(lh, LayoutUnit(ComputedFontSize())).ToInt(); |
| |
| return std::min(lh.Value(), LayoutUnit::Max().ToFloat()); |
| } |
| |
| LayoutUnit ComputedStyle::ComputedLineHeightAsFixed() const { |
| const Length& lh = LineHeight(); |
| |
| // Negative value means the line height is not set. Use the font's built-in |
| // spacing, if avalible. |
| if (lh.IsNegative() && GetFont().PrimaryFont()) |
| return GetFont().PrimaryFont()->GetFontMetrics().FixedLineSpacing(); |
| |
| if (lh.IsPercentOrCalc()) |
| return MinimumValueForLength(lh, ComputedFontSizeAsFixed()); |
| |
| return LayoutUnit::FromFloatRound(lh.Value()); |
| } |
| |
| void ComputedStyle::SetWordSpacing(float word_spacing) { |
| FontSelector* current_font_selector = GetFont().GetFontSelector(); |
| FontDescription desc(GetFontDescription()); |
| desc.SetWordSpacing(word_spacing); |
| SetFontDescription(desc); |
| GetFont().Update(current_font_selector); |
| } |
| |
| void ComputedStyle::SetLetterSpacing(float letter_spacing) { |
| FontSelector* current_font_selector = GetFont().GetFontSelector(); |
| FontDescription desc(GetFontDescription()); |
| desc.SetLetterSpacing(letter_spacing); |
| SetFontDescription(desc); |
| GetFont().Update(current_font_selector); |
| } |
| |
| void ComputedStyle::SetTextAutosizingMultiplier(float multiplier) { |
| SET_VAR(inherited_data_, text_autosizing_multiplier_, multiplier); |
| |
| float size = SpecifiedFontSize(); |
| |
| DCHECK(std::isfinite(size)); |
| if (!std::isfinite(size) || size < 0) |
| size = 0; |
| else |
| size = std::min(kMaximumAllowedFontSize, size); |
| |
| FontSelector* current_font_selector = GetFont().GetFontSelector(); |
| FontDescription desc(GetFontDescription()); |
| desc.SetSpecifiedSize(size); |
| desc.SetComputedSize(size); |
| |
| float autosized_font_size = |
| TextAutosizer::ComputeAutosizedFontSize(size, multiplier); |
| desc.SetComputedSize(std::min(kMaximumAllowedFontSize, autosized_font_size)); |
| |
| SetFontDescription(desc); |
| GetFont().Update(current_font_selector); |
| } |
| |
| void ComputedStyle::AddAppliedTextDecoration( |
| const AppliedTextDecoration& decoration) { |
| RefPtr<AppliedTextDecorationList>& list = |
| rare_inherited_data_.Access()->applied_text_decorations_; |
| |
| if (!list) |
| list = AppliedTextDecorationList::Create(); |
| else if (!list->HasOneRef()) |
| list = list->Copy(); |
| |
| list->push_back(decoration); |
| } |
| |
| void ComputedStyle::OverrideTextDecorationColors(Color override_color) { |
| RefPtr<AppliedTextDecorationList>& list = |
| rare_inherited_data_.Access()->applied_text_decorations_; |
| DCHECK(list); |
| if (!list->HasOneRef()) |
| list = list->Copy(); |
| |
| for (size_t i = 0; i < list->size(); ++i) |
| list->at(i).SetColor(override_color); |
| } |
| |
| void ComputedStyle::ApplyTextDecorations( |
| const Color& parent_text_decoration_color, |
| bool override_existing_colors) { |
| if (GetTextDecoration() == kTextDecorationNone && |
| !HasSimpleUnderlineInternal() && |
| !rare_inherited_data_->applied_text_decorations_) |
| return; |
| |
| // If there are any color changes or decorations set by this element, stop |
| // using m_hasSimpleUnderline. |
| Color current_text_decoration_color = |
| VisitedDependentColor(CSSPropertyTextDecorationColor); |
| if (HasSimpleUnderlineInternal() && |
| (GetTextDecoration() != kTextDecorationNone || |
| current_text_decoration_color != parent_text_decoration_color)) { |
| SetHasSimpleUnderlineInternal(false); |
| AddAppliedTextDecoration(AppliedTextDecoration( |
| kTextDecorationUnderline, kTextDecorationStyleSolid, |
| parent_text_decoration_color)); |
| } |
| if (override_existing_colors && |
| rare_inherited_data_->applied_text_decorations_) |
| OverrideTextDecorationColors(current_text_decoration_color); |
| if (GetTextDecoration() == kTextDecorationNone) |
| return; |
| DCHECK(!HasSimpleUnderlineInternal()); |
| // To save memory, we don't use AppliedTextDecoration objects in the common |
| // case of a single simple underline of currentColor. |
| TextDecoration decoration_lines = GetTextDecoration(); |
| TextDecorationStyle decoration_style = GetTextDecorationStyle(); |
| bool is_simple_underline = decoration_lines == kTextDecorationUnderline && |
| decoration_style == kTextDecorationStyleSolid && |
| TextDecorationColor().IsCurrentColor(); |
| if (is_simple_underline && !rare_inherited_data_->applied_text_decorations_) { |
| SetHasSimpleUnderlineInternal(true); |
| return; |
| } |
| |
| AddAppliedTextDecoration(AppliedTextDecoration( |
| decoration_lines, decoration_style, current_text_decoration_color)); |
| } |
| |
| void ComputedStyle::ClearAppliedTextDecorations() { |
| SetHasSimpleUnderlineInternal(false); |
| |
| if (rare_inherited_data_->applied_text_decorations_) |
| rare_inherited_data_.Access()->applied_text_decorations_ = nullptr; |
| } |
| |
| void ComputedStyle::RestoreParentTextDecorations( |
| const ComputedStyle& parent_style) { |
| SetHasSimpleUnderlineInternal(parent_style.HasSimpleUnderlineInternal()); |
| if (rare_inherited_data_->applied_text_decorations_ != |
| parent_style.rare_inherited_data_->applied_text_decorations_) { |
| rare_inherited_data_.Access()->applied_text_decorations_ = |
| parent_style.rare_inherited_data_->applied_text_decorations_; |
| } |
| } |
| |
| void ComputedStyle::ClearMultiCol() { |
| rare_non_inherited_data_.Access()->multi_col_ = nullptr; |
| rare_non_inherited_data_.Access()->multi_col_.Init(); |
| } |
| |
| StyleColor ComputedStyle::DecorationColorIncludingFallback( |
| bool visited_link) const { |
| StyleColor style_color = |
| visited_link ? VisitedLinkTextDecorationColor() : TextDecorationColor(); |
| |
| if (!style_color.IsCurrentColor()) |
| return style_color; |
| |
| if (TextStrokeWidth()) { |
| // Prefer stroke color if possible, but not if it's fully transparent. |
| StyleColor text_stroke_style_color = |
| visited_link ? VisitedLinkTextStrokeColor() : TextStrokeColor(); |
| if (!text_stroke_style_color.IsCurrentColor() && |
| text_stroke_style_color.GetColor().Alpha()) |
| return text_stroke_style_color; |
| } |
| |
| return visited_link ? VisitedLinkTextFillColor() : TextFillColor(); |
| } |
| |
| Color ComputedStyle::ColorIncludingFallback(int color_property, |
| bool visited_link) const { |
| StyleColor result(StyleColor::CurrentColor()); |
| EBorderStyle border_style = kBorderStyleNone; |
| switch (color_property) { |
| case CSSPropertyBackgroundColor: |
| result = visited_link ? VisitedLinkBackgroundColor() : BackgroundColor(); |
| break; |
| case CSSPropertyBorderLeftColor: |
| result = visited_link ? VisitedLinkBorderLeftColor() : BorderLeftColor(); |
| border_style = BorderLeftStyle(); |
| break; |
| case CSSPropertyBorderRightColor: |
| result = |
| visited_link ? VisitedLinkBorderRightColor() : BorderRightColor(); |
| border_style = BorderRightStyle(); |
| break; |
| case CSSPropertyBorderTopColor: |
| result = visited_link ? VisitedLinkBorderTopColor() : BorderTopColor(); |
| border_style = BorderTopStyle(); |
| break; |
| case CSSPropertyBorderBottomColor: |
| result = |
| visited_link ? VisitedLinkBorderBottomColor() : BorderBottomColor(); |
| border_style = BorderBottomStyle(); |
| break; |
| case CSSPropertyCaretColor: { |
| StyleAutoColor auto_color = |
| visited_link ? VisitedLinkCaretColor() : CaretColor(); |
| // TODO(rego): We may want to adjust the caret color if it's the same than |
| // the background to ensure good visibility and contrast. |
| result = auto_color.IsAutoColor() ? StyleColor::CurrentColor() |
| : auto_color.ToStyleColor(); |
| break; |
| } |
| case CSSPropertyColor: |
| result = visited_link ? VisitedLinkColor() : GetColor(); |
| break; |
| case CSSPropertyOutlineColor: |
| result = visited_link ? VisitedLinkOutlineColor() : OutlineColor(); |
| break; |
| case CSSPropertyColumnRuleColor: |
| result = visited_link ? VisitedLinkColumnRuleColor() : ColumnRuleColor(); |
| break; |
| case CSSPropertyWebkitTextEmphasisColor: |
| result = |
| visited_link ? VisitedLinkTextEmphasisColor() : TextEmphasisColor(); |
| break; |
| case CSSPropertyWebkitTextFillColor: |
| result = visited_link ? VisitedLinkTextFillColor() : TextFillColor(); |
| break; |
| case CSSPropertyWebkitTextStrokeColor: |
| result = visited_link ? VisitedLinkTextStrokeColor() : TextStrokeColor(); |
| break; |
| case CSSPropertyFloodColor: |
| result = FloodColor(); |
| break; |
| case CSSPropertyLightingColor: |
| result = LightingColor(); |
| break; |
| case CSSPropertyStopColor: |
| result = StopColor(); |
| break; |
| case CSSPropertyWebkitTapHighlightColor: |
| result = TapHighlightColor(); |
| break; |
| case CSSPropertyTextDecorationColor: |
| result = DecorationColorIncludingFallback(visited_link); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| if (!result.IsCurrentColor()) |
| return result.GetColor(); |
| |
| // FIXME: Treating styled borders with initial color differently causes |
| // problems, see crbug.com/316559, crbug.com/276231 |
| if (!visited_link && |
| (border_style == kBorderStyleInset || |
| border_style == kBorderStyleOutset || |
| border_style == kBorderStyleRidge || border_style == kBorderStyleGroove)) |
| return Color(238, 238, 238); |
| return visited_link ? VisitedLinkColor() : GetColor(); |
| } |
| |
| Color ComputedStyle::VisitedDependentColor(int color_property) const { |
| Color unvisited_color = ColorIncludingFallback(color_property, false); |
| if (InsideLink() != EInsideLink::kInsideVisitedLink) |
| return unvisited_color; |
| |
| Color visited_color = ColorIncludingFallback(color_property, true); |
| |
| // FIXME: Technically someone could explicitly specify the color transparent, |
| // but for now we'll just assume that if the background color is transparent |
| // that it wasn't set. Note that it's weird that we're returning unvisited |
| // info for a visited link, but given our restriction that the alpha values |
| // have to match, it makes more sense to return the unvisited background color |
| // if specified than it does to return black. This behavior matches what |
| // Firefox 4 does as well. |
| if (color_property == CSSPropertyBackgroundColor && |
| visited_color == Color::kTransparent) |
| return unvisited_color; |
| |
| // Take the alpha from the unvisited color, but get the RGB values from the |
| // visited color. |
| return Color(visited_color.Red(), visited_color.Green(), visited_color.Blue(), |
| unvisited_color.Alpha()); |
| } |
| |
| const BorderValue ComputedStyle::BorderBefore() const { |
| switch (GetWritingMode()) { |
| case WritingMode::kHorizontalTb: |
| return BorderTop(); |
| case WritingMode::kVerticalLr: |
| return BorderLeft(); |
| case WritingMode::kVerticalRl: |
| return BorderRight(); |
| } |
| NOTREACHED(); |
| return BorderTop(); |
| } |
| |
| const BorderValue ComputedStyle::BorderAfter() const { |
| switch (GetWritingMode()) { |
| case WritingMode::kHorizontalTb: |
| return BorderBottom(); |
| case WritingMode::kVerticalLr: |
| return BorderRight(); |
| case WritingMode::kVerticalRl: |
| return BorderLeft(); |
| } |
| NOTREACHED(); |
| return BorderBottom(); |
| } |
| |
| const BorderValue ComputedStyle::BorderStart() const { |
| if (IsHorizontalWritingMode()) |
| return IsLeftToRightDirection() ? BorderLeft() : BorderRight(); |
| return IsLeftToRightDirection() ? BorderTop() : BorderBottom(); |
| } |
| |
| const BorderValue ComputedStyle::BorderEnd() const { |
| if (IsHorizontalWritingMode()) |
| return IsLeftToRightDirection() ? BorderRight() : BorderLeft(); |
| return IsLeftToRightDirection() ? BorderBottom() : BorderTop(); |
| } |
| |
| float ComputedStyle::BorderBeforeWidth() const { |
| switch (GetWritingMode()) { |
| case WritingMode::kHorizontalTb: |
| return BorderTopWidth(); |
| case WritingMode::kVerticalLr: |
| return BorderLeftWidth(); |
| case WritingMode::kVerticalRl: |
| return BorderRightWidth(); |
| } |
| NOTREACHED(); |
| return BorderTopWidth(); |
| } |
| |
| float ComputedStyle::BorderAfterWidth() const { |
| switch (GetWritingMode()) { |
| case WritingMode::kHorizontalTb: |
| return BorderBottomWidth(); |
| case WritingMode::kVerticalLr: |
| return BorderRightWidth(); |
| case WritingMode::kVerticalRl: |
| return BorderLeftWidth(); |
| } |
| NOTREACHED(); |
| return BorderBottomWidth(); |
| } |
| |
| float ComputedStyle::BorderStartWidth() const { |
| if (IsHorizontalWritingMode()) |
| return IsLeftToRightDirection() ? BorderLeftWidth() : BorderRightWidth(); |
| return IsLeftToRightDirection() ? BorderTopWidth() : BorderBottomWidth(); |
| } |
| |
| float ComputedStyle::BorderEndWidth() const { |
| if (IsHorizontalWritingMode()) |
| return IsLeftToRightDirection() ? BorderRightWidth() : BorderLeftWidth(); |
| return IsLeftToRightDirection() ? BorderBottomWidth() : BorderTopWidth(); |
| } |
| |
| float ComputedStyle::BorderOverWidth() const { |
| return IsHorizontalWritingMode() ? BorderTopWidth() : BorderRightWidth(); |
| } |
| |
| float ComputedStyle::BorderUnderWidth() const { |
| return IsHorizontalWritingMode() ? BorderBottomWidth() : BorderLeftWidth(); |
| } |
| |
| void ComputedStyle::SetMarginStart(const Length& margin) { |
| if (IsHorizontalWritingMode()) { |
| if (IsLeftToRightDirection()) |
| SetMarginLeft(margin); |
| else |
| SetMarginRight(margin); |
| } else { |
| if (IsLeftToRightDirection()) |
| SetMarginTop(margin); |
| else |
| SetMarginBottom(margin); |
| } |
| } |
| |
| void ComputedStyle::SetMarginEnd(const Length& margin) { |
| if (IsHorizontalWritingMode()) { |
| if (IsLeftToRightDirection()) |
| SetMarginRight(margin); |
| else |
| SetMarginLeft(margin); |
| } else { |
| if (IsLeftToRightDirection()) |
| SetMarginBottom(margin); |
| else |
| SetMarginTop(margin); |
| } |
| } |
| |
| void ComputedStyle::SetOffsetPath(PassRefPtr<StylePath> path) { |
| rare_non_inherited_data_.Access()->transform_.Access()->motion_.path_ = |
| std::move(path); |
| } |
| |
| int ComputedStyle::OutlineOutsetExtent() const { |
| if (!HasOutline()) |
| return 0; |
| if (OutlineStyleIsAuto()) { |
| return GraphicsContext::FocusRingOutsetExtent( |
| OutlineOffset(), std::ceil(GetOutlineStrokeWidthForFocusRing())); |
| } |
| return std::max(0, SaturatedAddition(OutlineWidth(), OutlineOffset())); |
| } |
| |
| float ComputedStyle::GetOutlineStrokeWidthForFocusRing() const { |
| #if OS(MACOSX) |
| return OutlineWidth(); |
| #else |
| // Draw an outline with thickness in proportion to the zoom level, but never |
| // less than 1 pixel so that it remains visible. |
| return std::max(EffectiveZoom(), 1.f); |
| #endif |
| } |
| |
| bool ComputedStyle::ColumnRuleEquivalent( |
| const ComputedStyle* other_style) const { |
| return ColumnRuleStyle() == other_style->ColumnRuleStyle() && |
| ColumnRuleWidth() == other_style->ColumnRuleWidth() && |
| VisitedDependentColor(CSSPropertyColumnRuleColor) == |
| other_style->VisitedDependentColor(CSSPropertyColumnRuleColor); |
| } |
| |
| TextEmphasisMark ComputedStyle::GetTextEmphasisMark() const { |
| TextEmphasisMark mark = |
| static_cast<TextEmphasisMark>(rare_inherited_data_->text_emphasis_mark_); |
| if (mark != kTextEmphasisMarkAuto) |
| return mark; |
| |
| if (IsHorizontalWritingMode()) |
| return kTextEmphasisMarkDot; |
| |
| return kTextEmphasisMarkSesame; |
| } |
| |
| Color ComputedStyle::InitialTapHighlightColor() { |
| return LayoutTheme::TapHighlightColor(); |
| } |
| |
| const FilterOperations& ComputedStyle::InitialFilter() { |
| DEFINE_STATIC_LOCAL(FilterOperationsWrapper, ops, |
| (FilterOperationsWrapper::Create())); |
| return ops.Operations(); |
| } |
| |
| const FilterOperations& ComputedStyle::InitialBackdropFilter() { |
| DEFINE_STATIC_LOCAL(FilterOperationsWrapper, ops, |
| (FilterOperationsWrapper::Create())); |
| return ops.Operations(); |
| } |
| |
| LayoutRectOutsets ComputedStyle::ImageOutsets( |
| const NinePieceImage& image) const { |
| return LayoutRectOutsets( |
| NinePieceImage::ComputeOutset(image.Outset().Top(), BorderTopWidth()), |
| NinePieceImage::ComputeOutset(image.Outset().Right(), BorderRightWidth()), |
| NinePieceImage::ComputeOutset(image.Outset().Bottom(), |
| BorderBottomWidth()), |
| NinePieceImage::ComputeOutset(image.Outset().Left(), BorderLeftWidth())); |
| } |
| |
| void ComputedStyle::SetBorderImageSource(StyleImage* image) { |
| if (Border().image_.GetImage() == image) |
| return; |
| surround_data_.Access()->border_.image_.SetImage(image); |
| } |
| |
| void ComputedStyle::SetBorderImageSlices(const LengthBox& slices) { |
| if (Border().image_.ImageSlices() == slices) |
| return; |
| surround_data_.Access()->border_.image_.SetImageSlices(slices); |
| } |
| |
| void ComputedStyle::SetBorderImageSlicesFill(bool fill) { |
| if (Border().image_.Fill() == fill) |
| return; |
| surround_data_.Access()->border_.image_.SetFill(fill); |
| } |
| |
| void ComputedStyle::SetBorderImageWidth(const BorderImageLengthBox& slices) { |
| if (Border().image_.BorderSlices() == slices) |
| return; |
| surround_data_.Access()->border_.image_.SetBorderSlices(slices); |
| } |
| |
| void ComputedStyle::SetBorderImageOutset(const BorderImageLengthBox& outset) { |
| if (Border().image_.Outset() == outset) |
| return; |
| surround_data_.Access()->border_.image_.SetOutset(outset); |
| } |
| |
| bool ComputedStyle::BorderObscuresBackground() const { |
| if (!HasBorder()) |
| return false; |
| |
| // Bail if we have any border-image for now. We could look at the image alpha |
| // to improve this. |
| if (BorderImage().GetImage()) |
| return false; |
| |
| BorderEdge edges[4]; |
| GetBorderEdgeInfo(edges); |
| |
| for (int i = kBSTop; i <= kBSLeft; ++i) { |
| const BorderEdge& curr_edge = edges[i]; |
| if (!curr_edge.ObscuresBackground()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ComputedStyle::GetBorderEdgeInfo(BorderEdge edges[], |
| bool include_logical_left_edge, |
| bool include_logical_right_edge) const { |
| bool horizontal = IsHorizontalWritingMode(); |
| |
| edges[kBSTop] = BorderEdge( |
| BorderTopWidth(), VisitedDependentColor(CSSPropertyBorderTopColor), |
| BorderTopStyle(), horizontal || include_logical_left_edge); |
| |
| edges[kBSRight] = BorderEdge( |
| BorderRightWidth(), VisitedDependentColor(CSSPropertyBorderRightColor), |
| BorderRightStyle(), !horizontal || include_logical_right_edge); |
| |
| edges[kBSBottom] = BorderEdge( |
| BorderBottomWidth(), VisitedDependentColor(CSSPropertyBorderBottomColor), |
| BorderBottomStyle(), horizontal || include_logical_right_edge); |
| |
| edges[kBSLeft] = BorderEdge( |
| BorderLeftWidth(), VisitedDependentColor(CSSPropertyBorderLeftColor), |
| BorderLeftStyle(), !horizontal || include_logical_left_edge); |
| } |
| |
| void ComputedStyle::CopyChildDependentFlagsFrom(const ComputedStyle& other) { |
| SetEmptyState(other.EmptyState()); |
| if (other.HasExplicitlyInheritedProperties()) |
| SetHasExplicitlyInheritedProperties(); |
| } |
| |
| bool ComputedStyle::ShadowListHasCurrentColor(const ShadowList* shadow_list) { |
| if (!shadow_list) |
| return false; |
| for (size_t i = shadow_list->Shadows().size(); i--;) { |
| if (shadow_list->Shadows()[i].GetColor().IsCurrentColor()) |
| return true; |
| } |
| return false; |
| } |
| |
| static inline Vector<GridTrackSize> InitialGridAutoTracks() { |
| Vector<GridTrackSize> track_size_list; |
| track_size_list.ReserveInitialCapacity(1); |
| track_size_list.UncheckedAppend(GridTrackSize(Length(kAuto))); |
| return track_size_list; |
| } |
| |
| Vector<GridTrackSize> ComputedStyle::InitialGridAutoColumns() { |
| return InitialGridAutoTracks(); |
| } |
| |
| Vector<GridTrackSize> ComputedStyle::InitialGridAutoRows() { |
| return InitialGridAutoTracks(); |
| } |
| |
| int AdjustForAbsoluteZoom(int value, float zoom_factor) { |
| if (zoom_factor == 1) |
| return value; |
| // Needed because computeLengthInt truncates (rather than rounds) when scaling |
| // up. |
| float fvalue = value; |
| if (zoom_factor > 1) { |
| if (value < 0) |
| fvalue -= 0.5f; |
| else |
| fvalue += 0.5f; |
| } |
| |
| return RoundForImpreciseConversion<int>(fvalue / zoom_factor); |
| } |
| |
| } // namespace blink |