blob: 43e94e751a47f273cdc1eea7df3f34b17d3db88e [file] [log] [blame]
/*
* (C) 1999-2003 Lars Knoll (knoll@kde.org)
* Copyright (C) 2004, 2006, 2007, 2012 Apple 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 "core/css/StyleSheetContents.h"
#include "core/css/CSSPropertyValueSet.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/CSSTiming.h"
#include "core/css/StyleEngine.h"
#include "core/css/StyleRule.h"
#include "core/css/StyleRuleImport.h"
#include "core/css/StyleRuleNamespace.h"
#include "core/css/parser/CSSParser.h"
#include "core/dom/Document.h"
#include "core/dom/Node.h"
#include "core/frame/UseCounter.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/loader/resource/CSSStyleSheetResource.h"
#include "platform/Histogram.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/weborigin/SecurityOrigin.h"
namespace blink {
// static
const Document* StyleSheetContents::SingleOwnerDocument(
const StyleSheetContents* style_sheet_contents) {
// TODO(https://crbug.com/242125): We may want to handle stylesheets that have
// multiple owners when this is used for UseCounter.
if (style_sheet_contents && style_sheet_contents->HasSingleOwnerNode())
return style_sheet_contents->SingleOwnerDocument();
return nullptr;
}
// Rough size estimate for the memory cache.
unsigned StyleSheetContents::EstimatedSizeInBytes() const {
// Note that this does not take into account size of the strings hanging from
// various objects. The assumption is that nearly all of of them are atomic
// and would exist anyway.
unsigned size = sizeof(*this);
// FIXME: This ignores the children of media rules.
// Most rules are StyleRules.
size += RuleCount() * StyleRule::AverageSizeInBytes();
for (unsigned i = 0; i < import_rules_.size(); ++i) {
if (StyleSheetContents* sheet = import_rules_[i]->GetStyleSheet())
size += sheet->EstimatedSizeInBytes();
}
return size;
}
StyleSheetContents::StyleSheetContents(StyleRuleImport* owner_rule,
const String& original_url,
const CSSParserContext* context)
: owner_rule_(owner_rule),
original_url_(original_url),
default_namespace_(g_star_atom),
has_syntactically_valid_css_header_(true),
did_load_error_occur_(false),
is_mutable_(false),
has_font_face_rule_(false),
has_viewport_rule_(false),
has_media_queries_(false),
has_single_owner_document_(true),
is_used_from_text_cache_(false),
parser_context_(context) {}
StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
: owner_rule_(nullptr),
original_url_(o.original_url_),
import_rules_(o.import_rules_.size()),
namespace_rules_(o.namespace_rules_.size()),
child_rules_(o.child_rules_.size()),
namespaces_(o.namespaces_),
default_namespace_(o.default_namespace_),
has_syntactically_valid_css_header_(
o.has_syntactically_valid_css_header_),
did_load_error_occur_(false),
is_mutable_(false),
has_font_face_rule_(o.has_font_face_rule_),
has_viewport_rule_(o.has_viewport_rule_),
has_media_queries_(o.has_media_queries_),
has_single_owner_document_(true),
is_used_from_text_cache_(false),
parser_context_(o.parser_context_) {
// FIXME: Copy import rules.
DCHECK(o.import_rules_.IsEmpty());
for (unsigned i = 0; i < namespace_rules_.size(); ++i) {
namespace_rules_[i] =
static_cast<StyleRuleNamespace*>(o.namespace_rules_[i]->Copy());
}
// LazyParseCSS: Copying child rules is a strict point for lazy parsing, so
// there is no need to copy lazy parsing state here.
for (unsigned i = 0; i < child_rules_.size(); ++i)
child_rules_[i] = o.child_rules_[i]->Copy();
}
StyleSheetContents::~StyleSheetContents() = default;
void StyleSheetContents::SetHasSyntacticallyValidCSSHeader(bool is_valid_css) {
has_syntactically_valid_css_header_ = is_valid_css;
}
bool StyleSheetContents::IsCacheableForResource() const {
// This would require dealing with multiple clients for load callbacks.
if (!LoadCompleted())
return false;
if (has_media_queries_ &&
!RuntimeEnabledFeatures::CacheStyleSheetWithMediaQueriesEnabled())
return false;
// FIXME: Support copying import rules.
if (!import_rules_.IsEmpty())
return false;
// FIXME: Support cached stylesheets in import rules.
if (owner_rule_)
return false;
if (did_load_error_occur_)
return false;
// It is not the original sheet anymore.
if (is_mutable_)
return false;
// If the header is valid we are not going to need to check the
// SecurityOrigin.
// FIXME: Valid mime type avoids the check too.
if (!has_syntactically_valid_css_header_)
return false;
return true;
}
bool StyleSheetContents::IsCacheableForStyleElement() const {
// FIXME: Support copying import rules.
if (!ImportRules().IsEmpty())
return false;
// Until import rules are supported in cached sheets it's not possible for
// loading to fail.
DCHECK(!DidLoadErrorOccur());
// It is not the original sheet anymore.
if (IsMutable())
return false;
if (!HasSyntacticallyValidCSSHeader())
return false;
return true;
}
void StyleSheetContents::ParserAppendRule(StyleRuleBase* rule) {
if (rule->IsImportRule()) {
// Parser enforces that @import rules come before anything else
DCHECK(child_rules_.IsEmpty());
StyleRuleImport* import_rule = ToStyleRuleImport(rule);
if (import_rule->MediaQueries())
SetHasMediaQueries();
import_rules_.push_back(import_rule);
import_rules_.back()->SetParentStyleSheet(this);
import_rules_.back()->RequestStyleSheet();
return;
}
if (rule->IsNamespaceRule()) {
// Parser enforces that @namespace rules come before all rules other than
// import/charset rules
DCHECK(child_rules_.IsEmpty());
StyleRuleNamespace& namespace_rule = ToStyleRuleNamespace(*rule);
ParserAddNamespace(namespace_rule.Prefix(), namespace_rule.Uri());
namespace_rules_.push_back(&namespace_rule);
return;
}
child_rules_.push_back(rule);
}
void StyleSheetContents::SetHasMediaQueries() {
has_media_queries_ = true;
if (ParentStyleSheet())
ParentStyleSheet()->SetHasMediaQueries();
}
StyleRuleBase* StyleSheetContents::RuleAt(unsigned index) const {
SECURITY_DCHECK(index < RuleCount());
if (index < import_rules_.size())
return import_rules_[index].Get();
index -= import_rules_.size();
if (index < namespace_rules_.size())
return namespace_rules_[index].Get();
index -= namespace_rules_.size();
return child_rules_[index].Get();
}
unsigned StyleSheetContents::RuleCount() const {
return import_rules_.size() + namespace_rules_.size() + child_rules_.size();
}
void StyleSheetContents::ClearRules() {
for (unsigned i = 0; i < import_rules_.size(); ++i) {
DCHECK_EQ(import_rules_.at(i)->ParentStyleSheet(), this);
import_rules_[i]->ClearParentStyleSheet();
}
import_rules_.clear();
namespace_rules_.clear();
child_rules_.clear();
}
bool StyleSheetContents::WrapperInsertRule(StyleRuleBase* rule,
unsigned index) {
DCHECK(is_mutable_);
SECURITY_DCHECK(index <= RuleCount());
if (index < import_rules_.size() ||
(index == import_rules_.size() && rule->IsImportRule())) {
// Inserting non-import rule before @import is not allowed.
if (!rule->IsImportRule())
return false;
StyleRuleImport* import_rule = ToStyleRuleImport(rule);
if (import_rule->MediaQueries())
SetHasMediaQueries();
import_rules_.insert(index, import_rule);
import_rules_[index]->SetParentStyleSheet(this);
import_rules_[index]->RequestStyleSheet();
// FIXME: Stylesheet doesn't actually change meaningfully before the
// imported sheets are loaded.
return true;
}
// Inserting @import rule after a non-import rule is not allowed.
if (rule->IsImportRule())
return false;
index -= import_rules_.size();
if (index < namespace_rules_.size() ||
(index == namespace_rules_.size() && rule->IsNamespaceRule())) {
// Inserting non-namespace rules other than import rule before @namespace is
// not allowed.
if (!rule->IsNamespaceRule())
return false;
// Inserting @namespace rule when rules other than import/namespace/charset
// are present is not allowed.
if (!child_rules_.IsEmpty())
return false;
StyleRuleNamespace* namespace_rule = ToStyleRuleNamespace(rule);
namespace_rules_.insert(index, namespace_rule);
// For now to be compatible with IE and Firefox if namespace rule with same
// prefix is added irrespective of adding the rule at any index, last added
// rule's value is considered.
// TODO (ramya.v@samsung.com): As per spec last valid rule should be
// considered, which means if namespace rule is added in the middle of
// existing namespace rules, rule which comes later in rule list with same
// prefix needs to be considered.
ParserAddNamespace(namespace_rule->Prefix(), namespace_rule->Uri());
return true;
}
if (rule->IsNamespaceRule())
return false;
index -= namespace_rules_.size();
child_rules_.insert(index, rule);
return true;
}
bool StyleSheetContents::WrapperDeleteRule(unsigned index) {
DCHECK(is_mutable_);
SECURITY_DCHECK(index < RuleCount());
if (index < import_rules_.size()) {
import_rules_[index]->ClearParentStyleSheet();
if (import_rules_[index]->IsFontFaceRule())
NotifyRemoveFontFaceRule(ToStyleRuleFontFace(import_rules_[index].Get()));
import_rules_.EraseAt(index);
return true;
}
index -= import_rules_.size();
if (index < namespace_rules_.size()) {
if (!child_rules_.IsEmpty())
return false;
namespace_rules_.EraseAt(index);
return true;
}
index -= namespace_rules_.size();
if (child_rules_[index]->IsFontFaceRule())
NotifyRemoveFontFaceRule(ToStyleRuleFontFace(child_rules_[index].Get()));
child_rules_.EraseAt(index);
return true;
}
void StyleSheetContents::ParserAddNamespace(const AtomicString& prefix,
const AtomicString& uri) {
DCHECK(!uri.IsNull());
if (prefix.IsNull()) {
default_namespace_ = uri;
return;
}
PrefixNamespaceURIMap::AddResult result = namespaces_.insert(prefix, uri);
if (result.is_new_entry)
return;
result.stored_value->value = uri;
}
const AtomicString& StyleSheetContents::NamespaceURIFromPrefix(
const AtomicString& prefix) const {
return namespaces_.at(prefix);
}
void StyleSheetContents::ParseAuthorStyleSheet(
const CSSStyleSheetResource* cached_style_sheet,
const SecurityOrigin* security_origin) {
TRACE_EVENT1("blink,devtools.timeline", "ParseAuthorStyleSheet", "data",
InspectorParseAuthorStyleSheetEvent::Data(cached_style_sheet));
double start_time = MonotonicallyIncreasingTime();
bool is_same_origin_request =
security_origin && security_origin->CanRequest(BaseURL());
// When the response was fetched via the Service Worker, the original URL may
// not be same as the base URL.
// TODO(horo): When we will use the original URL as the base URL, we can
// remove this check. crbug.com/553535
if (is_same_origin_request &&
cached_style_sheet->GetResponse().WasFetchedViaServiceWorker()) {
const KURL original_url(
cached_style_sheet->GetResponse().OriginalURLViaServiceWorker());
// |originalURL| is empty when the response is created in the SW.
if (!original_url.IsEmpty() && !security_origin->CanRequest(original_url))
is_same_origin_request = false;
}
CSSStyleSheetResource::MIMETypeCheck mime_type_check =
IsQuirksModeBehavior(parser_context_->Mode()) && is_same_origin_request
? CSSStyleSheetResource::MIMETypeCheck::kLax
: CSSStyleSheetResource::MIMETypeCheck::kStrict;
String sheet_text =
cached_style_sheet->SheetText(parser_context_, mime_type_check);
const ResourceResponse& response = cached_style_sheet->GetResponse();
source_map_url_ = response.HttpHeaderField(HTTPNames::SourceMap);
if (source_map_url_.IsEmpty()) {
// Try to get deprecated header.
source_map_url_ = response.HttpHeaderField(HTTPNames::X_SourceMap);
}
const CSSParserContext* context =
CSSParserContext::CreateWithStyleSheetContents(ParserContext(), this);
CSSParser::ParseSheet(context, this, sheet_text,
RuntimeEnabledFeatures::LazyParseCSSEnabled());
DEFINE_STATIC_LOCAL(CustomCountHistogram, parse_histogram,
("Style.AuthorStyleSheet.ParseTime", 0, 10000000, 50));
double parse_duration_seconds = (MonotonicallyIncreasingTime() - start_time);
parse_histogram.Count(parse_duration_seconds * 1000 * 1000);
if (Document* document = SingleOwnerDocument()) {
CSSTiming::From(*document).RecordAuthorStyleSheetParseTime(
parse_duration_seconds);
}
}
void StyleSheetContents::ParseString(const String& sheet_text) {
ParseStringAtPosition(sheet_text, TextPosition::MinimumPosition());
}
void StyleSheetContents::ParseStringAtPosition(
const String& sheet_text,
const TextPosition& start_position) {
const CSSParserContext* context =
CSSParserContext::CreateWithStyleSheetContents(ParserContext(), this);
CSSParser::ParseSheet(context, this, sheet_text);
}
bool StyleSheetContents::IsLoading() const {
for (unsigned i = 0; i < import_rules_.size(); ++i) {
if (import_rules_[i]->IsLoading())
return true;
}
return false;
}
bool StyleSheetContents::LoadCompleted() const {
StyleSheetContents* parent_sheet = ParentStyleSheet();
if (parent_sheet)
return parent_sheet->LoadCompleted();
StyleSheetContents* root = RootStyleSheet();
return root->loading_clients_.IsEmpty();
}
void StyleSheetContents::CheckLoaded() {
if (IsLoading())
return;
StyleSheetContents* parent_sheet = ParentStyleSheet();
if (parent_sheet) {
parent_sheet->CheckLoaded();
return;
}
DCHECK_EQ(this, RootStyleSheet());
if (loading_clients_.IsEmpty())
return;
// Avoid |CSSSStyleSheet| and |OwnerNode| being deleted by scripts that run
// via ScriptableDocumentParser::ExecuteScriptsWaitingForResources(). Also
// protect the |CSSStyleSheet| from being deleted during iteration via the
// |SheetLoaded| method.
//
// When a sheet is loaded it is moved from the set of loading clients
// to the set of completed clients. We therefore need the copy in order to
// not modify the set while iterating it.
HeapVector<Member<CSSStyleSheet>> loading_clients;
CopyToVector(loading_clients_, loading_clients);
for (unsigned i = 0; i < loading_clients.size(); ++i) {
if (loading_clients[i]->LoadCompleted())
continue;
// sheetLoaded might be invoked after its owner node is removed from
// document.
if (Node* owner_node = loading_clients[i]->ownerNode()) {
if (loading_clients[i]->SheetLoaded())
owner_node->NotifyLoadedSheetAndAllCriticalSubresources(
did_load_error_occur_ ? Node::kErrorOccurredLoadingSubresource
: Node::kNoErrorLoadingSubresource);
}
}
}
void StyleSheetContents::NotifyLoadedSheet(const CSSStyleSheetResource* sheet) {
DCHECK(sheet);
did_load_error_occur_ |= sheet->ErrorOccurred();
// updateLayoutIgnorePendingStyleSheets can cause us to create the RuleSet on
// this sheet before its imports have loaded. So clear the RuleSet when the
// imports load since the import's subrules are flattened into its parent
// sheet's RuleSet.
ClearRuleSet();
}
void StyleSheetContents::StartLoadingDynamicSheet() {
StyleSheetContents* root = RootStyleSheet();
for (const auto& client : root->loading_clients_)
client->StartLoadingDynamicSheet();
// Copy the completed clients to a vector for iteration.
// startLoadingDynamicSheet will move the style sheet from the completed state
// to the loading state which modifies the set of completed clients. We
// therefore need the copy in order to not modify the set of completed clients
// while iterating it.
HeapVector<Member<CSSStyleSheet>> completed_clients;
CopyToVector(root->completed_clients_, completed_clients);
for (unsigned i = 0; i < completed_clients.size(); ++i)
completed_clients[i]->StartLoadingDynamicSheet();
}
StyleSheetContents* StyleSheetContents::RootStyleSheet() const {
const StyleSheetContents* root = this;
while (root->ParentStyleSheet())
root = root->ParentStyleSheet();
return const_cast<StyleSheetContents*>(root);
}
bool StyleSheetContents::HasSingleOwnerNode() const {
return RootStyleSheet()->HasOneClient();
}
Node* StyleSheetContents::SingleOwnerNode() const {
StyleSheetContents* root = RootStyleSheet();
if (!root->HasOneClient())
return nullptr;
if (root->loading_clients_.size())
return (*root->loading_clients_.begin())->ownerNode();
return (*root->completed_clients_.begin())->ownerNode();
}
Document* StyleSheetContents::SingleOwnerDocument() const {
StyleSheetContents* root = RootStyleSheet();
return root->ClientSingleOwnerDocument();
}
Document* StyleSheetContents::AnyOwnerDocument() const {
return RootStyleSheet()->ClientAnyOwnerDocument();
}
static bool ChildRulesHaveFailedOrCanceledSubresources(
const HeapVector<Member<StyleRuleBase>>& rules) {
for (unsigned i = 0; i < rules.size(); ++i) {
const StyleRuleBase* rule = rules[i].Get();
switch (rule->GetType()) {
case StyleRuleBase::kStyle:
if (ToStyleRule(rule)->PropertiesHaveFailedOrCanceledSubresources())
return true;
break;
case StyleRuleBase::kFontFace:
if (ToStyleRuleFontFace(rule)
->Properties()
.HasFailedOrCanceledSubresources())
return true;
break;
case StyleRuleBase::kMedia:
if (ChildRulesHaveFailedOrCanceledSubresources(
ToStyleRuleMedia(rule)->ChildRules()))
return true;
break;
case StyleRuleBase::kCharset:
case StyleRuleBase::kImport:
case StyleRuleBase::kNamespace:
NOTREACHED();
case StyleRuleBase::kPage:
case StyleRuleBase::kKeyframes:
case StyleRuleBase::kKeyframe:
case StyleRuleBase::kSupports:
case StyleRuleBase::kViewport:
break;
}
}
return false;
}
bool StyleSheetContents::HasFailedOrCanceledSubresources() const {
DCHECK(IsCacheableForResource());
return ChildRulesHaveFailedOrCanceledSubresources(child_rules_);
}
Document* StyleSheetContents::ClientAnyOwnerDocument() const {
if (ClientSize() <= 0)
return nullptr;
if (loading_clients_.size())
return (*loading_clients_.begin())->OwnerDocument();
return (*completed_clients_.begin())->OwnerDocument();
}
Document* StyleSheetContents::ClientSingleOwnerDocument() const {
return has_single_owner_document_ ? ClientAnyOwnerDocument() : nullptr;
}
StyleSheetContents* StyleSheetContents::ParentStyleSheet() const {
return owner_rule_ ? owner_rule_->ParentStyleSheet() : nullptr;
}
void StyleSheetContents::RegisterClient(CSSStyleSheet* sheet) {
DCHECK(!loading_clients_.Contains(sheet));
DCHECK(!completed_clients_.Contains(sheet));
// InspectorCSSAgent::BuildObjectForRule creates CSSStyleSheet without any
// owner node.
if (!sheet->OwnerDocument())
return;
if (Document* document = ClientSingleOwnerDocument()) {
if (sheet->OwnerDocument() != document)
has_single_owner_document_ = false;
}
loading_clients_.insert(sheet);
}
void StyleSheetContents::UnregisterClient(CSSStyleSheet* sheet) {
loading_clients_.erase(sheet);
completed_clients_.erase(sheet);
if (!sheet->OwnerDocument() || !loading_clients_.IsEmpty() ||
!completed_clients_.IsEmpty())
return;
has_single_owner_document_ = true;
}
void StyleSheetContents::ClientLoadCompleted(CSSStyleSheet* sheet) {
DCHECK(loading_clients_.Contains(sheet) || !sheet->OwnerDocument());
loading_clients_.erase(sheet);
// In owner_node_->SheetLoaded, the CSSStyleSheet might be detached.
// (i.e. ClearOwnerNode was invoked.)
// In this case, we don't need to add the stylesheet to completed clients.
if (!sheet->OwnerDocument())
return;
completed_clients_.insert(sheet);
}
void StyleSheetContents::ClientLoadStarted(CSSStyleSheet* sheet) {
DCHECK(completed_clients_.Contains(sheet));
completed_clients_.erase(sheet);
loading_clients_.insert(sheet);
}
void StyleSheetContents::SetReferencedFromResource(
CSSStyleSheetResource* resource) {
DCHECK(resource);
DCHECK(!IsReferencedFromResource());
DCHECK(IsCacheableForResource());
referenced_from_resource_ = resource;
}
void StyleSheetContents::ClearReferencedFromResource() {
DCHECK(IsReferencedFromResource());
DCHECK(IsCacheableForResource());
referenced_from_resource_ = nullptr;
}
RuleSet& StyleSheetContents::EnsureRuleSet(const MediaQueryEvaluator& medium,
AddRuleFlags add_rule_flags) {
if (!rule_set_) {
rule_set_ = RuleSet::Create();
rule_set_->AddRulesFromSheet(this, medium, add_rule_flags);
}
return *rule_set_.Get();
}
static void SetNeedsActiveStyleUpdateForClients(
HeapHashSet<WeakMember<CSSStyleSheet>>& clients) {
for (const auto& sheet : clients) {
Document* document = sheet->OwnerDocument();
Node* node = sheet->ownerNode();
if (!document || !node || !node->isConnected())
continue;
document->GetStyleEngine().SetNeedsActiveStyleUpdate(node->GetTreeScope());
}
}
void StyleSheetContents::ClearRuleSet() {
if (StyleSheetContents* parent_sheet = ParentStyleSheet())
parent_sheet->ClearRuleSet();
if (!rule_set_)
return;
rule_set_.Clear();
SetNeedsActiveStyleUpdateForClients(loading_clients_);
SetNeedsActiveStyleUpdateForClients(completed_clients_);
}
static void RemoveFontFaceRules(HeapHashSet<WeakMember<CSSStyleSheet>>& clients,
const StyleRuleFontFace* font_face_rule) {
for (const auto& sheet : clients) {
if (Node* owner_node = sheet->ownerNode())
owner_node->GetDocument().GetStyleEngine().RemoveFontFaceRules(
HeapVector<Member<const StyleRuleFontFace>>(1, font_face_rule));
}
}
void StyleSheetContents::NotifyRemoveFontFaceRule(
const StyleRuleFontFace* font_face_rule) {
StyleSheetContents* root = RootStyleSheet();
RemoveFontFaceRules(root->loading_clients_, font_face_rule);
RemoveFontFaceRules(root->completed_clients_, font_face_rule);
}
static void FindFontFaceRulesFromRules(
const HeapVector<Member<StyleRuleBase>>& rules,
HeapVector<Member<const StyleRuleFontFace>>& font_face_rules) {
for (unsigned i = 0; i < rules.size(); ++i) {
StyleRuleBase* rule = rules[i].Get();
if (rule->IsFontFaceRule()) {
font_face_rules.push_back(ToStyleRuleFontFace(rule));
} else if (rule->IsMediaRule()) {
StyleRuleMedia* media_rule = ToStyleRuleMedia(rule);
// We cannot know whether the media rule matches or not, but
// for safety, remove @font-face in the media rule (if exists).
FindFontFaceRulesFromRules(media_rule->ChildRules(), font_face_rules);
}
}
}
void StyleSheetContents::FindFontFaceRules(
HeapVector<Member<const StyleRuleFontFace>>& font_face_rules) {
for (unsigned i = 0; i < import_rules_.size(); ++i) {
if (!import_rules_[i]->GetStyleSheet())
continue;
import_rules_[i]->GetStyleSheet()->FindFontFaceRules(font_face_rules);
}
FindFontFaceRulesFromRules(ChildRules(), font_face_rules);
}
void StyleSheetContents::Trace(blink::Visitor* visitor) {
visitor->Trace(owner_rule_);
visitor->Trace(import_rules_);
visitor->Trace(namespace_rules_);
visitor->Trace(child_rules_);
visitor->Trace(loading_clients_);
visitor->Trace(completed_clients_);
visitor->Trace(rule_set_);
visitor->Trace(referenced_from_resource_);
visitor->Trace(parser_context_);
}
} // namespace blink