| /* |
| * (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 "third_party/blink/renderer/core/css/style_sheet_contents.h" |
| |
| #include "third_party/blink/renderer/core/css/css_property_value_set.h" |
| #include "third_party/blink/renderer/core/css/css_style_sheet.h" |
| #include "third_party/blink/renderer/core/css/parser/css_parser.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_rule_counter_style.h" |
| #include "third_party/blink/renderer/core/css/style_rule_import.h" |
| #include "third_party/blink/renderer/core/css/style_rule_namespace.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/node.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" |
| #include "third_party/blink/renderer/core/loader/resource/css_style_sheet_resource.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.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(const CSSParserContext* context, |
| const String& original_url, |
| StyleRuleImport* owner_rule) |
| : 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()); |
| } |
| |
| // Copying child rules is a strict point for deferred property 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; |
| // 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 (auto* import_rule = DynamicTo<StyleRuleImport>(rule)) { |
| // Parser enforces that @import rules come before anything else |
| DCHECK(child_rules_.IsEmpty()); |
| if (import_rule->MediaQueries()) |
| SetHasMediaQueries(); |
| import_rules_.push_back(import_rule); |
| import_rules_.back()->SetParentStyleSheet(this); |
| import_rules_.back()->RequestStyleSheet(); |
| return; |
| } |
| |
| if (auto* namespace_rule = DynamicTo<StyleRuleNamespace>(rule)) { |
| // Parser enforces that @namespace rules come before all rules other than |
| // import/charset rules |
| DCHECK(child_rules_.IsEmpty()); |
| 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. |
| auto* import_rule = DynamicTo<StyleRuleImport>(rule); |
| if (!import_rule) |
| return false; |
| |
| 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. |
| auto* namespace_rule = DynamicTo<StyleRuleNamespace>(rule); |
| if (!namespace_rule) |
| return false; |
| // Inserting @namespace rule when rules other than import/namespace/charset |
| // are present is not allowed. |
| if (!child_rules_.IsEmpty()) |
| return false; |
| |
| 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(); |
| 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(To<StyleRuleFontFace>(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; |
| } |
| namespaces_.Set(prefix, uri); |
| } |
| |
| const AtomicString& StyleSheetContents::NamespaceURIFromPrefix( |
| const AtomicString& prefix) const { |
| return namespaces_.at(prefix); |
| } |
| |
| void StyleSheetContents::ParseAuthorStyleSheet( |
| const CSSStyleSheetResource* cached_style_sheet) { |
| TRACE_EVENT1( |
| "blink,devtools.timeline", "ParseAuthorStyleSheet", "data", |
| inspector_parse_author_style_sheet_event::Data(cached_style_sheet)); |
| |
| const ResourceResponse& response = cached_style_sheet->GetResponse(); |
| CSSStyleSheetResource::MIMETypeCheck mime_type_check = |
| (IsQuirksModeBehavior(parser_context_->Mode()) && |
| response.IsCorsSameOrigin()) |
| ? CSSStyleSheetResource::MIMETypeCheck::kLax |
| : CSSStyleSheetResource::MIMETypeCheck::kStrict; |
| String sheet_text = |
| cached_style_sheet->SheetText(parser_context_, mime_type_check); |
| |
| source_map_url_ = response.HttpHeaderField(http_names::kSourceMap); |
| if (source_map_url_.IsEmpty()) { |
| // Try to get deprecated header. |
| source_map_url_ = response.HttpHeaderField(http_names::kXSourceMap); |
| } |
| |
| const auto* context = |
| MakeGarbageCollected<CSSParserContext>(ParserContext(), this); |
| CSSParser::ParseSheet(context, this, sheet_text, |
| CSSDeferPropertyParsing::kYes); |
| } |
| |
| ParseSheetResult StyleSheetContents::ParseString(const String& sheet_text, |
| bool allow_import_rules) { |
| return ParseStringAtPosition(sheet_text, TextPosition::MinimumPosition(), |
| allow_import_rules); |
| } |
| |
| ParseSheetResult StyleSheetContents::ParseStringAtPosition( |
| const String& sheet_text, |
| const TextPosition& start_position, |
| bool allow_import_rules) { |
| const auto* context = |
| MakeGarbageCollected<CSSParserContext>(ParserContext(), this); |
| return CSSParser::ParseSheet(context, this, sheet_text, |
| CSSDeferPropertyParsing::kNo, |
| allow_import_rules); |
| } |
| |
| 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; |
| if (loading_clients[i]->IsConstructed()) |
| 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 (To<StyleRule>(rule)->PropertiesHaveFailedOrCanceledSubresources()) |
| return true; |
| break; |
| case StyleRuleBase::kFontFace: |
| if (To<StyleRuleFontFace>(rule) |
| ->Properties() |
| .HasFailedOrCanceledSubresources()) |
| return true; |
| break; |
| case StyleRuleBase::kContainer: |
| case StyleRuleBase::kMedia: |
| if (ChildRulesHaveFailedOrCanceledSubresources( |
| To<StyleRuleGroup>(rule)->ChildRules())) |
| return true; |
| break; |
| case StyleRuleBase::kCharset: |
| case StyleRuleBase::kImport: |
| case StyleRuleBase::kNamespace: |
| NOTREACHED(); |
| break; |
| case StyleRuleBase::kPage: |
| case StyleRuleBase::kProperty: |
| case StyleRuleBase::kKeyframes: |
| case StyleRuleBase::kKeyframe: |
| case StyleRuleBase::kScrollTimeline: |
| case StyleRuleBase::kSupports: |
| case StyleRuleBase::kViewport: |
| break; |
| case StyleRuleBase::kCounterStyle: |
| if (To<StyleRuleCounterStyle>(rule)->HasFailedOrCanceledSubresources()) |
| return true; |
| 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_->DidMediaQueryResultsChange(medium)) |
| rule_set_ = nullptr; |
| if (!rule_set_) { |
| rule_set_ = MakeGarbageCollected<RuleSet>(); |
| 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 (auto* font_face_rule = DynamicTo<StyleRuleFontFace>(rule)) { |
| font_face_rules.push_back(font_face_rule); |
| } else if (auto* media_rule = DynamicTo<StyleRuleMedia>(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(Visitor* visitor) const { |
| 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 |