| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. |
| * All rights reserved. |
| * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
| * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| * (http://www.torchmobile.com/) |
| * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
| * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" |
| |
| #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h" |
| #include "third_party/blink/renderer/core/animation/css/css_animations.h" |
| #include "third_party/blink/renderer/core/animation/element_animations.h" |
| #include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h" |
| #include "third_party/blink/renderer/core/css/css_default_style_sheets.h" |
| #include "third_party/blink/renderer/core/css/css_font_selector.h" |
| #include "third_party/blink/renderer/core/css/css_identifier_value.h" |
| #include "third_party/blink/renderer/core/css/css_initial_color_value.h" |
| #include "third_party/blink/renderer/core/css/css_keyframe_rule.h" |
| #include "third_party/blink/renderer/core/css/css_keyframes_rule.h" |
| #include "third_party/blink/renderer/core/css/css_property_names.h" |
| #include "third_party/blink/renderer/core/css/css_rule_list.h" |
| #include "third_party/blink/renderer/core/css/css_selector.h" |
| #include "third_party/blink/renderer/core/css/css_selector_watch.h" |
| #include "third_party/blink/renderer/core/css/css_style_declaration.h" |
| #include "third_party/blink/renderer/core/css/css_style_rule.h" |
| #include "third_party/blink/renderer/core/css/element_rule_collector.h" |
| #include "third_party/blink/renderer/core/css/font_face.h" |
| #include "third_party/blink/renderer/core/css/page_rule_collector.h" |
| #include "third_party/blink/renderer/core/css/part_names.h" |
| #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" |
| #include "third_party/blink/renderer/core/css/properties/css_property.h" |
| #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" |
| #include "third_party/blink/renderer/core/css/resolver/match_result.h" |
| #include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h" |
| #include "third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_adjuster.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_cascade.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_stats.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_rule_usage_tracker.h" |
| #include "third_party/blink/renderer/core/css/scoped_css_value.h" |
| #include "third_party/blink/renderer/core/css/style_engine.h" |
| #include "third_party/blink/renderer/core/css/style_rule_import.h" |
| #include "third_party/blink/renderer/core/css/style_sheet_contents.h" |
| #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h" |
| #include "third_party/blink/renderer/core/dom/node_computed_style.h" |
| #include "third_party/blink/renderer/core/dom/shadow_root.h" |
| #include "third_party/blink/renderer/core/dom/space_split_string.h" |
| #include "third_party/blink/renderer/core/dom/text.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/frame/web_feature.h" |
| #include "third_party/blink/renderer/core/html/custom/custom_element_definition.h" |
| #include "third_party/blink/renderer/core/html/html_iframe_element.h" |
| #include "third_party/blink/renderer/core/html/html_slot_element.h" |
| #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" |
| #include "third_party/blink/renderer/core/html/track/text_track.h" |
| #include "third_party/blink/renderer/core/html/track/vtt/vtt_cue.h" |
| #include "third_party/blink/renderer/core/html/track/vtt/vtt_element.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h" |
| #include "third_party/blink/renderer/core/mathml/mathml_operator_element.h" |
| #include "third_party/blink/renderer/core/mathml/mathml_padded_element.h" |
| #include "third_party/blink/renderer/core/mathml/mathml_space_element.h" |
| #include "third_party/blink/renderer/core/mathml_names.h" |
| #include "third_party/blink/renderer/core/media_type_names.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/style/style_initial_data.h" |
| #include "third_party/blink/renderer/core/style_property_shorthand.h" |
| #include "third_party/blink/renderer/core/svg/svg_element.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_set.h" |
| #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" |
| #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" |
| #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| void SetAnimationUpdateIfNeeded(StyleResolverState& state, Element& element) { |
| // If any changes to CSS Animations were detected, stash the update away for |
| // application after the layout object is updated if we're in the appropriate |
| // scope. |
| if (!state.AnimationUpdate().IsEmpty()) { |
| auto& element_animations = element.EnsureElementAnimations(); |
| element_animations.CssAnimations().SetPendingUpdate( |
| state.AnimationUpdate()); |
| } |
| } |
| |
| bool HasAnimationsOrTransitions(const StyleResolverState& state) { |
| return state.Style()->Animations() || state.Style()->Transitions() || |
| (state.GetAnimatingElement() && |
| state.GetAnimatingElement()->HasAnimations()); |
| } |
| |
| bool ShouldComputeBaseComputedStyle(const ComputedStyle* base_computed_style) { |
| #if DCHECK_IS_ON() |
| // The invariant in the base computed style optimization is that as long as |
| // |IsAnimationStyleChange| is true, the computed style that would be |
| // generated by the style resolver is equivalent to the one we hold |
| // internally. To ensure this, we always compute a new style here disregarding |
| // the fact that we have a base computed style when DCHECKs are enabled, and |
| // call ValidateBaseComputedStyle() to check that the optimization was sound. |
| return true; |
| #else |
| return !base_computed_style; |
| #endif // !DCHECK_IS_ON() |
| } |
| |
| // Compare the base computed style with the one we compute to validate that the |
| // optimization is sound. |
| bool ValidateBaseComputedStyle(const ComputedStyle* base_computed_style, |
| const ComputedStyle& computed_style) { |
| #if DCHECK_IS_ON() |
| if (!base_computed_style) |
| return true; |
| // Under certain conditions ComputedStyle::operator==() may return false for |
| // differences that are permitted during an animation. |
| // The FontFaceCache version number may be increased without forcing a style |
| // recalc (see crbug.com/471079). |
| if (!base_computed_style->GetFont().IsFallbackValid()) |
| return true; |
| // Images use instance equality rather than value equality (see |
| // crbug.com/781461). |
| for (CSSPropertyID id : |
| {CSSPropertyID::kBackgroundImage, CSSPropertyID::kWebkitMaskImage}) { |
| if (!CSSPropertyEquality::PropertiesEqual( |
| PropertyHandle(CSSProperty::Get(id)), *base_computed_style, |
| computed_style)) { |
| return true; |
| } |
| } |
| return *base_computed_style == computed_style; |
| #else |
| return true; |
| #endif // DCHECK_IS_ON() |
| } |
| |
| // When force-computing the base computed style for validation purposes, |
| // we need to reset the StyleCascade when the base computed style optimization |
| // is used. This is because we don't want the computation of the base to |
| // populate the cascade, as they are supposed to be empty when the optimization |
| // is in use. This is to match the behavior of non-DCHECK builds. |
| void MaybeResetCascade(StyleCascade& cascade) { |
| #if DCHECK_IS_ON() |
| cascade.Reset(); |
| #endif // DCHECK_IS_ON() |
| } |
| |
| } // namespace |
| |
| static CSSPropertyValueSet* LeftToRightDeclaration() { |
| DEFINE_STATIC_LOCAL( |
| Persistent<MutableCSSPropertyValueSet>, left_to_right_decl, |
| (MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLQuirksMode))); |
| if (left_to_right_decl->IsEmpty()) { |
| left_to_right_decl->SetProperty(CSSPropertyID::kDirection, |
| CSSValueID::kLtr); |
| } |
| return left_to_right_decl; |
| } |
| |
| static CSSPropertyValueSet* RightToLeftDeclaration() { |
| DEFINE_STATIC_LOCAL( |
| Persistent<MutableCSSPropertyValueSet>, right_to_left_decl, |
| (MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLQuirksMode))); |
| if (right_to_left_decl->IsEmpty()) { |
| right_to_left_decl->SetProperty(CSSPropertyID::kDirection, |
| CSSValueID::kRtl); |
| } |
| return right_to_left_decl; |
| } |
| |
| static CSSPropertyValueSet* DocumentElementUserAgentDeclarations() { |
| DEFINE_STATIC_LOCAL( |
| Persistent<MutableCSSPropertyValueSet>, document_element_ua_decl, |
| (MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode))); |
| if (document_element_ua_decl->IsEmpty()) { |
| document_element_ua_decl->SetProperty(CSSPropertyID::kColor, |
| *CSSInitialColorValue::Create()); |
| } |
| return document_element_ua_decl; |
| } |
| |
| static void CollectScopedResolversForHostedShadowTrees( |
| const Element& element, |
| HeapVector<Member<ScopedStyleResolver>, 8>& resolvers) { |
| ShadowRoot* root = element.GetShadowRoot(); |
| if (!root) |
| return; |
| |
| // Adding scoped resolver for active shadow roots for shadow host styling. |
| if (ScopedStyleResolver* resolver = root->GetScopedStyleResolver()) |
| resolvers.push_back(resolver); |
| } |
| |
| StyleResolver::StyleResolver(Document& document) : document_(document) { |
| UpdateMediaType(); |
| } |
| |
| StyleResolver::~StyleResolver() = default; |
| |
| void StyleResolver::Dispose() { |
| matched_properties_cache_.Clear(); |
| } |
| |
| void StyleResolver::SetRuleUsageTracker(StyleRuleUsageTracker* tracker) { |
| tracker_ = tracker; |
| } |
| |
| static inline ScopedStyleResolver* ScopedResolverFor(const Element& element) { |
| // For normal elements, returning element->treeScope().scopedStyleResolver() |
| // is enough. Rules for ::cue and custom pseudo elements like |
| // ::-webkit-meter-bar pierce through a single shadow dom boundary and apply |
| // to elements in sub-scopes. |
| // |
| // An assumption here is that these elements belong to scopes without a |
| // ScopedStyleResolver due to the fact that VTT scopes and UA shadow trees |
| // don't have <style> or <link> elements. This is backed up by the DCHECKs |
| // below. The one exception to this assumption are the media controls which |
| // use a <style> element for CSS animations in the shadow DOM. If a <style> |
| // element is present in the shadow DOM then this will also block any |
| // author styling. |
| |
| TreeScope* tree_scope = &element.GetTreeScope(); |
| if (ScopedStyleResolver* resolver = tree_scope->GetScopedStyleResolver()) { |
| #if DCHECK_IS_ON() |
| if (!element.HasMediaControlAncestor()) |
| DCHECK(element.ShadowPseudoId().IsEmpty()); |
| #endif |
| DCHECK(!element.IsVTTElement()); |
| return resolver; |
| } |
| |
| tree_scope = tree_scope->ParentTreeScope(); |
| if (!tree_scope) |
| return nullptr; |
| if (element.ShadowPseudoId().IsEmpty() && !element.IsVTTElement()) |
| return nullptr; |
| return tree_scope->GetScopedStyleResolver(); |
| } |
| |
| // Matches :host and :host-context rules if the element is a shadow host. |
| // It matches rules from the ShadowHostRules of the ScopedStyleResolver |
| // of the attached shadow root. |
| static void MatchHostRules(const Element& element, |
| ElementRuleCollector& collector) { |
| ShadowRoot* shadow_root = element.GetShadowRoot(); |
| if (!shadow_root) |
| return; |
| if (ScopedStyleResolver* resolver = shadow_root->GetScopedStyleResolver()) |
| resolver->CollectMatchingShadowHostRules(collector); |
| } |
| |
| // Matches custom element rules from Custom Element Default Style. |
| static void MatchCustomElementRules(const Element& element, |
| ElementRuleCollector& collector) { |
| if (!RuntimeEnabledFeatures::CustomElementDefaultStyleEnabled()) |
| return; |
| if (CustomElementDefinition* definition = |
| element.GetCustomElementDefinition()) { |
| if (definition->HasDefaultStyleSheets()) { |
| for (CSSStyleSheet* style : definition->DefaultStyleSheets()) { |
| if (!style) |
| continue; |
| RuleSet* rule_set = |
| element.GetDocument().GetStyleEngine().RuleSetForSheet(*style); |
| if (rule_set) |
| collector.CollectMatchingRules(MatchRequest(rule_set)); |
| } |
| } |
| } |
| } |
| |
| // Matches :host and :host-context rules |
| // and custom element rules from Custom Element Default Style. |
| static void MatchHostAndCustomElementRules(const Element& element, |
| ElementRuleCollector& collector) { |
| ShadowRoot* shadow_root = element.GetShadowRoot(); |
| ScopedStyleResolver* resolver = |
| shadow_root ? shadow_root->GetScopedStyleResolver() : nullptr; |
| if (!resolver && !RuntimeEnabledFeatures::CustomElementDefaultStyleEnabled()) |
| return; |
| collector.ClearMatchedRules(); |
| MatchCustomElementRules(element, collector); |
| MatchHostRules(element, collector); |
| collector.SortAndTransferMatchedRules(); |
| // TODO(futhark): If the resolver is null here, it means we are matching rules |
| // for custom element default styles. Since we don't have a |
| // ScopedStyleResolver if the custom element does not have a shadow root, |
| // there is no way to collect @-rules for @font-face, @keyframes, etc. We |
| // currently pass the element's TreeScope, which might not be what we want. It |
| // means that if you have: |
| // |
| // <style>@keyframes anim { ... }</style> |
| // <custom-element></custom-element> |
| // |
| // and the custom-element is defined with: |
| // |
| // @keyframes anim { ... } |
| // custom-element { animation-name: anim } |
| // |
| // it means that the custom element will pick up the @keyframes definition |
| // from the element's scope. |
| collector.FinishAddingAuthorRulesForTreeScope( |
| resolver ? resolver->GetTreeScope() : element.GetTreeScope()); |
| } |
| |
| static void MatchSlottedRules(const Element&, ElementRuleCollector&); |
| static void MatchSlottedRulesForUAHost(const Element& element, |
| ElementRuleCollector& collector) { |
| if (element.ShadowPseudoId() != shadow_element_names::kPseudoInputPlaceholder) |
| return; |
| |
| // We allow ::placeholder pseudo element after ::slotted(). Since we are |
| // matching such pseudo elements starting from inside the UA shadow DOM of |
| // the element having the placeholder, we need to match ::slotted rules from |
| // the scopes to which the placeholder's host element may be slotted. |
| // |
| // Example: |
| // |
| // <div id=host> |
| // <:shadow-root> |
| // <style>::slotted(input)::placeholder { color: green }</style> |
| // <slot /> |
| // </:shadow-root> |
| // <input placeholder="PLACEHOLDER-TEXT"> |
| // <:ua-shadow-root> |
| // ... <placeholder>PLACEHOLDER-TEXT</placeholder> ... |
| // </:ua-shadow-root> |
| // </input> |
| // </div> |
| // |
| // Here we need to match the ::slotted rule from the #host shadow tree where |
| // the input is slotted on the placeholder element. |
| DCHECK(element.OwnerShadowHost()); |
| MatchSlottedRules(*element.OwnerShadowHost(), collector); |
| } |
| |
| // Matches `::slotted` selectors. It matches rules in the element's slot's |
| // scope. If that slot is itself slotted it will match rules in the slot's |
| // slot's scope and so on. The result is that it considers a chain of scopes |
| // descending from the element's own scope. |
| static void MatchSlottedRules(const Element& element, |
| ElementRuleCollector& collector) { |
| MatchSlottedRulesForUAHost(element, collector); |
| HTMLSlotElement* slot = element.AssignedSlot(); |
| if (!slot) |
| return; |
| |
| HeapVector<Member<ScopedStyleResolver>> resolvers; |
| for (; slot; slot = slot->AssignedSlot()) { |
| if (ScopedStyleResolver* resolver = |
| slot->GetTreeScope().GetScopedStyleResolver()) |
| resolvers.push_back(resolver); |
| } |
| for (auto it = resolvers.rbegin(); it != resolvers.rend(); ++it) { |
| collector.ClearMatchedRules(); |
| (*it)->CollectMatchingSlottedRules(collector); |
| collector.SortAndTransferMatchedRules(); |
| collector.FinishAddingAuthorRulesForTreeScope((*it)->GetTreeScope()); |
| } |
| } |
| |
| const static TextTrack* GetTextTrackFromElement(const Element& element) { |
| if (auto* vtt_element = DynamicTo<VTTElement>(element)) |
| return vtt_element->GetTrack(); |
| if (auto* vtt_cue_background_box = DynamicTo<VTTCueBackgroundBox>(element)) |
| return vtt_cue_background_box->GetTrack(); |
| return nullptr; |
| } |
| |
| static void MatchVTTRules(const Element& element, |
| ElementRuleCollector& collector) { |
| const TextTrack* text_track = GetTextTrackFromElement(element); |
| if (!text_track) |
| return; |
| const HeapVector<Member<CSSStyleSheet>>& styles = |
| text_track->GetCSSStyleSheets(); |
| if (!styles.IsEmpty()) { |
| int style_sheet_index = 0; |
| collector.ClearMatchedRules(); |
| for (CSSStyleSheet* style : styles) { |
| StyleEngine& style_engine = element.GetDocument().GetStyleEngine(); |
| RuleSet* rule_set = style_engine.RuleSetForSheet(*style); |
| if (rule_set) { |
| collector.CollectMatchingRules(MatchRequest( |
| rule_set, nullptr /* scope */, style, style_sheet_index, |
| style_engine.EnsureVTTOriginatingElement())); |
| style_sheet_index++; |
| } |
| } |
| collector.SortAndTransferMatchedRules(); |
| } |
| } |
| |
| // Matches rules from the element's scope. The selectors may cross shadow |
| // boundaries during matching, like for :host-context. |
| static void MatchElementScopeRules(const Element& element, |
| ScopedStyleResolver* element_scope_resolver, |
| ElementRuleCollector& collector) { |
| if (element_scope_resolver) { |
| collector.ClearMatchedRules(); |
| element_scope_resolver->CollectMatchingElementScopeRules(collector); |
| collector.SortAndTransferMatchedRules(); |
| } |
| |
| MatchVTTRules(element, collector); |
| if (element.IsStyledElement() && element.InlineStyle() && |
| !collector.IsCollectingForPseudoElement()) { |
| // Inline style is immutable as long as there is no CSSOM wrapper. |
| bool is_inline_style_cacheable = !element.InlineStyle()->IsMutable(); |
| collector.AddElementStyleProperties(element.InlineStyle(), |
| is_inline_style_cacheable); |
| } |
| |
| collector.FinishAddingAuthorRulesForTreeScope( |
| element_scope_resolver ? element_scope_resolver->GetTreeScope() |
| : element.GetTreeScope()); |
| } |
| |
| void StyleResolver::MatchPseudoPartRulesForUAHost( |
| const Element& element, |
| ElementRuleCollector& collector) { |
| const AtomicString& pseudo_id = element.ShadowPseudoId(); |
| if (pseudo_id != shadow_element_names::kPseudoInputPlaceholder && |
| pseudo_id != shadow_element_names::kPseudoFileUploadButton) { |
| return; |
| } |
| |
| // We allow ::placeholder pseudo element after ::part(). See |
| // MatchSlottedRulesForUAHost for a more detailed explanation. |
| DCHECK(element.OwnerShadowHost()); |
| MatchPseudoPartRules(*element.OwnerShadowHost(), collector, |
| /* for_shadow_pseudo */ true); |
| } |
| |
| void StyleResolver::MatchPseudoPartRules(const Element& element, |
| ElementRuleCollector& collector, |
| bool for_shadow_pseudo) { |
| if (!for_shadow_pseudo) |
| MatchPseudoPartRulesForUAHost(element, collector); |
| DOMTokenList* part = element.GetPart(); |
| if (!part) |
| return; |
| |
| PartNames current_names(part->TokenSet()); |
| |
| // ::part selectors in the shadow host's scope and above can match this |
| // element. |
| Element* host = element.OwnerShadowHost(); |
| if (!host) |
| return; |
| |
| while (current_names.size()) { |
| TreeScope& tree_scope = host->GetTreeScope(); |
| if (ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver()) { |
| collector.ClearMatchedRules(); |
| resolver->CollectMatchingPartPseudoRules(collector, current_names, |
| for_shadow_pseudo); |
| collector.SortAndTransferMatchedRules(); |
| collector.FinishAddingAuthorRulesForTreeScope(resolver->GetTreeScope()); |
| } |
| |
| // If the host doesn't forward any parts using partmap= then the element is |
| // unreachable from any scope further above and we can stop. |
| const NamesMap* part_map = host->PartNamesMap(); |
| if (!part_map) |
| return; |
| |
| // We have reached the top-level document. |
| if (!(host = host->OwnerShadowHost())) |
| return; |
| |
| current_names.PushMap(*part_map); |
| } |
| } |
| |
| void StyleResolver::MatchAuthorRules( |
| const Element& element, |
| ScopedStyleResolver* element_scope_resolver, |
| ElementRuleCollector& collector) { |
| MatchHostAndCustomElementRules(element, collector); |
| MatchSlottedRules(element, collector); |
| MatchElementScopeRules(element, element_scope_resolver, collector); |
| MatchPseudoPartRules(element, collector); |
| } |
| |
| void StyleResolver::MatchUserRules(ElementRuleCollector& collector) { |
| collector.ClearMatchedRules(); |
| GetDocument().GetStyleEngine().CollectMatchingUserRules(collector); |
| collector.SortAndTransferMatchedRules(); |
| collector.FinishAddingUserRules(); |
| } |
| |
| namespace { |
| |
| bool IsInMediaUAShadow(const Element& element) { |
| ShadowRoot* root = element.ContainingShadowRoot(); |
| if (!root || !root->IsUserAgent()) |
| return false; |
| ShadowRoot* outer_root; |
| do { |
| outer_root = root; |
| root = root->host().ContainingShadowRoot(); |
| } while (root && root->IsUserAgent()); |
| return outer_root->host().IsMediaElement(); |
| } |
| |
| } // namespace |
| |
| void StyleResolver::MatchUARules(const Element& element, |
| ElementRuleCollector& collector) { |
| collector.SetMatchingUARules(true); |
| |
| CSSDefaultStyleSheets& default_style_sheets = |
| CSSDefaultStyleSheets::Instance(); |
| if (!print_media_type_) { |
| if (LIKELY(element.IsHTMLElement() || element.IsVTTElement())) { |
| MatchRuleSet(collector, default_style_sheets.DefaultStyle()); |
| if (UNLIKELY(IsInMediaUAShadow(element))) { |
| MatchRuleSet(collector, |
| default_style_sheets.DefaultMediaControlsStyle()); |
| } |
| } else if (element.IsSVGElement()) { |
| MatchRuleSet(collector, default_style_sheets.DefaultSVGStyle()); |
| } else if (element.namespaceURI() == mathml_names::kNamespaceURI) { |
| MatchRuleSet(collector, default_style_sheets.DefaultMathMLStyle()); |
| } |
| } else { |
| MatchRuleSet(collector, default_style_sheets.DefaultPrintStyle()); |
| } |
| |
| // In quirks mode, we match rules from the quirks user agent sheet. |
| if (GetDocument().InQuirksMode()) |
| MatchRuleSet(collector, default_style_sheets.DefaultQuirksStyle()); |
| |
| // If document uses view source styles (in view source mode or in xml viewer |
| // mode), then we match rules from the view source style sheet. |
| if (GetDocument().IsViewSource()) |
| MatchRuleSet(collector, default_style_sheets.DefaultViewSourceStyle()); |
| |
| // If the system is in forced colors mode, match rules from the forced colors |
| // style sheet. |
| if (IsForcedColorsModeEnabled()) |
| MatchRuleSet(collector, default_style_sheets.DefaultForcedColorStyle()); |
| |
| if (collector.IsCollectingForPseudoElement()) { |
| if (RuleSet* default_pseudo_style = |
| default_style_sheets.DefaultPseudoElementStyleOrNull()) |
| MatchRuleSet(collector, default_pseudo_style); |
| } |
| |
| collector.FinishAddingUARules(); |
| collector.SetMatchingUARules(false); |
| } |
| |
| void StyleResolver::MatchRuleSet(ElementRuleCollector& collector, |
| RuleSet* rules) { |
| collector.ClearMatchedRules(); |
| collector.CollectMatchingRules(MatchRequest(rules)); |
| collector.SortAndTransferMatchedRules(); |
| } |
| |
| DISABLE_CFI_PERF |
| void StyleResolver::MatchAllRules(StyleResolverState& state, |
| ElementRuleCollector& collector, |
| bool include_smil_properties) { |
| Element& element = state.GetElement(); |
| MatchUARules(element, collector); |
| MatchUserRules(collector); |
| |
| // Now check author rules, beginning first with presentational attributes |
| // mapped from HTML. |
| if (element.IsStyledElement() && !state.IsForPseudoElement()) { |
| collector.AddElementStyleProperties(element.PresentationAttributeStyle()); |
| |
| // Now we check additional mapped declarations. |
| // Tables and table cells share an additional mapped rule that must be |
| // applied after all attributes, since their mapped style depends on the |
| // values of multiple attributes. |
| collector.AddElementStyleProperties( |
| element.AdditionalPresentationAttributeStyle()); |
| |
| if (auto* html_element = DynamicTo<HTMLElement>(element)) { |
| if (html_element->HasDirectionAuto()) { |
| collector.AddElementStyleProperties( |
| html_element->CachedDirectionality() == TextDirection::kLtr |
| ? LeftToRightDeclaration() |
| : RightToLeftDeclaration()); |
| } |
| } |
| } |
| |
| ScopedStyleResolver* element_scope_resolver = ScopedResolverFor(element); |
| MatchAuthorRules(element, element_scope_resolver, collector); |
| |
| if (element.IsStyledElement() && !state.IsForPseudoElement()) { |
| // Now check SMIL animation override style. |
| auto* svg_element = DynamicTo<SVGElement>(element); |
| if (include_smil_properties && svg_element) { |
| collector.AddElementStyleProperties( |
| svg_element->AnimatedSMILStyleProperties(), false /* isCacheable */); |
| } |
| } |
| |
| collector.FinishAddingAuthorRulesForTreeScope( |
| element_scope_resolver ? element_scope_resolver->GetTreeScope() |
| : element.GetTreeScope()); |
| } |
| |
| scoped_refptr<ComputedStyle> StyleResolver::StyleForViewport() { |
| scoped_refptr<ComputedStyle> viewport_style = |
| InitialStyleForElement(GetDocument()); |
| |
| viewport_style->SetZIndex(0); |
| viewport_style->SetIsStackingContextWithoutContainment(true); |
| viewport_style->SetDisplay(EDisplay::kBlock); |
| viewport_style->SetPosition(EPosition::kAbsolute); |
| |
| // Document::InheritHtmlAndBodyElementStyles will set the final overflow |
| // style values, but they should initially be auto to avoid premature |
| // scrollbar removal in PaintLayerScrollableArea::UpdateAfterStyleChange. |
| viewport_style->SetOverflowX(EOverflow::kAuto); |
| viewport_style->SetOverflowY(EOverflow::kAuto); |
| |
| GetDocument().GetStyleEngine().ApplyVisionDeficiencyStyle(viewport_style); |
| |
| return viewport_style; |
| } |
| |
| static ElementAnimations* GetElementAnimations( |
| const StyleResolverState& state) { |
| if (!state.GetAnimatingElement()) |
| return nullptr; |
| return state.GetAnimatingElement()->GetElementAnimations(); |
| } |
| |
| static const ComputedStyle* CachedAnimationBaseComputedStyle( |
| StyleResolverState& state) { |
| ElementAnimations* element_animations = GetElementAnimations(state); |
| if (!element_animations) |
| return nullptr; |
| |
| return element_animations->BaseComputedStyle(); |
| } |
| |
| static void UpdateAnimationBaseComputedStyle(StyleResolverState& state, |
| StyleCascade& cascade, |
| bool forced_update) { |
| if (!state.GetAnimatingElement()) |
| return; |
| |
| if (!state.CanCacheBaseStyle()) |
| return; |
| |
| if (forced_update) |
| state.GetAnimatingElement()->EnsureElementAnimations(); |
| |
| ElementAnimations* element_animations = |
| state.GetAnimatingElement()->GetElementAnimations(); |
| if (!element_animations) |
| return; |
| |
| if (element_animations->IsAnimationStyleChange() && |
| element_animations->BaseComputedStyle()) { |
| return; |
| } |
| |
| std::unique_ptr<CSSBitset> important_set = cascade.GetImportantSet(); |
| element_animations->UpdateBaseComputedStyle(state.Style(), |
| std::move(important_set)); |
| } |
| |
| scoped_refptr<ComputedStyle> StyleResolver::StyleForElement( |
| Element* element, |
| const StyleRecalcContext& style_recalc_context, |
| const ComputedStyle* default_parent, |
| const ComputedStyle* default_layout_parent, |
| RuleMatchingBehavior matching_behavior) { |
| DCHECK(GetDocument().GetFrame()); |
| DCHECK(GetDocument().GetSettings()); |
| |
| GetDocument().GetStyleEngine().IncStyleForElementCount(); |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), elements_styled, |
| 1); |
| |
| SelectorFilterParentScope::EnsureParentStackIsPushed(); |
| |
| StyleResolverState state(GetDocument(), *element, default_parent, |
| default_layout_parent); |
| |
| // This function can return different results with different last 3 params, |
| // but we can only cache one base computed style for animations, thus we cache |
| // only when this function is called with default last 3 params. |
| bool can_cache_animation_base_computed_style = |
| !default_parent && !default_layout_parent && |
| matching_behavior == kMatchAllRules; |
| state.SetCanCacheBaseStyle(can_cache_animation_base_computed_style); |
| |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| |
| ApplyBaseStyle(element, style_recalc_context, state, cascade, |
| cascade.MutableMatchResult(), matching_behavior, |
| can_cache_animation_base_computed_style); |
| |
| if (ApplyAnimatedStyle(state, cascade)) { |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| styles_animated, 1); |
| StyleAdjuster::AdjustComputedStyle(state, element); |
| } |
| |
| if (IsA<HTMLBodyElement>(*element)) { |
| GetDocument().GetTextLinkColors().SetTextColor( |
| state.Style()->GetCurrentColor()); |
| } |
| |
| if (element->IsMathMLElement()) |
| ApplyMathMLCustomStyleProperties(element, state); |
| |
| SetAnimationUpdateIfNeeded(state, *element); |
| |
| if (state.Style()->HasViewportUnits()) |
| GetDocument().SetHasViewportUnits(); |
| |
| if (state.Style()->HasRemUnits()) |
| GetDocument().GetStyleEngine().SetUsesRemUnit(true); |
| |
| if (state.Style()->HasGlyphRelativeUnits()) |
| UseCounter::Count(GetDocument(), WebFeature::kHasGlyphRelativeUnits); |
| |
| state.LoadPendingResources(); |
| |
| // Now return the style. |
| return state.TakeStyle(); |
| } |
| |
| void StyleResolver::InitStyleAndApplyInheritance(Element& element, |
| StyleResolverState& state) { |
| if (state.ParentStyle()) { |
| scoped_refptr<ComputedStyle> style = ComputedStyle::Create(); |
| style->InheritFrom(*state.ParentStyle(), |
| IsAtShadowBoundary(&element) |
| ? ComputedStyle::kAtShadowBoundary |
| : ComputedStyle::kNotAtShadowBoundary); |
| state.SetStyle(std::move(style)); |
| |
| // contenteditable attribute (implemented by -webkit-user-modify) should |
| // be propagated from shadow host to distributed node. |
| if (element.AssignedSlot()) { |
| if (Element* parent = element.parentElement()) { |
| if (const ComputedStyle* shadow_host_style = parent->GetComputedStyle()) |
| state.Style()->SetUserModify(shadow_host_style->UserModify()); |
| } |
| } |
| } else { |
| state.SetStyle(InitialStyleForElement(GetDocument())); |
| state.SetParentStyle(ComputedStyle::Clone(*state.Style())); |
| state.SetLayoutParentStyle(state.ParentStyle()); |
| if (element != GetDocument().documentElement()) { |
| // Strictly, we should only allow the root element to inherit from |
| // initial styles, but we allow getComputedStyle() for connected |
| // elements outside the flat tree rooted at an unassigned shadow host |
| // child or a slot fallback element. |
| DCHECK((IsShadowHost(element.parentNode()) || |
| IsA<HTMLSlotElement>(element.parentNode())) && |
| !LayoutTreeBuilderTraversal::ParentElement(element)); |
| state.Style()->SetIsEnsuredOutsideFlatTree(); |
| } |
| } |
| |
| if (element.IsLink()) { |
| state.Style()->SetIsLink(); |
| EInsideLink link_state = state.ElementLinkState(); |
| if (link_state != EInsideLink::kNotInsideLink) { |
| bool force_visited = false; |
| probe::ForcePseudoState(&element, CSSSelector::kPseudoVisited, |
| &force_visited); |
| if (force_visited) |
| link_state = EInsideLink::kInsideVisitedLink; |
| } |
| state.Style()->SetInsideLink(link_state); |
| } |
| } |
| |
| void StyleResolver::ApplyMathMLCustomStyleProperties( |
| Element* element, |
| StyleResolverState& state) { |
| DCHECK(element && element->IsMathMLElement()); |
| ComputedStyle& style = state.StyleRef(); |
| if (auto* space = DynamicTo<MathMLSpaceElement>(*element)) { |
| space->AddMathBaselineIfNeeded(style, state.CssToLengthConversionData()); |
| } else if (auto* padded = DynamicTo<MathMLPaddedElement>(*element)) { |
| padded->AddMathBaselineIfNeeded(style, state.CssToLengthConversionData()); |
| padded->AddMathPaddedDepthIfNeeded(style, |
| state.CssToLengthConversionData()); |
| padded->AddMathPaddedLSpaceIfNeeded(style, |
| state.CssToLengthConversionData()); |
| padded->AddMathPaddedVOffsetIfNeeded(style, |
| state.CssToLengthConversionData()); |
| } else if (auto* fraction = DynamicTo<MathMLFractionElement>(*element)) { |
| fraction->AddMathFractionBarThicknessIfNeeded( |
| style, state.CssToLengthConversionData()); |
| } else if (auto* operator_element = |
| DynamicTo<MathMLOperatorElement>(*element)) { |
| operator_element->AddMathLSpaceIfNeeded(style, |
| state.CssToLengthConversionData()); |
| operator_element->AddMathRSpaceIfNeeded(style, |
| state.CssToLengthConversionData()); |
| operator_element->AddMathMinSizeIfNeeded(style, |
| state.CssToLengthConversionData()); |
| operator_element->AddMathMaxSizeIfNeeded(style, |
| state.CssToLengthConversionData()); |
| } |
| } |
| |
| void StyleResolver::ApplyBaseStyle( |
| Element* element, |
| const StyleRecalcContext& style_recalc_context, |
| StyleResolverState& state, |
| StyleCascade& cascade, |
| MatchResult& match_result, |
| RuleMatchingBehavior matching_behavior, |
| bool can_cache_animation_base_computed_style) { |
| bool base_is_usable = can_cache_animation_base_computed_style && |
| CanReuseBaseComputedStyle(state); |
| const ComputedStyle* animation_base_computed_style = |
| base_is_usable ? CachedAnimationBaseComputedStyle(state) : nullptr; |
| if (ShouldComputeBaseComputedStyle(animation_base_computed_style)) { |
| InitStyleAndApplyInheritance(*element, state); |
| |
| GetDocument().GetStyleEngine().EnsureUAStyleForElement(*element); |
| |
| // This adds a CSSInitialColorValue to the cascade for the document |
| // element. The CSSInitialColorValue will resolve to a color-scheme |
| // sensitive color in Color::ApplyValue. It is added at the start of the |
| // MatchResult such that subsequent declarations (even from the UA sheet) |
| // get a higher priority. |
| // |
| // TODO(crbug.com/1046753): Remove this when canvastext is supported. |
| if (element == state.GetDocument().documentElement()) { |
| cascade.MutableMatchResult().AddMatchedProperties( |
| DocumentElementUserAgentDeclarations()); |
| } |
| |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, match_result, |
| state.Style(), state.Style()->InsideLink()); |
| |
| MatchAllRules(state, collector, |
| matching_behavior != kMatchAllRulesExcludingSMIL); |
| |
| if (tracker_) |
| AddMatchedRulesToTracker(collector); |
| |
| if (element->GetComputedStyle() && |
| element->GetComputedStyle()->TextAutosizingMultiplier() != |
| state.Style()->TextAutosizingMultiplier()) { |
| // Preserve the text autosizing multiplier on style recalc. Autosizer will |
| // update it during layout if needed. |
| // NOTE: this must occur before ApplyMatchedProperties for correct |
| // computation of font-relative lengths. |
| state.Style()->SetTextAutosizingMultiplier( |
| element->GetComputedStyle()->TextAutosizingMultiplier()); |
| } |
| |
| CascadeAndApplyMatchedProperties(state, cascade); |
| |
| if (collector.MatchedResult().DependsOnContainerQueries()) |
| state.Style()->SetDependsOnContainerQueries(true); |
| |
| ApplyCallbackSelectors(state); |
| |
| // Cache our original display. |
| state.Style()->SetOriginalDisplay(state.Style()->Display()); |
| |
| StyleAdjuster::AdjustComputedStyle(state, element); |
| |
| DCHECK(ValidateBaseComputedStyle(animation_base_computed_style, |
| *state.Style())); |
| } |
| |
| if (base_is_usable) { |
| DCHECK(animation_base_computed_style); |
| state.SetStyle(ComputedStyle::Clone(*animation_base_computed_style)); |
| if (!state.ParentStyle()) { |
| state.SetParentStyle(InitialStyleForElement(GetDocument())); |
| state.SetLayoutParentStyle(state.ParentStyle()); |
| } |
| MaybeResetCascade(cascade); |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| base_styles_used, 1); |
| } |
| } |
| |
| CompositorKeyframeValue* StyleResolver::CreateCompositorKeyframeValueSnapshot( |
| Element& element, |
| const ComputedStyle& base_style, |
| const ComputedStyle* parent_style, |
| const PropertyHandle& property, |
| const CSSValue* value, |
| double offset) { |
| // TODO(alancutter): Avoid creating a StyleResolverState just to apply a |
| // single value on a ComputedStyle. |
| StyleResolverState state(element.GetDocument(), element, parent_style, |
| parent_style); |
| state.SetStyle(ComputedStyle::Clone(base_style)); |
| if (value) { |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| auto* set = |
| MakeGarbageCollected<MutableCSSPropertyValueSet>(state.GetParserMode()); |
| set->SetProperty(property.GetCSSProperty().PropertyID(), *value); |
| cascade.MutableMatchResult().FinishAddingUARules(); |
| cascade.MutableMatchResult().FinishAddingUserRules(); |
| cascade.MutableMatchResult().AddMatchedProperties(set); |
| cascade.MutableMatchResult().FinishAddingAuthorRulesForTreeScope( |
| element.GetTreeScope()); |
| cascade.Apply(); |
| } |
| return CompositorKeyframeValueFactory::Create(property, *state.Style(), |
| offset); |
| } |
| |
| scoped_refptr<ComputedStyle> StyleResolver::PseudoStyleForElement( |
| Element* element, |
| const StyleRecalcContext& style_recalc_context, |
| const PseudoElementStyleRequest& pseudo_style_request, |
| const ComputedStyle* parent_style, |
| const ComputedStyle* parent_layout_object_style) { |
| DCHECK(parent_style); |
| if (!element) |
| return nullptr; |
| |
| StyleResolverState state( |
| GetDocument(), *element, pseudo_style_request.pseudo_id, |
| pseudo_style_request.type, parent_style, parent_layout_object_style); |
| |
| DCHECK(GetDocument().GetFrame()); |
| DCHECK(GetDocument().GetSettings()); |
| DCHECK(pseudo_style_request.pseudo_id != kPseudoIdFirstLineInherited); |
| |
| SelectorFilterParentScope::EnsureParentStackIsPushed(); |
| |
| bool base_is_usable = CanReuseBaseComputedStyle(state); |
| const ComputedStyle* animation_base_computed_style = |
| base_is_usable ? CachedAnimationBaseComputedStyle(state) : nullptr; |
| |
| // Since we don't use pseudo-elements in any of our quirk/print |
| // user agent rules, don't waste time walking those rules. |
| |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| |
| if (ShouldComputeBaseComputedStyle(animation_base_computed_style)) { |
| if (pseudo_style_request.AllowsInheritance(parent_style)) { |
| scoped_refptr<ComputedStyle> style = ComputedStyle::Create(); |
| style->InheritFrom(*parent_style); |
| state.SetStyle(std::move(style)); |
| } else { |
| // ::backdrop inherits from initial styles. All other pseudo elements |
| // inherit from their originating element (::before/::after), or |
| // originating element descendants (::first-line/::first-letter). |
| DCHECK(pseudo_style_request.pseudo_id == kPseudoIdBackdrop); |
| state.SetStyle(InitialStyleForElement(GetDocument())); |
| state.SetParentStyle(ComputedStyle::Clone(*state.Style())); |
| } |
| state.Style()->SetStyleType(pseudo_style_request.pseudo_id); |
| |
| // Check UA, user and author rules. |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, |
| cascade.MutableMatchResult(), state.Style(), |
| state.Style()->InsideLink()); |
| collector.SetPseudoElementStyleRequest(pseudo_style_request); |
| |
| GetDocument().GetStyleEngine().EnsureUAStyleForPseudoElement( |
| pseudo_style_request.pseudo_id); |
| |
| // TODO(obrufau): support styling nested pseudo-elements |
| if (!element->IsPseudoElement()) |
| MatchAllRules(state, collector, /* include_smil_properties */ false); |
| else |
| MatchUARules(*element, collector); |
| |
| if (tracker_) |
| AddMatchedRulesToTracker(collector); |
| |
| if (!collector.MatchedResult().HasMatchedProperties()) { |
| StyleAdjuster::AdjustComputedStyle(state, nullptr); |
| if (pseudo_style_request.type == PseudoElementStyleRequest::kForRenderer) |
| return nullptr; |
| return state.TakeStyle(); |
| } |
| |
| CascadeAndApplyMatchedProperties(state, cascade); |
| |
| if (collector.MatchedResult().DependsOnContainerQueries()) |
| state.Style()->SetDependsOnContainerQueries(true); |
| |
| ApplyCallbackSelectors(state); |
| |
| // Cache our original display. |
| state.Style()->SetOriginalDisplay(state.Style()->Display()); |
| |
| // FIXME: Passing 0 as the Element* introduces a lot of complexity |
| // in the StyleAdjuster::AdjustComputedStyle code. |
| StyleAdjuster::AdjustComputedStyle(state, nullptr); |
| |
| DCHECK(ValidateBaseComputedStyle(animation_base_computed_style, |
| *state.Style())); |
| } |
| |
| if (animation_base_computed_style) { |
| state.SetStyle(ComputedStyle::Clone(*animation_base_computed_style)); |
| state.Style()->SetStyleType(pseudo_style_request.pseudo_id); |
| MaybeResetCascade(cascade); |
| } |
| |
| state.SetCanCacheBaseStyle(true); |
| if (ApplyAnimatedStyle(state, cascade)) |
| StyleAdjuster::AdjustComputedStyle(state, nullptr); |
| |
| GetDocument().GetStyleEngine().IncStyleForElementCount(); |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| pseudo_elements_styled, 1); |
| |
| if (state.Style()->HasViewportUnits()) |
| GetDocument().SetHasViewportUnits(); |
| |
| if (state.Style()->HasGlyphRelativeUnits()) |
| UseCounter::Count(GetDocument(), WebFeature::kHasGlyphRelativeUnits); |
| |
| if (PseudoElement* pseudo_element = |
| element->GetPseudoElement(pseudo_style_request.pseudo_id)) { |
| SetAnimationUpdateIfNeeded(state, *pseudo_element); |
| state.LoadPendingResources(); |
| } |
| |
| return state.TakeStyle(); |
| } |
| |
| scoped_refptr<const ComputedStyle> StyleResolver::StyleForPage( |
| uint32_t page_index, |
| const AtomicString& page_name) { |
| scoped_refptr<const ComputedStyle> initial_style = |
| InitialStyleForElement(GetDocument()); |
| if (!GetDocument().documentElement()) |
| return initial_style; |
| |
| StyleResolverState state(GetDocument(), *GetDocument().documentElement(), |
| initial_style.get(), initial_style.get()); |
| |
| scoped_refptr<ComputedStyle> style = ComputedStyle::Create(); |
| const ComputedStyle* root_element_style = |
| state.RootElementStyle() ? state.RootElementStyle() |
| : GetDocument().GetComputedStyle(); |
| DCHECK(root_element_style); |
| style->InheritFrom(*root_element_style); |
| state.SetStyle(std::move(style)); |
| |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| |
| PageRuleCollector collector(root_element_style, page_index, page_name, |
| cascade.MutableMatchResult()); |
| |
| collector.MatchPageRules( |
| CSSDefaultStyleSheets::Instance().DefaultPrintStyle()); |
| |
| if (ScopedStyleResolver* scoped_resolver = |
| GetDocument().GetScopedStyleResolver()) |
| scoped_resolver->MatchPageRules(collector); |
| |
| cascade.Apply(); |
| |
| // Now return the style. |
| return state.TakeStyle(); |
| } |
| |
| scoped_refptr<ComputedStyle> StyleResolver::InitialStyleForElement( |
| Document& document) { |
| const LocalFrame* frame = document.GetFrame(); |
| |
| scoped_refptr<ComputedStyle> initial_style = ComputedStyle::Create(); |
| |
| initial_style->SetRtlOrdering(document.VisuallyOrdered() ? EOrder::kVisual |
| : EOrder::kLogical); |
| initial_style->SetZoom(frame && !document.Printing() ? frame->PageZoomFactor() |
| : 1); |
| initial_style->SetEffectiveZoom(initial_style->Zoom()); |
| initial_style->SetInForcedColorsMode(document.InForcedColorsMode()); |
| |
| FontDescription document_font_description = |
| initial_style->GetFontDescription(); |
| document_font_description.SetLocale( |
| LayoutLocale::Get(document.ContentLanguage())); |
| |
| initial_style->SetFontDescription(document_font_description); |
| initial_style->SetUserModify(document.InDesignMode() |
| ? EUserModify::kReadWrite |
| : EUserModify::kReadOnly); |
| document.SetupFontBuilder(*initial_style); |
| |
| scoped_refptr<StyleInitialData> initial_data = |
| document.GetStyleEngine().MaybeCreateAndGetInitialData(); |
| if (initial_data) |
| initial_style->SetInitialData(std::move(initial_data)); |
| |
| return initial_style; |
| } |
| |
| scoped_refptr<const ComputedStyle> StyleResolver::StyleForText( |
| Text* text_node) { |
| DCHECK(text_node); |
| if (Node* parent_node = LayoutTreeBuilderTraversal::Parent(*text_node)) { |
| const ComputedStyle* style = parent_node->GetComputedStyle(); |
| if (style && !style->IsEnsuredInDisplayNone()) |
| return style; |
| } |
| return nullptr; |
| } |
| |
| void StyleResolver::UpdateFont(StyleResolverState& state) { |
| state.GetFontBuilder().CreateFont(state.StyleRef(), state.ParentStyle()); |
| state.SetConversionFontSizes(CSSToLengthConversionData::FontSizes( |
| state.Style(), state.RootElementStyle())); |
| state.SetConversionZoom(state.Style()->EffectiveZoom()); |
| } |
| |
| void StyleResolver::AddMatchedRulesToTracker( |
| const ElementRuleCollector& collector) { |
| collector.AddMatchedRulesToTracker(tracker_); |
| } |
| |
| StyleRuleList* StyleResolver::StyleRulesForElement(Element* element, |
| unsigned rules_to_include) { |
| DCHECK(element); |
| StyleResolverState state(GetDocument(), *element); |
| MatchResult match_result; |
| // TODO(crbug.com/1145970): Use actual StyleRecalcContext. |
| StyleRecalcContext style_recalc_context; |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, match_result, state.Style(), |
| EInsideLink::kNotInsideLink); |
| collector.SetMode(SelectorChecker::kCollectingStyleRules); |
| CollectPseudoRulesForElement(*element, collector, kPseudoIdNone, |
| rules_to_include); |
| return collector.MatchedStyleRuleList(); |
| } |
| |
| HeapHashMap<CSSPropertyName, Member<const CSSValue>> |
| StyleResolver::CascadedValuesForElement(Element* element, PseudoId pseudo_id) { |
| StyleResolverState state(GetDocument(), *element); |
| state.SetStyle(ComputedStyle::Create()); |
| |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| // TODO(crbug.com/1145970): Use actual StyleRecalcContext. |
| StyleRecalcContext style_recalc_context; |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, cascade.MutableMatchResult(), |
| state.Style(), EInsideLink::kNotInsideLink); |
| collector.SetPseudoElementStyleRequest(PseudoElementStyleRequest(pseudo_id)); |
| MatchAllRules(state, collector, false /* include_smil_properties */); |
| |
| cascade.Apply(); |
| return cascade.GetCascadedValues(); |
| } |
| |
| RuleIndexList* StyleResolver::PseudoCSSRulesForElement( |
| Element* element, |
| PseudoId pseudo_id, |
| unsigned rules_to_include) { |
| DCHECK(element); |
| StyleResolverState state(GetDocument(), *element); |
| MatchResult match_result; |
| // TODO(crbug.com/1145970): Use actual StyleRecalcContext. |
| StyleRecalcContext style_recalc_context; |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, match_result, state.Style(), |
| EInsideLink::kNotInsideLink); |
| collector.SetMode(SelectorChecker::kCollectingCSSRules); |
| // TODO(obrufau): support collecting rules for nested ::marker |
| if (!element->IsPseudoElement()) { |
| CollectPseudoRulesForElement(*element, collector, pseudo_id, |
| rules_to_include); |
| } |
| |
| if (tracker_) |
| AddMatchedRulesToTracker(collector); |
| return collector.MatchedCSSRuleList(); |
| } |
| |
| RuleIndexList* StyleResolver::CssRulesForElement(Element* element, |
| unsigned rules_to_include) { |
| return PseudoCSSRulesForElement(element, kPseudoIdNone, rules_to_include); |
| } |
| |
| void StyleResolver::CollectPseudoRulesForElement( |
| const Element& element, |
| ElementRuleCollector& collector, |
| PseudoId pseudo_id, |
| unsigned rules_to_include) { |
| collector.SetPseudoElementStyleRequest(PseudoElementStyleRequest(pseudo_id)); |
| |
| if (rules_to_include & kUACSSRules) |
| MatchUARules(element, collector); |
| else |
| collector.FinishAddingUARules(); |
| |
| if (rules_to_include & kUserCSSRules) |
| MatchUserRules(collector); |
| else |
| collector.FinishAddingUserRules(); |
| |
| if (rules_to_include & kAuthorCSSRules) { |
| collector.SetSameOriginOnly(!(rules_to_include & kCrossOriginCSSRules)); |
| collector.SetIncludeEmptyRules(rules_to_include & kEmptyCSSRules); |
| MatchAuthorRules(element, ScopedResolverFor(element), collector); |
| } |
| } |
| |
| bool StyleResolver::ApplyAnimatedStyle(StyleResolverState& state, |
| StyleCascade& cascade) { |
| Element& element = state.GetElement(); |
| |
| // The animating element may be this element, the pseudo element we are |
| // resolving style for, or null if we are resolving style for a pseudo |
| // element which is not represented by a PseudoElement like scrollbar pseudo |
| // elements. |
| Element* animating_element = state.GetAnimatingElement(); |
| DCHECK(animating_element == &element || !animating_element || |
| animating_element->ParentOrShadowHostElement() == element); |
| |
| if (!HasAnimationsOrTransitions(state)) { |
| // Ensure that the base computed style is not stale even if not currently |
| // running an animation or transition. This ensures that any new transitions |
| // use the correct starting point based on the "before change" style. |
| UpdateAnimationBaseComputedStyle(state, cascade, false); |
| return false; |
| } |
| |
| CalculateAnimationUpdate(state); |
| |
| CSSAnimations::CalculateCompositorAnimationUpdate( |
| state.AnimationUpdate(), animating_element, element, *state.Style(), |
| state.ParentStyle(), WasViewportResized()); |
| CSSAnimations::CalculateTransitionUpdate( |
| state.AnimationUpdate(), CSSAnimations::PropertyPass::kStandard, |
| animating_element, *state.Style()); |
| CSSAnimations::CalculateTransitionUpdate(state.AnimationUpdate(), |
| CSSAnimations::PropertyPass::kCustom, |
| animating_element, *state.Style()); |
| |
| CSSAnimations::SnapshotCompositorKeyframes( |
| element, state.AnimationUpdate(), *state.Style(), state.ParentStyle()); |
| |
| bool has_update = !state.AnimationUpdate().IsEmpty(); |
| UpdateAnimationBaseComputedStyle(state, cascade, has_update); |
| |
| if (!has_update) |
| return false; |
| |
| const ActiveInterpolationsMap& standard_animations = |
| state.AnimationUpdate().ActiveInterpolationsForStandardAnimations(); |
| const ActiveInterpolationsMap& standard_transitions = |
| state.AnimationUpdate().ActiveInterpolationsForStandardTransitions(); |
| const ActiveInterpolationsMap& custom_animations = |
| state.AnimationUpdate().ActiveInterpolationsForCustomAnimations(); |
| const ActiveInterpolationsMap& custom_transitions = |
| state.AnimationUpdate().ActiveInterpolationsForCustomTransitions(); |
| |
| cascade.AddInterpolations(&standard_animations, CascadeOrigin::kAnimation); |
| cascade.AddInterpolations(&standard_transitions, CascadeOrigin::kTransition); |
| cascade.AddInterpolations(&custom_animations, CascadeOrigin::kAnimation); |
| cascade.AddInterpolations(&custom_transitions, CascadeOrigin::kTransition); |
| |
| CascadeFilter filter; |
| if (state.Style()->StyleType() == kPseudoIdMarker) |
| filter = filter.Add(CSSProperty::kValidForMarker, false); |
| if (IsHighlightPseudoElement(state.Style()->StyleType())) |
| filter = filter.Add(CSSProperty::kValidForHighlight, false); |
| filter = filter.Add(CSSProperty::kAnimation, true); |
| |
| cascade.Apply(filter); |
| |
| // Start loading resources used by animations. |
| state.LoadPendingResources(); |
| |
| DCHECK(!state.GetFontBuilder().FontDirty()); |
| |
| return true; |
| } |
| |
| StyleRuleKeyframes* StyleResolver::FindKeyframesRule( |
| const Element* element, |
| const AtomicString& animation_name) { |
| HeapVector<Member<ScopedStyleResolver>, 8> resolvers; |
| CollectScopedResolversForHostedShadowTrees(*element, resolvers); |
| if (ScopedStyleResolver* scoped_resolver = |
| element->GetTreeScope().GetScopedStyleResolver()) |
| resolvers.push_back(scoped_resolver); |
| |
| for (auto& resolver : resolvers) { |
| if (StyleRuleKeyframes* keyframes_rule = |
| resolver->KeyframeStylesForAnimation(animation_name)) |
| return keyframes_rule; |
| } |
| |
| if (StyleRuleKeyframes* keyframes_rule = |
| GetDocument().GetStyleEngine().KeyframeStylesForAnimation( |
| animation_name)) |
| return keyframes_rule; |
| |
| for (auto& resolver : resolvers) |
| resolver->SetHasUnresolvedKeyframesRule(); |
| return nullptr; |
| } |
| |
| void StyleResolver::InvalidateMatchedPropertiesCache() { |
| matched_properties_cache_.Clear(); |
| } |
| |
| void StyleResolver::SetResizedForViewportUnits() { |
| DCHECK(!was_viewport_resized_); |
| was_viewport_resized_ = true; |
| GetDocument().GetStyleEngine().UpdateActiveStyle(); |
| matched_properties_cache_.ClearViewportDependent(); |
| } |
| |
| void StyleResolver::ClearResizedForViewportUnits() { |
| was_viewport_resized_ = false; |
| } |
| |
| bool StyleResolver::CacheSuccess::EffectiveZoomChanged( |
| const ComputedStyle& style) const { |
| if (!cached_matched_properties) |
| return false; |
| return cached_matched_properties->computed_style->EffectiveZoom() != |
| style.EffectiveZoom(); |
| } |
| |
| bool StyleResolver::CacheSuccess::FontChanged( |
| const ComputedStyle& style) const { |
| if (!cached_matched_properties) |
| return false; |
| return cached_matched_properties->computed_style->GetFontDescription() != |
| style.GetFontDescription(); |
| } |
| |
| bool StyleResolver::CacheSuccess::InheritedVariablesChanged( |
| const ComputedStyle& style) const { |
| if (!cached_matched_properties) |
| return false; |
| return cached_matched_properties->computed_style->InheritedVariables() != |
| style.InheritedVariables(); |
| } |
| |
| bool StyleResolver::CacheSuccess::IsUsableAfterApplyInheritedOnly( |
| const ComputedStyle& style) const { |
| return !EffectiveZoomChanged(style) && !FontChanged(style) && |
| !InheritedVariablesChanged(style); |
| } |
| |
| StyleResolver::CacheSuccess StyleResolver::ApplyMatchedCache( |
| StyleResolverState& state, |
| const MatchResult& match_result) { |
| const Element& element = state.GetElement(); |
| |
| MatchedPropertiesCache::Key key(match_result); |
| |
| bool is_inherited_cache_hit = false; |
| bool is_non_inherited_cache_hit = false; |
| const CachedMatchedProperties* cached_matched_properties = |
| key.IsValid() ? matched_properties_cache_.Find(key, state) : nullptr; |
| |
| if (cached_matched_properties && MatchedPropertiesCache::IsCacheable(state)) { |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| matched_property_cache_hit, 1); |
| // We can build up the style by copying non-inherited properties from an |
| // earlier style object built using the same exact style declarations. We |
| // then only need to apply the inherited properties, if any, as their values |
| // can depend on the element context. This is fast and saves memory by |
| // reusing the style data structures. Note that we cannot do this if the |
| // direct parent is a ShadowRoot. |
| if (state.ParentStyle()->InheritedDataShared( |
| *cached_matched_properties->parent_computed_style) && |
| !IsAtShadowBoundary(&element)) { |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| matched_property_cache_inherited_hit, 1); |
| |
| EInsideLink link_status = state.Style()->InsideLink(); |
| // If the cache item parent style has identical inherited properties to |
| // the current parent style then the resulting style will be identical |
| // too. We copy the inherited properties over from the cache and are done. |
| state.Style()->InheritFrom(*cached_matched_properties->computed_style); |
| |
| // Unfortunately the link status is treated like an inherited property. We |
| // need to explicitly restore it. |
| state.Style()->SetInsideLink(link_status); |
| |
| is_inherited_cache_hit = true; |
| } |
| if (!IsForcedColorsModeEnabled() || is_inherited_cache_hit) { |
| state.Style()->CopyNonInheritedFromCached( |
| *cached_matched_properties->computed_style); |
| // If the child style is a cache hit, we'll never reach StyleBuilder:: |
| // ApplyProperty, hence we'll never set the flag on the parent. |
| if (state.Style()->HasExplicitInheritance()) |
| state.ParentStyle()->SetChildHasExplicitInheritance(); |
| is_non_inherited_cache_hit = true; |
| } |
| UpdateFont(state); |
| } |
| |
| return CacheSuccess(is_inherited_cache_hit, is_non_inherited_cache_hit, key, |
| cached_matched_properties); |
| } |
| |
| void StyleResolver::MaybeAddToMatchedPropertiesCache( |
| StyleResolverState& state, |
| const CacheSuccess& cache_success, |
| const MatchResult& match_result) { |
| state.LoadPendingResources(); |
| if (!cache_success.cached_matched_properties && cache_success.key.IsValid() && |
| MatchedPropertiesCache::IsCacheable(state)) { |
| INCREMENT_STYLE_STATS_COUNTER(GetDocument().GetStyleEngine(), |
| matched_property_cache_added, 1); |
| matched_properties_cache_.Add(cache_success.key, *state.Style(), |
| *state.ParentStyle()); |
| } |
| } |
| |
| void StyleResolver::CalculateAnimationUpdate(StyleResolverState& state) { |
| Element* animating_element = state.GetAnimatingElement(); |
| |
| DCHECK(state.Style()->Animations() || state.Style()->Transitions() || |
| (animating_element && animating_element->HasAnimations())); |
| DCHECK(!state.IsAnimationInterpolationMapReady()); |
| |
| CSSAnimations::CalculateAnimationUpdate( |
| state.AnimationUpdate(), animating_element, state.GetElement(), |
| *state.Style(), state.ParentStyle(), this); |
| |
| state.SetIsAnimationInterpolationMapReady(); |
| } |
| |
| bool StyleResolver::CanReuseBaseComputedStyle(const StyleResolverState& state) { |
| // TODO(crbug.com/1180159): @container and transitions properly. |
| if (RuntimeEnabledFeatures::CSSContainerQueriesEnabled()) |
| return false; |
| |
| ElementAnimations* element_animations = GetElementAnimations(state); |
| if (!element_animations || !element_animations->BaseComputedStyle()) |
| return false; |
| |
| if (!element_animations->IsAnimationStyleChange()) |
| return false; |
| |
| // Animating a custom property can have side effects on other properties |
| // via variable references. Disallow base computed style optimization in such |
| // cases. |
| if (CSSAnimations::IsAnimatingCustomProperties(element_animations)) |
| return false; |
| |
| // We need to build the cascade to know what to revert to. |
| if (CSSAnimations::IsAnimatingRevert(element_animations)) |
| return false; |
| |
| // When applying an animation or transition for a font affecting property, |
| // font-relative units (e.g. em, ex) in the base style must respond to the |
| // animation. We cannot use the base computed style optimization in such |
| // cases. |
| if (CSSAnimations::IsAnimatingFontAffectingProperties(element_animations)) { |
| if (element_animations->BaseComputedStyle() && |
| element_animations->BaseComputedStyle()->HasFontRelativeUnits()) { |
| return false; |
| } |
| } |
| |
| // Normally, we apply all active animation effects on top of the style created |
| // by regular CSS declarations. However, !important declarations have a |
| // higher priority than animation effects [1]. If we're currently animating |
| // (not transitioning) a property which was declared !important in the base |
| // style, we disable the base computed style optimization. |
| // [1] https://drafts.csswg.org/css-cascade-4/#cascade-origin |
| if (CSSAnimations::IsAnimatingStandardProperties( |
| element_animations, element_animations->BaseImportantSet(), |
| KeyframeEffect::kDefaultPriority)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| const CSSValue* StyleResolver::ComputeValue( |
| Element* element, |
| const CSSPropertyName& property_name, |
| const CSSValue& value) { |
| const ComputedStyle* base_style = element->GetComputedStyle(); |
| StyleResolverState state(element->GetDocument(), *element); |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| state.SetStyle(ComputedStyle::Clone(*base_style)); |
| auto* set = |
| MakeGarbageCollected<MutableCSSPropertyValueSet>(state.GetParserMode()); |
| if (property_name.IsCustomProperty()) { |
| set->SetProperty(CSSPropertyValue(property_name, value)); |
| } else { |
| set->SetProperty(property_name.Id(), value); |
| } |
| cascade.MutableMatchResult().FinishAddingUARules(); |
| cascade.MutableMatchResult().FinishAddingUserRules(); |
| cascade.MutableMatchResult().AddMatchedProperties(set); |
| cascade.MutableMatchResult().FinishAddingAuthorRulesForTreeScope( |
| element->GetTreeScope()); |
| cascade.Apply(); |
| |
| CSSPropertyRef property_ref(property_name, element->GetDocument()); |
| return ComputedStyleUtils::ComputedPropertyValue(property_ref.GetProperty(), |
| *state.Style()); |
| } |
| |
| scoped_refptr<ComputedStyle> StyleResolver::StyleForInterpolations( |
| Element& element, |
| ActiveInterpolationsMap& interpolations) { |
| StyleResolverState state(GetDocument(), element); |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| |
| // TODO(crbug.com/1145970): Use actual StyleRecalcContext. |
| StyleRecalcContext style_recalc_context; |
| ApplyBaseStyle(&element, style_recalc_context, state, cascade, |
| cascade.MutableMatchResult(), kMatchAllRules, true); |
| ApplyInterpolations(state, cascade, interpolations); |
| |
| return state.TakeStyle(); |
| } |
| |
| void StyleResolver::ApplyInterpolations( |
| StyleResolverState& state, |
| StyleCascade& cascade, |
| ActiveInterpolationsMap& interpolations) { |
| cascade.AddInterpolations(&interpolations, CascadeOrigin::kAnimation); |
| cascade.Apply(); |
| } |
| |
| scoped_refptr<ComputedStyle> |
| StyleResolver::BeforeChangeStyleForTransitionUpdate( |
| Element& element, |
| const ComputedStyle& base_style, |
| ActiveInterpolationsMap& transition_interpolations) { |
| StyleResolverState state(GetDocument(), element); |
| STACK_UNINITIALIZED StyleCascade cascade(state); |
| state.SetStyle(ComputedStyle::Clone(base_style)); |
| |
| // Various property values may depend on the parent style. A valid parent |
| // style is required, even if animating the root element, in order to |
| // handle these dependencies. The root element inherits from initial |
| // styles. |
| if (!state.ParentStyle()) { |
| if (element != GetDocument().documentElement()) { |
| // Do not apply interpolations to a detached element. |
| return state.TakeStyle(); |
| } |
| state.SetParentStyle(InitialStyleForElement(GetDocument())); |
| state.SetLayoutParentStyle(state.ParentStyle()); |
| } |
| |
| // TODO(crbug.com/1098937): Include active CSS animations in a separate |
| // interpolations map and add each map at the appropriate CascadeOrigin. |
| ApplyInterpolations(state, cascade, transition_interpolations); |
| return state.TakeStyle(); |
| } |
| |
| void StyleResolver::CascadeAndApplyMatchedProperties(StyleResolverState& state, |
| StyleCascade& cascade) { |
| const MatchResult& result = cascade.GetMatchResult(); |
| |
| CacheSuccess cache_success = ApplyMatchedCache(state, result); |
| |
| if (cache_success.IsFullCacheHit()) |
| return; |
| |
| if (cache_success.ShouldApplyInheritedOnly()) { |
| cascade.Apply(CascadeFilter(CSSProperty::kInherited, false)); |
| if (!cache_success.IsUsableAfterApplyInheritedOnly(state.StyleRef())) |
| cascade.Apply(CascadeFilter(CSSProperty::kInherited, true)); |
| } else { |
| cascade.Apply(); |
| } |
| |
| MaybeAddToMatchedPropertiesCache(state, cache_success, result); |
| |
| DCHECK(!state.GetFontBuilder().FontDirty()); |
| } |
| |
| void StyleResolver::ApplyCallbackSelectors(StyleResolverState& state) { |
| RuleSet* watched_selectors_rule_set = |
| GetDocument().GetStyleEngine().WatchedSelectorsRuleSet(); |
| if (!watched_selectors_rule_set) |
| return; |
| |
| // TODO(crbug.com/1145970): Use actual StyleRecalcContext. |
| StyleRecalcContext style_recalc_context; |
| MatchResult match_result; |
| ElementRuleCollector collector(state.ElementContext(), style_recalc_context, |
| selector_filter_, match_result, state.Style(), |
| state.Style()->InsideLink()); |
| collector.SetMode(SelectorChecker::kCollectingStyleRules); |
| collector.SetIncludeEmptyRules(true); |
| |
| MatchRequest match_request(watched_selectors_rule_set); |
| collector.CollectMatchingRules(match_request); |
| collector.SortAndTransferMatchedRules(); |
| |
| if (tracker_) |
| AddMatchedRulesToTracker(collector); |
| |
| StyleRuleList* rules = collector.MatchedStyleRuleList(); |
| if (!rules) |
| return; |
| for (auto rule : *rules) |
| state.Style()->AddCallbackSelector(rule->SelectorList().SelectorsText()); |
| } |
| |
| // Font properties are also handled by FontStyleResolver outside the main |
| // thread. If you add/remove properties here, make sure they are also properly |
| // handled by FontStyleResolver. |
| void StyleResolver::ComputeFont(Element& element, |
| ComputedStyle* style, |
| const CSSPropertyValueSet& property_set) { |
| static const CSSProperty* properties[7] = { |
| &GetCSSPropertyFontSize(), &GetCSSPropertyFontFamily(), |
| &GetCSSPropertyFontStretch(), &GetCSSPropertyFontStyle(), |
| &GetCSSPropertyFontVariantCaps(), &GetCSSPropertyFontWeight(), |
| &GetCSSPropertyLineHeight(), |
| }; |
| |
| // TODO(timloh): This is weird, the style is being used as its own parent |
| StyleResolverState state(GetDocument(), element, style, style); |
| state.SetStyle(style); |
| |
| for (const CSSProperty* property : properties) { |
| if (property->IDEquals(CSSPropertyID::kLineHeight)) |
| UpdateFont(state); |
| // TODO(futhark): If we start supporting fonts on ShadowRoot.fonts in |
| // addition to Document.fonts, we need to pass the correct TreeScope instead |
| // of GetDocument() in the ScopedCSSValue below. |
| StyleBuilder::ApplyProperty( |
| *property, state, |
| ScopedCSSValue( |
| *property_set.GetPropertyCSSValue(property->PropertyID()), |
| &GetDocument())); |
| } |
| } |
| |
| void StyleResolver::UpdateMediaType() { |
| if (LocalFrameView* view = GetDocument().View()) { |
| bool was_print = print_media_type_; |
| print_media_type_ = |
| EqualIgnoringASCIICase(view->MediaType(), media_type_names::kPrint); |
| if (was_print != print_media_type_) |
| matched_properties_cache_.ClearViewportDependent(); |
| } |
| } |
| |
| void StyleResolver::Trace(Visitor* visitor) const { |
| visitor->Trace(matched_properties_cache_); |
| visitor->Trace(selector_filter_); |
| visitor->Trace(document_); |
| visitor->Trace(tracker_); |
| } |
| |
| bool StyleResolver::IsForcedColorsModeEnabled() const { |
| return GetDocument().InForcedColorsMode(); |
| } |
| |
| bool StyleResolver::IsForcedColorsModeEnabled( |
| const StyleResolverState& state) const { |
| return IsForcedColorsModeEnabled() && |
| state.Style()->ForcedColorAdjust() != EForcedColorAdjust::kNone; |
| } |
| |
| } // namespace blink |