blob: c1bbef07aa02a4b2f8b7189969a3cf95a9c2a698 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
* All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/animation/document_timeline.h"
#include "third_party/blink/renderer/core/css/counter_style_map.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.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/resolver/match_request.h"
#include "third_party/blink/renderer/core/css/rule_feature_set.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/html/html_style_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/svg/svg_style_element.h"
namespace blink {
ScopedStyleResolver* ScopedStyleResolver::Parent() const {
for (TreeScope* scope = GetTreeScope().ParentTreeScope(); scope;
scope = scope->ParentTreeScope()) {
if (ScopedStyleResolver* resolver = scope->GetScopedStyleResolver())
return resolver;
}
return nullptr;
}
void ScopedStyleResolver::AddKeyframeRules(const RuleSet& rule_set) {
const HeapVector<Member<StyleRuleKeyframes>> keyframes_rules =
rule_set.KeyframesRules();
for (auto rule : keyframes_rules)
AddKeyframeStyle(rule);
}
CounterStyleMap& ScopedStyleResolver::EnsureCounterStyleMap() {
if (!counter_style_map_)
counter_style_map_ = CounterStyleMap::CreateAuthorCounterStyleMap(*scope_);
return *counter_style_map_;
}
void ScopedStyleResolver::AddFontFaceRules(const RuleSet& rule_set) {
// FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for
// the moment.
if (!GetTreeScope().RootNode().IsDocumentNode())
return;
Document& document = GetTreeScope().GetDocument();
CSSFontSelector* css_font_selector =
document.GetStyleEngine().GetFontSelector();
const HeapVector<Member<StyleRuleFontFace>> font_face_rules =
rule_set.FontFaceRules();
for (auto& font_face_rule : font_face_rules) {
if (FontFace* font_face = FontFace::Create(&document, font_face_rule))
css_font_selector->GetFontFaceCache()->Add(font_face_rule, font_face);
}
if (font_face_rules.size())
document.GetStyleResolver().InvalidateMatchedPropertiesCache();
}
void ScopedStyleResolver::AddCounterStyleRules(const RuleSet& rule_set) {
if (!RuntimeEnabledFeatures::CSSAtRuleCounterStyleInShadowDOMEnabled()) {
// Our support of @counter-style rules in shadow DOM is experimental and
// non-standard. See https://github.com/w3c/csswg-drafts/issues/5693
if (!GetTreeScope().RootNode().IsDocumentNode())
return;
}
if (rule_set.CounterStyleRules().IsEmpty())
return;
EnsureCounterStyleMap().AddCounterStyles(rule_set);
}
void ScopedStyleResolver::AppendActiveStyleSheets(
unsigned index,
const ActiveStyleSheetVector& active_sheets) {
for (auto* active_iterator = active_sheets.begin() + index;
active_iterator != active_sheets.end(); active_iterator++) {
CSSStyleSheet* sheet = active_iterator->first;
viewport_dependent_media_query_results_.AppendVector(
sheet->ViewportDependentMediaQueryResults());
device_dependent_media_query_results_.AppendVector(
sheet->DeviceDependentMediaQueryResults());
if (!active_iterator->second)
continue;
const RuleSet& rule_set = *active_iterator->second;
style_sheets_.push_back(sheet);
AddKeyframeRules(rule_set);
AddFontFaceRules(rule_set);
AddCounterStyleRules(rule_set);
AddSlottedRules(rule_set, sheet, index++);
}
}
void ScopedStyleResolver::CollectFeaturesTo(
RuleFeatureSet& features,
HeapHashSet<Member<const StyleSheetContents>>&
visited_shared_style_sheet_contents) const {
features.ViewportDependentMediaQueryResults().AppendVector(
viewport_dependent_media_query_results_);
features.DeviceDependentMediaQueryResults().AppendVector(
device_dependent_media_query_results_);
for (auto sheet : style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
StyleSheetContents* contents = sheet->Contents();
if (contents->HasOneClient() ||
visited_shared_style_sheet_contents.insert(contents).is_new_entry)
features.Add(contents->GetRuleSet().Features());
}
if (slotted_rule_set_) {
for (const auto& rules : *slotted_rule_set_)
features.Add(rules->rule_set_->Features());
}
}
void ScopedStyleResolver::ResetStyle() {
style_sheets_.clear();
viewport_dependent_media_query_results_.clear();
device_dependent_media_query_results_.clear();
keyframes_rule_map_.clear();
if (counter_style_map_)
counter_style_map_->Dispose();
slotted_rule_set_ = nullptr;
needs_append_all_sheets_ = false;
}
StyleRuleKeyframes* ScopedStyleResolver::KeyframeStylesForAnimation(
const AtomicString& animation_name) {
if (keyframes_rule_map_.IsEmpty())
return nullptr;
KeyframesRuleMap::iterator it = keyframes_rule_map_.find(animation_name);
if (it == keyframes_rule_map_.end())
return nullptr;
return it->value.Get();
}
void ScopedStyleResolver::AddKeyframeStyle(StyleRuleKeyframes* rule) {
AtomicString name = rule->GetName();
if (rule->IsVendorPrefixed()) {
KeyframesRuleMap::iterator it = keyframes_rule_map_.find(name);
if (it == keyframes_rule_map_.end())
keyframes_rule_map_.Set(name, rule);
else if (it->value->IsVendorPrefixed())
keyframes_rule_map_.Set(name, rule);
} else {
keyframes_rule_map_.Set(name, rule);
}
}
Element& ScopedStyleResolver::InvalidationRootForTreeScope(
const TreeScope& tree_scope) {
DCHECK(tree_scope.GetDocument().documentElement());
if (tree_scope.RootNode() == tree_scope.GetDocument())
return *tree_scope.GetDocument().documentElement();
return To<ShadowRoot>(tree_scope.RootNode()).host();
}
void ScopedStyleResolver::KeyframesRulesAdded(const TreeScope& tree_scope) {
// Called when @keyframes rules are about to be added/removed from a
// TreeScope. @keyframes rules may apply to animations on elements in the
// same TreeScope as the stylesheet, or the host element in the parent
// TreeScope if the TreeScope is a shadow tree.
if (!tree_scope.GetDocument().documentElement())
return;
ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver();
ScopedStyleResolver* parent_resolver =
tree_scope.ParentTreeScope()
? tree_scope.ParentTreeScope()->GetScopedStyleResolver()
: nullptr;
bool had_unresolved_keyframes = false;
if (resolver && resolver->has_unresolved_keyframes_rule_) {
resolver->has_unresolved_keyframes_rule_ = false;
had_unresolved_keyframes = true;
}
if (parent_resolver && parent_resolver->has_unresolved_keyframes_rule_) {
parent_resolver->has_unresolved_keyframes_rule_ = false;
had_unresolved_keyframes = true;
}
if (had_unresolved_keyframes) {
// If an animation ended up not being started because no @keyframes
// rules were found for the animation-name, we need to recalculate style
// for the elements in the scope, including its shadow host if
// applicable.
InvalidationRootForTreeScope(tree_scope)
.SetNeedsStyleRecalc(kSubtreeStyleChange,
StyleChangeReasonForTracing::Create(
style_change_reason::kStyleSheetChange));
return;
}
// If we have animations running, added/removed @keyframes may affect these.
tree_scope.GetDocument().Timeline().InvalidateKeyframeEffects(tree_scope);
}
void ScopedStyleResolver::CollectMatchingElementScopeRules(
ElementRuleCollector& collector) {
wtf_size_t sheet_index = 0;
for (auto sheet : style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingRules(match_request);
}
}
void ScopedStyleResolver::CollectMatchingShadowHostRules(
ElementRuleCollector& collector) {
wtf_size_t sheet_index = 0;
for (auto sheet : style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingShadowHostRules(match_request);
}
}
void ScopedStyleResolver::CollectMatchingSlottedRules(
ElementRuleCollector& collector) {
if (!slotted_rule_set_)
return;
for (const auto& rules : *slotted_rule_set_) {
MatchRequest request(rules->rule_set_.Get(), &GetTreeScope().RootNode(),
rules->parent_style_sheet_, rules->parent_index_);
collector.CollectMatchingRules(request, true);
}
}
void ScopedStyleResolver::CollectMatchingPartPseudoRules(
ElementRuleCollector& collector,
PartNames& part_names,
bool for_shadow_pseudo) {
wtf_size_t sheet_index = 0;
for (auto sheet : style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingPartPseudoRules(match_request, part_names,
for_shadow_pseudo);
}
}
void ScopedStyleResolver::MatchPageRules(PageRuleCollector& collector) {
// Currently, only @page rules in the document scope apply.
DCHECK(scope_->RootNode().IsDocumentNode());
for (auto sheet : style_sheets_)
collector.MatchPageRules(&sheet->Contents()->GetRuleSet());
}
void ScopedStyleResolver::Trace(Visitor* visitor) const {
visitor->Trace(scope_);
visitor->Trace(style_sheets_);
visitor->Trace(keyframes_rule_map_);
visitor->Trace(counter_style_map_);
visitor->Trace(slotted_rule_set_);
}
static void AddRules(RuleSet* rule_set,
const HeapVector<MinimalRuleData>& rules) {
for (const auto& info : rules) {
// TODO(crbug.com/1145970): Store container_query on MinimalRuleData
// and propagate it here.
rule_set->AddRule(info.rule_, info.selector_index_, info.flags_,
nullptr /* container_query */);
}
}
void ScopedStyleResolver::AddSlottedRules(const RuleSet& rules,
CSSStyleSheet* parent_style_sheet,
unsigned sheet_index) {
bool is_document_scope = GetTreeScope().RootNode().IsDocumentNode();
if (is_document_scope || rules.SlottedPseudoElementRules().IsEmpty())
return;
auto* slotted_rule_set = MakeGarbageCollected<RuleSet>();
AddRules(slotted_rule_set, rules.SlottedPseudoElementRules());
if (!slotted_rule_set_)
slotted_rule_set_ = MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
slotted_rule_set_->push_back(MakeGarbageCollected<RuleSubSet>(
parent_style_sheet, sheet_index, slotted_rule_set));
}
void ScopedStyleResolver::RuleSubSet::Trace(Visitor* visitor) const {
visitor->Trace(parent_style_sheet_);
visitor->Trace(rule_set_);
}
} // namespace blink