blob: 1ecd871306625b8a25e20b3c9df6860d3269987a [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/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);
}
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())
document.GetStyleResolver()->InvalidateMatchedPropertiesCache();
for (const auto& rule : rule_set.FontFeatureValuesRules())
document.GetStyleEngine().AddDefaultFontDisplay(rule);
}
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;
author_style_sheets_.push_back(sheet);
AddKeyframeRules(rule_set);
AddFontFaceRules(rule_set);
AddTreeBoundaryCrossingRules(rule_set, sheet, index);
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 : author_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 (tree_boundary_crossing_rule_set_) {
for (const auto& rules : *tree_boundary_crossing_rule_set_)
features.Add(rules->rule_set_->Features());
}
if (slotted_rule_set_) {
for (const auto& rules : *slotted_rule_set_)
features.Add(rules->rule_set_->Features());
}
}
void ScopedStyleResolver::ResetAuthorStyle() {
author_style_sheets_.clear();
viewport_dependent_media_query_results_.clear();
device_dependent_media_query_results_.clear();
keyframes_rule_map_.clear();
tree_boundary_crossing_rule_set_ = nullptr;
slotted_rule_set_ = nullptr;
has_deep_or_shadow_selector_ = false;
needs_append_all_sheets_ = false;
}
StyleRuleKeyframes* ScopedStyleResolver::KeyframeStylesForAnimation(
const StringImpl* 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 s(rule->GetName());
if (rule->IsVendorPrefixed()) {
KeyframesRuleMap::iterator it = keyframes_rule_map_.find(s.Impl());
if (it == keyframes_rule_map_.end())
keyframes_rule_map_.Set(s.Impl(), rule);
else if (it->value->IsVendorPrefixed())
keyframes_rule_map_.Set(s.Impl(), rule);
} else {
keyframes_rule_map_.Set(s.Impl(), rule);
}
}
ContainerNode& ScopedStyleResolver::InvalidationRootForTreeScope(
const TreeScope& tree_scope) {
if (tree_scope.RootNode() == tree_scope.GetDocument())
return tree_scope.GetDocument();
return ToShadowRoot(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.
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::CollectMatchingAuthorRules(
ElementRuleCollector& collector,
ShadowV0CascadeOrder cascade_order) {
wtf_size_t sheet_index = 0;
for (auto sheet : author_style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingRules(match_request, cascade_order);
}
}
void ScopedStyleResolver::CollectMatchingShadowHostRules(
ElementRuleCollector& collector,
ShadowV0CascadeOrder cascade_order) {
wtf_size_t sheet_index = 0;
for (auto sheet : author_style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingShadowHostRules(match_request, cascade_order);
}
}
void ScopedStyleResolver::CollectMatchingSlottedRules(
ElementRuleCollector& collector,
ShadowV0CascadeOrder cascade_order) {
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, cascade_order, true);
}
}
void ScopedStyleResolver::CollectMatchingTreeBoundaryCrossingRules(
ElementRuleCollector& collector,
ShadowV0CascadeOrder cascade_order) {
if (!tree_boundary_crossing_rule_set_)
return;
for (const auto& rules : *tree_boundary_crossing_rule_set_) {
MatchRequest request(rules->rule_set_.Get(), &GetTreeScope().RootNode(),
rules->parent_style_sheet_, rules->parent_index_);
collector.CollectMatchingRules(request, cascade_order, true);
}
}
void ScopedStyleResolver::CollectMatchingPartPseudoRules(
ElementRuleCollector& collector,
PartNames& part_names,
ShadowV0CascadeOrder cascade_order) {
if (!RuntimeEnabledFeatures::CSSPartPseudoElementEnabled())
return;
wtf_size_t sheet_index = 0;
for (auto sheet : author_style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingPartPseudoRules(match_request, part_names,
cascade_order);
}
}
void ScopedStyleResolver::MatchPageRules(PageRuleCollector& collector) {
// Only consider the global author RuleSet for @page rules, as per the HTML5
// spec.
DCHECK(scope_->RootNode().IsDocumentNode());
for (auto sheet : author_style_sheets_)
collector.MatchPageRules(&sheet->Contents()->GetRuleSet());
}
void ScopedStyleResolver::Trace(blink::Visitor* visitor) {
visitor->Trace(scope_);
visitor->Trace(author_style_sheets_);
visitor->Trace(keyframes_rule_map_);
visitor->Trace(tree_boundary_crossing_rule_set_);
visitor->Trace(slotted_rule_set_);
}
static void AddRules(RuleSet* rule_set,
const HeapVector<MinimalRuleData>& rules) {
for (const auto& info : rules)
rule_set->AddRule(info.rule_, info.selector_index_, info.flags_);
}
void ScopedStyleResolver::AddTreeBoundaryCrossingRules(
const RuleSet& author_rules,
CSSStyleSheet* parent_style_sheet,
unsigned sheet_index) {
bool is_document_scope = GetTreeScope().RootNode().IsDocumentNode();
if (author_rules.DeepCombinatorOrShadowPseudoRules().IsEmpty() &&
(is_document_scope ||
(author_rules.ContentPseudoElementRules().IsEmpty())))
return;
if (!author_rules.DeepCombinatorOrShadowPseudoRules().IsEmpty())
has_deep_or_shadow_selector_ = true;
RuleSet* rule_set_for_scope = RuleSet::Create();
AddRules(rule_set_for_scope,
author_rules.DeepCombinatorOrShadowPseudoRules());
if (!is_document_scope)
AddRules(rule_set_for_scope, author_rules.ContentPseudoElementRules());
if (!tree_boundary_crossing_rule_set_) {
tree_boundary_crossing_rule_set_ =
MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
GetTreeScope().GetDocument().GetStyleEngine().AddTreeBoundaryCrossingScope(
GetTreeScope());
}
tree_boundary_crossing_rule_set_->push_back(
RuleSubSet::Create(parent_style_sheet, sheet_index, rule_set_for_scope));
}
void ScopedStyleResolver::V0ShadowAddedOnV1Document() {
// See the comment in AddSlottedRules().
if (!slotted_rule_set_)
return;
if (!tree_boundary_crossing_rule_set_) {
tree_boundary_crossing_rule_set_ =
MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
GetTreeScope().GetDocument().GetStyleEngine().AddTreeBoundaryCrossingScope(
GetTreeScope());
}
tree_boundary_crossing_rule_set_->AppendVector(*slotted_rule_set_);
slotted_rule_set_ = nullptr;
}
void ScopedStyleResolver::AddSlottedRules(const RuleSet& author_rules,
CSSStyleSheet* parent_style_sheet,
unsigned sheet_index) {
bool is_document_scope = GetTreeScope().RootNode().IsDocumentNode();
if (is_document_scope || author_rules.SlottedPseudoElementRules().IsEmpty())
return;
RuleSet* slotted_rule_set = RuleSet::Create();
AddRules(slotted_rule_set, author_rules.SlottedPseudoElementRules());
// In case ::slotted rule is used in V0/V1 mixed document, put ::slotted
// rules in tree boundary crossing rules as the pure v1 fast path in
// StyleResolver misses them.
// Adding this tree scope to tree boundary crossing scopes may end up in
// O(N^2) where N is number of scopes which has ::slotted() rules.
// Once the document-wide cascade order flag downgrades from V1 to V0,
// these slotted rules have to be moved back to tree boundary crossing
// rule sets. See V0ShadowAddedOnV1Document().
if (GetTreeScope().GetDocument().MayContainV0Shadow()) {
if (!tree_boundary_crossing_rule_set_) {
tree_boundary_crossing_rule_set_ =
MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
GetTreeScope()
.GetDocument()
.GetStyleEngine()
.AddTreeBoundaryCrossingScope(GetTreeScope());
}
tree_boundary_crossing_rule_set_->push_back(
RuleSubSet::Create(parent_style_sheet, sheet_index, slotted_rule_set));
return;
}
if (!slotted_rule_set_)
slotted_rule_set_ = MakeGarbageCollected<CSSStyleSheetRuleSubSet>();
slotted_rule_set_->push_back(
RuleSubSet::Create(parent_style_sheet, sheet_index, slotted_rule_set));
}
void ScopedStyleResolver::RuleSubSet::Trace(blink::Visitor* visitor) {
visitor->Trace(parent_style_sheet_);
visitor->Trace(rule_set_);
}
} // namespace blink